smith-agents 0.4.4 → 0.5.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +262 -0
- data/README.md +116 -6
- data/docs/CONFIGURATION.md +4 -1
- data/docs/PATTERNS.md +7 -0
- data/docs/PERSISTENCE.md +502 -1
- data/docs/workflow_claim.md +4 -1
- data/lib/smith/agent/lifecycle.rb +19 -10
- data/lib/smith/agent/registry/execution_binding_capture.rb +26 -0
- data/lib/smith/agent/registry/mutation_boundary.rb +29 -0
- data/lib/smith/agent/registry.rb +8 -0
- data/lib/smith/agent.rb +24 -2
- data/lib/smith/budget/amount_contract.rb +67 -0
- data/lib/smith/budget/amount_representation.rb +43 -0
- data/lib/smith/budget/decimal_context.rb +18 -0
- data/lib/smith/budget/ledger.rb +110 -18
- data/lib/smith/budget/ledger_publication.rb +60 -0
- data/lib/smith/budget/ledger_state.rb +21 -0
- data/lib/smith/budget/ledger_state_transition.rb +72 -0
- data/lib/smith/budget/public_state_validator.rb +25 -0
- data/lib/smith/budget/reservation.rb +20 -0
- data/lib/smith/budget/reservation_contract.rb +34 -0
- data/lib/smith/context/observation_masking.rb +3 -1
- data/lib/smith/context/state_injection.rb +1 -1
- data/lib/smith/doctor/checks/persistence_capabilities.rb +18 -3
- data/lib/smith/error.rb +5 -0
- data/lib/smith/errors.rb +6 -0
- data/lib/smith/exponential_backoff.rb +128 -0
- data/lib/smith/persistence_adapters/active_record_connection_errors.rb +23 -0
- data/lib/smith/persistence_adapters/active_record_exact_predicate.rb +43 -0
- data/lib/smith/persistence_adapters/active_record_exact_store.rb +62 -0
- data/lib/smith/persistence_adapters/active_record_exact_write.rb +40 -0
- data/lib/smith/persistence_adapters/active_record_initial_write.rb +42 -0
- data/lib/smith/persistence_adapters/active_record_store.rb +95 -52
- data/lib/smith/persistence_adapters/cache_store.rb +4 -1
- data/lib/smith/persistence_adapters/memory.rb +45 -14
- data/lib/smith/persistence_adapters/payload_version.rb +23 -0
- data/lib/smith/persistence_adapters/redis_client_access.rb +38 -0
- data/lib/smith/persistence_adapters/redis_exact_write.rb +44 -0
- data/lib/smith/persistence_adapters/redis_store.rb +38 -43
- data/lib/smith/persistence_adapters/redis_versioned_write.rb +56 -0
- data/lib/smith/persistence_adapters/retry.rb +22 -10
- data/lib/smith/persistence_adapters/version_expectation.rb +19 -0
- data/lib/smith/persistence_adapters.rb +26 -18
- data/lib/smith/persistence_payload_conflict.rb +12 -0
- data/lib/smith/tool/budget_enforcement.rb +2 -2
- data/lib/smith/types.rb +1 -0
- data/lib/smith/version.rb +2 -1
- data/lib/smith/workflow/branch_env.rb +1 -1
- data/lib/smith/workflow/budget_integration.rb +21 -11
- data/lib/smith/workflow/definition_identity/class_methods.rb +48 -0
- data/lib/smith/workflow/definition_identity.rb +25 -0
- data/lib/smith/workflow/deterministic_step.rb +2 -2
- data/lib/smith/workflow/dsl.rb +73 -5
- data/lib/smith/workflow/durability.rb +2 -1
- data/lib/smith/workflow/evaluator_optimizer.rb +7 -11
- data/lib/smith/workflow/execution.rb +16 -18
- data/lib/smith/workflow/execution_binding_resolution.rb +16 -0
- data/lib/smith/workflow/execution_result_snapshot.rb +118 -0
- data/lib/smith/workflow/fanout_execution.rb +1 -1
- data/lib/smith/workflow/graph/diagnostic.rb +27 -1
- data/lib/smith/workflow/graph/diagnostic_path.rb +24 -0
- data/lib/smith/workflow/graph/execution_successors.rb +65 -0
- data/lib/smith/workflow/graph/identifier_projection.rb +13 -0
- data/lib/smith/workflow/graph/metrics.rb +1 -3
- data/lib/smith/workflow/graph/nested_readiness_diagnostics.rb +15 -16
- data/lib/smith/workflow/graph/optimization_contract.rb +2 -2
- data/lib/smith/workflow/graph/orchestration_contract.rb +3 -3
- data/lib/smith/workflow/graph/reachability.rb +19 -31
- data/lib/smith/workflow/graph/reachability_diagnostics.rb +17 -1
- data/lib/smith/workflow/graph/retry_policy_diagnostic.rb +36 -0
- data/lib/smith/workflow/graph/runtime_binding_diagnostics.rb +2 -2
- data/lib/smith/workflow/graph/runtime_readiness.rb +3 -44
- data/lib/smith/workflow/graph/runtime_readiness_metrics.rb +2 -2
- data/lib/smith/workflow/graph/runtime_readiness_report_builder.rb +47 -0
- data/lib/smith/workflow/graph/runtime_readiness_traversal.rb +108 -0
- data/lib/smith/workflow/graph/state_diagnostics.rb +2 -2
- data/lib/smith/workflow/graph/transition_contract.rb +47 -0
- data/lib/smith/workflow/graph/transition_contract_attributes.rb +73 -0
- data/lib/smith/workflow/graph/transition_contract_configurations.rb +100 -0
- data/lib/smith/workflow/graph/transition_diagnostics.rb +54 -12
- data/lib/smith/workflow/graph/transition_optimization_configuration.rb +62 -0
- data/lib/smith/workflow/graph/transition_snapshot.rb +4 -1
- data/lib/smith/workflow/graph/validator.rb +1 -1
- data/lib/smith/workflow/graph.rb +62 -4
- data/lib/smith/workflow/guardrail_integration.rb +23 -9
- data/lib/smith/workflow/identifier.rb +24 -0
- data/lib/smith/workflow/message_admission.rb +40 -0
- data/lib/smith/workflow/message_admission_boundary.rb +22 -0
- data/lib/smith/workflow/message_batch.rb +60 -0
- data/lib/smith/workflow/message_value_normalizer.rb +133 -0
- data/lib/smith/workflow/nested_execution.rb +10 -0
- data/lib/smith/workflow/orchestrator_worker.rb +3 -7
- data/lib/smith/workflow/parallel/cancellation_signal.rb +14 -5
- data/lib/smith/workflow/parallel/execution_context.rb +113 -0
- data/lib/smith/workflow/parallel/nested_execution.rb +124 -0
- data/lib/smith/workflow/parallel/root_execution.rb +91 -0
- data/lib/smith/workflow/parallel.rb +19 -22
- data/lib/smith/workflow/parallel_agent_binding.rb +24 -0
- data/lib/smith/workflow/parallel_execution.rb +7 -3
- data/lib/smith/workflow/persistence.rb +49 -10
- data/lib/smith/workflow/prepared_step.rb +122 -0
- data/lib/smith/workflow/prepared_step_dispatch.rb +88 -0
- data/lib/smith/workflow/prepared_step_execution_authorization.rb +116 -0
- data/lib/smith/workflow/prepared_step_execution_result.rb +49 -0
- data/lib/smith/workflow/prepared_step_execution_scope.rb +45 -0
- data/lib/smith/workflow/prepared_step_recovery.rb +38 -0
- data/lib/smith/workflow/retry_execution.rb +20 -11
- data/lib/smith/workflow/split_step_persistence/boundary.rb +128 -0
- data/lib/smith/workflow/split_step_persistence/boundary_reset.rb +50 -0
- data/lib/smith/workflow/split_step_persistence/canonical_payload_digest.rb +55 -0
- data/lib/smith/workflow/split_step_persistence/checkpoint.rb +119 -0
- data/lib/smith/workflow/split_step_persistence/checkpoint_state.rb +51 -0
- data/lib/smith/workflow/split_step_persistence/definition_boundary.rb +19 -0
- data/lib/smith/workflow/split_step_persistence/dispatch_boundary.rb +16 -0
- data/lib/smith/workflow/split_step_persistence/dispatch_claim.rb +118 -0
- data/lib/smith/workflow/split_step_persistence/dispatch_confirmation.rb +70 -0
- data/lib/smith/workflow/split_step_persistence/dispatch_verification.rb +35 -0
- data/lib/smith/workflow/split_step_persistence/execution.rb +128 -0
- data/lib/smith/workflow/split_step_persistence/execution_authorization.rb +89 -0
- data/lib/smith/workflow/split_step_persistence/execution_binding_collector.rb +90 -0
- data/lib/smith/workflow/split_step_persistence/execution_binding_snapshot.rb +95 -0
- data/lib/smith/workflow/split_step_persistence/execution_result_capture.rb +48 -0
- data/lib/smith/workflow/split_step_persistence/execution_verification.rb +65 -0
- data/lib/smith/workflow/split_step_persistence/execution_workflow_snapshot.rb +77 -0
- data/lib/smith/workflow/split_step_persistence/inheritance.rb +22 -0
- data/lib/smith/workflow/split_step_persistence/payloads.rb +54 -0
- data/lib/smith/workflow/split_step_persistence/preparation.rb +125 -0
- data/lib/smith/workflow/split_step_persistence/preparation_claim.rb +117 -0
- data/lib/smith/workflow/split_step_persistence/preparation_payload.rb +25 -0
- data/lib/smith/workflow/split_step_persistence/preparation_recovery.rb +54 -0
- data/lib/smith/workflow/split_step_persistence/recovery.rb +125 -0
- data/lib/smith/workflow/split_step_persistence/recovery_boundary.rb +56 -0
- data/lib/smith/workflow/split_step_persistence/recovery_class_methods.rb +14 -0
- data/lib/smith/workflow/split_step_persistence/replace_exact_signature.rb +46 -0
- data/lib/smith/workflow/split_step_persistence/restart_safe_adapter.rb +42 -0
- data/lib/smith/workflow/split_step_persistence/state_snapshot.rb +84 -0
- data/lib/smith/workflow/split_step_persistence/subclass_boundary.rb +102 -0
- data/lib/smith/workflow/split_step_persistence/transaction_identity.rb +45 -0
- data/lib/smith/workflow/split_step_persistence/transition_contract.rb +48 -0
- data/lib/smith/workflow/split_step_persistence/transition_contract_freezer.rb +83 -0
- data/lib/smith/workflow/split_step_persistence/transition_contract_signature.rb +107 -0
- data/lib/smith/workflow/split_step_persistence/transition_contract_structured_values.rb +51 -0
- data/lib/smith/workflow/split_step_persistence.rb +79 -0
- data/lib/smith/workflow/step_completion.rb +31 -0
- data/lib/smith/workflow/string_snapshot.rb +25 -0
- data/lib/smith/workflow/transition.rb +122 -133
- data/lib/smith/workflow/transition_actionability.rb +22 -0
- data/lib/smith/workflow.rb +57 -19
- data/lib/smith.rb +42 -2
- data/sig/smith.rbs +1 -0
- metadata +131 -19
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 5f0194d534f9aa31c42dc14a0bd92a092f0088b3ac138ffe65060b00c6c564a0
|
|
4
|
+
data.tar.gz: 314f07b2e7c51f2372fec28cf7d495672ce38f4ba1f09a9af4cccba6f9cefd17
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f75713ef01aa104f3add7f6804dc8f77061ad12ea787598a16dce09bb0b08c23e256f35b346d4f3e53c99b86a0bdb5603ca6f7236dc66a1c60130a16ab9180a5
|
|
7
|
+
data.tar.gz: 28953ec750b620efff05055af313031eb24e97f6591dea4ea04b029d8c71d0a72de37d2aecb6ec78c532f73d3301d854b6f853bd26515abd98f93a5fe4668edb
|
data/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,268 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). Version
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [0.5.0] - 2026-07-19
|
|
10
|
+
|
|
11
|
+
This release contains intentional pre-1.0 public contract tightening and is a
|
|
12
|
+
minor release rather than a `0.4.x` patch.
|
|
13
|
+
|
|
14
|
+
### Added
|
|
15
|
+
|
|
16
|
+
- Add configurable per-execution parallel branch/concurrency limits and a
|
|
17
|
+
shared finite exponential-backoff contract for workflow and persistence
|
|
18
|
+
retries.
|
|
19
|
+
- Keep nested fan-out inside one bounded execution context across Fiber
|
|
20
|
+
boundaries, inherit outer cancellation, use only idle top-level workers, and
|
|
21
|
+
drain already-running branch work rather than detaching work or forcibly
|
|
22
|
+
killing threads with live resources. Add a configurable nesting limit,
|
|
23
|
+
hard-capped at 256, so recursive host composition fails before Ruby stack
|
|
24
|
+
exhaustion.
|
|
25
|
+
|
|
26
|
+
- Add bounded host-coordinated session-message admission through
|
|
27
|
+
`Workflow#append_session_messages!`. Smith owns a canonical immutable copy,
|
|
28
|
+
serializes append against workflow execution, and returns an immutable
|
|
29
|
+
`MessageAdmission` digest witness while leaving persistence, idempotency,
|
|
30
|
+
session identity, and resume policy with the host.
|
|
31
|
+
- Add opt-in restart-safe prepared-step recovery. Workflow classes bind a
|
|
32
|
+
host-supplied executable `definition_digest`; hosts submit an immutable typed
|
|
33
|
+
`PreparedStepRecovery.not_started` decision; Smith verifies the exact
|
|
34
|
+
committed preparation before reconstructing a guarded boundary.
|
|
35
|
+
- Add exact-payload `replace_exact` and bounded `persistence_identity` adapter
|
|
36
|
+
capabilities. Restart-safe execution atomically advances the durable payload
|
|
37
|
+
from `prepared` to `dispatching` before transition work, preventing original
|
|
38
|
+
and recovered processes from both dispatching the same operation.
|
|
39
|
+
- Separate restart-safe dispatch claim, commit confirmation, and transition
|
|
40
|
+
execution so transactional hosts can atomically correlate their attempt
|
|
41
|
+
ledger before any provider, tool, or deterministic step work begins.
|
|
42
|
+
- Add immutable, bounded `PreparedStepDispatch` receipts. An exclusive host that
|
|
43
|
+
durably proves execution never started can reconstruct an exact committed
|
|
44
|
+
`dispatching` boundary without replaying its claim.
|
|
45
|
+
- Add a generic process-local prepared-step execution authorization. Smith
|
|
46
|
+
verifies an exact preparation or dispatch without performing transition work;
|
|
47
|
+
a host may then commit its own executing-attempt record before consuming the
|
|
48
|
+
non-copyable, process-bound capability exactly once. Standard Marshal,
|
|
49
|
+
Psych/YAML, and JSON serialization hooks reject the capability.
|
|
50
|
+
- Add `PreparedStepExecutionResult` so hosts can distinguish successful and
|
|
51
|
+
failure-routed transition attempts without inferring success from workflow
|
|
52
|
+
state.
|
|
53
|
+
- Add strict bounded `PreparedStep.deserialize` transport decoding and the
|
|
54
|
+
typed `Sha256Hex` scalar. Prepared step counters are constrained to positive
|
|
55
|
+
signed 64-bit values for both Hash and JSON transports.
|
|
56
|
+
|
|
57
|
+
- Expose `Workflow#pending_transition_name` so hosts can inspect the exact next
|
|
58
|
+
transition without serializing workflow state or traversing the graph.
|
|
59
|
+
- Expose an immutable `Smith::Workflow::PreparedStep` descriptor for an active
|
|
60
|
+
strict split-step boundary. Hosts can persist opaque token, transition,
|
|
61
|
+
persistence-version, step-number, and preparation-digest identity without
|
|
62
|
+
reaching into workflow internals or copying Smith state.
|
|
63
|
+
`prepare_persisted_step!` retains its existing transition-name return value.
|
|
64
|
+
- Compute descriptor identity before persistence dispatch with bounded,
|
|
65
|
+
canonical JSON hashing, preventing post-write validation failures and
|
|
66
|
+
unbounded preparation work.
|
|
67
|
+
- Fence transactional split-step descriptors to an adapter-provided exact
|
|
68
|
+
transaction identity. `ActiveRecordStore` uses Rails' public
|
|
69
|
+
`current_transaction.uuid`; custom transactional adapters fail before writing
|
|
70
|
+
unless they expose an equivalent identity.
|
|
71
|
+
|
|
72
|
+
### Changed
|
|
73
|
+
|
|
74
|
+
- Make budget settlement receipt-based and one-shot. Reservation, reconciliation,
|
|
75
|
+
and release now fail closed on cross-ledger, replayed, unknown-dimension, or
|
|
76
|
+
amount-only settlement and publish aggregate state atomically. This tightens
|
|
77
|
+
the pre-1.0 ledger API and requires the next minor release.
|
|
78
|
+
- Define budget scalar inputs as finite, non-negative `Integer` or `Float`
|
|
79
|
+
values and use exact internal decimal arithmetic so ordinary Float budgets do
|
|
80
|
+
not strand receipts or reject mathematically valid capacity. Ledger snapshots
|
|
81
|
+
remain JSON-safe numerics. This intentionally rejects opaque custom numeric
|
|
82
|
+
objects and non-JSON-safe numeric types at the boundary. Budget arithmetic is
|
|
83
|
+
isolated from and restores any host-configured `BigDecimal` precision limit;
|
|
84
|
+
`bigdecimal` is now an explicit runtime dependency.
|
|
85
|
+
- Reject repeated or mixed primary transition declarations instead of silently
|
|
86
|
+
replacing an earlier execution primitive.
|
|
87
|
+
- Treat named transition target-origin mismatches as graph errors rather than
|
|
88
|
+
warnings because runtime cannot execute the declared route from that state.
|
|
89
|
+
- Constrain workflow topology identifiers to non-blank String or Symbol values,
|
|
90
|
+
matching runtime lookup, event, persistence, and graph-inspection semantics.
|
|
91
|
+
- Reject oversized static fan-out declarations, non-finite or unschedulable
|
|
92
|
+
retry delays, and retry attempt counts above the configurable default limit
|
|
93
|
+
of 100 before executable work begins. This intentionally tightens previously
|
|
94
|
+
unbounded pre-1.0 declarations; hosts that require a higher finite attempt
|
|
95
|
+
bound must configure it before loading workflow classes.
|
|
96
|
+
|
|
97
|
+
- Restore persisted workflow state without mutating caller-owned nested outcome
|
|
98
|
+
data while preserving Smith's symbolized runtime outcome contract.
|
|
99
|
+
- Reject strict in-progress payloads before schema migration so a migration
|
|
100
|
+
cannot clear the uncertainty marker and replay a transition.
|
|
101
|
+
- Keep an ambiguously acknowledged exact dispatch claim fail-closed in durable
|
|
102
|
+
`dispatching` state. Smith does not infer or replay an uncertain external
|
|
103
|
+
outcome.
|
|
104
|
+
- Run Redis versioned and exact-payload CAS inside redis-rb's native
|
|
105
|
+
no-reconnect scope and do not replay either write after a transient
|
|
106
|
+
connection failure; a lost acknowledgement is outcome-unknown and must be
|
|
107
|
+
reconciled.
|
|
108
|
+
- Pin the executable definition digest for the complete split-step boundary and
|
|
109
|
+
seal it on the workflow class when preparation or recovery authority is
|
|
110
|
+
acquired. Concurrent digest setters linearize before sealing or fail before
|
|
111
|
+
claim and execution.
|
|
112
|
+
- Make Active Record exact replacement one conditional SQL update over key and
|
|
113
|
+
byte-exact payload while incrementing its optimistic-lock column. Validate
|
|
114
|
+
the database primary key or an unconditional single-column unique key index
|
|
115
|
+
through Active Record's table-keyed schema cache before each exact write.
|
|
116
|
+
- Resolve callable Redis command clients as clients rather than factories, and
|
|
117
|
+
require a native reconnect-disabling scope before any Redis CAS begins.
|
|
118
|
+
- Treat a persistence identity as available in doctor diagnostics only when its
|
|
119
|
+
value satisfies the same bounded non-empty contract used at runtime.
|
|
120
|
+
- Revalidate workflow definition and transition identity when an execution
|
|
121
|
+
authorization is consumed. Capability release and execution are linearized by
|
|
122
|
+
exact identity so concurrent or stale holders cannot revoke or replay work.
|
|
123
|
+
- Route an active prepared execution through a Smith-owned private-method
|
|
124
|
+
membrane, including reachable nested workflows. Subclass overrides retain
|
|
125
|
+
normal Ruby dispatch outside the authorized boundary but cannot substitute
|
|
126
|
+
retry, dispatch, guardrail, budget, or completion behavior during it. Nested
|
|
127
|
+
authority is limited to the root execution thread and revoked when that
|
|
128
|
+
execution closes, even when a host retains a child workflow instance.
|
|
129
|
+
- Own String workflow identifiers at declaration time and keep graph snapshots
|
|
130
|
+
isolated from mutable caller aliases. Reject other topology object types at
|
|
131
|
+
the DSL boundary so graph lookup, event, and persistence semantics remain
|
|
132
|
+
consistent. The authorized
|
|
133
|
+
completion and failure boundaries bind Smith's implementation directly so
|
|
134
|
+
subclass method-name collisions cannot bypass typed result capture.
|
|
135
|
+
- Capture exact direct, fan-out, optimizer, orchestrator, and reachable nested
|
|
136
|
+
agent bindings during authorization with bounded `O(V + E)` traversal. Later
|
|
137
|
+
mutable registry replacement cannot alter the authorized execution.
|
|
138
|
+
Same-agent parallel execution resolves its captured binding on the authorized
|
|
139
|
+
root thread and passes an identity-scoped class reference into worker
|
|
140
|
+
branches without leaking it into re-entrant workflows on the same thread.
|
|
141
|
+
- Replace recursive graph reachability with an iterative walk over a graph-local
|
|
142
|
+
outgoing index. Existing graph snapshots remain isolated from later workflow
|
|
143
|
+
class mutation. State-reference validation and terminal-state metrics use
|
|
144
|
+
constant-time graph indexes. Nested runtime-readiness inspection uses an
|
|
145
|
+
iterative postorder traversal with workflow-class identity memoization and
|
|
146
|
+
cycle-edge diagnostics. Nested workflow contracts are captured and
|
|
147
|
+
revalidated before and immediately after child construction without repeated
|
|
148
|
+
full-transition scans.
|
|
149
|
+
- Bound durable preparation comparison by canonical payload byte and node
|
|
150
|
+
limits, and preserve the legacy mutable step Hash while typed execution
|
|
151
|
+
results own an immutable, cycle-aware, bounded JSON-like snapshot. Result
|
|
152
|
+
validation now completes before successful workflow state mutation; invalid
|
|
153
|
+
provider output enters normal failure routing and returns a typed failure.
|
|
154
|
+
Hash keys are limited to owned String or Symbol values, and non-finite Floats
|
|
155
|
+
are rejected before success mutation.
|
|
156
|
+
- Normalize top-level string keys in restored message Hashes before rebuilding
|
|
157
|
+
RubyLLM messages. Nested message content remains untouched so transport
|
|
158
|
+
decoding does not rewrite host payloads.
|
|
159
|
+
|
|
160
|
+
- Maintain a declaration-time outgoing-transition index. First-transition and
|
|
161
|
+
terminal checks are constant-time; enumerating transitions from one state is
|
|
162
|
+
proportional to that state's outdegree. Stable declaration precedence,
|
|
163
|
+
subclass isolation, and transition redefinition semantics are preserved.
|
|
164
|
+
- Custom adapters that report an open transaction now require
|
|
165
|
+
`transaction_identity` for strict split-step preparation. This intentionally
|
|
166
|
+
tightens the 0.4.5 boolean transaction contract so later unrelated
|
|
167
|
+
transactions cannot re-authorize a rolled-back descriptor.
|
|
168
|
+
|
|
169
|
+
### Verification
|
|
170
|
+
|
|
171
|
+
- Default suite: 1,304 examples, 0 failures on the final tree.
|
|
172
|
+
- Runtime-hardening practical suite: 551 scenarios and 33,722 assertions across
|
|
173
|
+
graph reachability, retry/backoff, nested parallel execution, cancellation,
|
|
174
|
+
receipt-based budgets, hostile decimal precision, and asynchronous thread
|
|
175
|
+
interruption.
|
|
176
|
+
- The built gem and its declared dependency set loaded under Ruby 3.2.2 and
|
|
177
|
+
Ruby 3.2.8; each runtime passed the same 551-scenario practical suite.
|
|
178
|
+
- Downstream verification against the local checkout: Smith Runtime passed
|
|
179
|
+
1,188 runs and 6,287 assertions with 35 skips, plus 20 practical scenarios;
|
|
180
|
+
Smith Studio passed 1,848 runs and 13,851 assertions with one skip, plus 20
|
|
181
|
+
practical scenarios.
|
|
182
|
+
- Message-admission and restore-input suite: 30 examples, 0 failures. A
|
|
183
|
+
44-scenario public-API matrix covered canonicalization, alias isolation,
|
|
184
|
+
bounded rejection, lifecycle contention, persistence round trips, and a
|
|
185
|
+
90,000-value near-limit message.
|
|
186
|
+
- Focused graph, execution-authorization, binding-snapshot, typed-result, and
|
|
187
|
+
restored-message suite: 71 examples, 0 failures. All newly added files pass RuboCop;
|
|
188
|
+
the changed surface passes after excluding the repository's pre-existing
|
|
189
|
+
metrics baseline, and `git diff --check` passes.
|
|
190
|
+
- Practical gem execution: 30 restart-safe scenarios covering serialized
|
|
191
|
+
preparation and dispatch recovery, exact-claim contention, corruption,
|
|
192
|
+
definition drift, ambiguous acknowledgements, and Active Record
|
|
193
|
+
commit/rollback coordination; a real redis-rb 5.4.1 run additionally proved
|
|
194
|
+
direct-client and factory resolution plus bounded multi-client exact-claim
|
|
195
|
+
contention.
|
|
196
|
+
- Practical execution-authorization matrix: 30 public-API scenarios covering
|
|
197
|
+
success and handled failure variants, immutable graph snapshots up to 5,000
|
|
198
|
+
transitions, cyclic and shared result values, bounded rejection cases,
|
|
199
|
+
release, single-use authorization, copying, serialization, and concurrent
|
|
200
|
+
claims.
|
|
201
|
+
- Additional practical hardening matrix: 20 public-API checks covering mutable
|
|
202
|
+
topology projection, 1,000/2,000/4,000-transition validation, 5,000-transition
|
|
203
|
+
reachability, a 1,200-workflow nested chain, shared-child memoization, cycle
|
|
204
|
+
diagnostics, root and nested execution membranes, and authorization
|
|
205
|
+
contention. Measured validation times were 5.80 ms, 12.80 ms, and 16.39 ms
|
|
206
|
+
respectively on the local Ruby 4.0.1 verification host.
|
|
207
|
+
- A downstream host passed its serial and parallel suites against the
|
|
208
|
+
local Smith checkout and completed its practical direct-model boundary.
|
|
209
|
+
|
|
210
|
+
## [0.4.5] - 2026-07-11
|
|
211
|
+
|
|
212
|
+
Patch release for generic host-coordinated workflow step boundaries and
|
|
213
|
+
fail-closed persistence correctness. Smith now exposes a bounded strict
|
|
214
|
+
split-step protocol while leaving transactions, scheduling, lifecycle records,
|
|
215
|
+
tools, and product policy under host ownership.
|
|
216
|
+
|
|
217
|
+
### Fixed
|
|
218
|
+
|
|
219
|
+
- Align `ActiveRecordStore#store_versioned` with Smith's persistence contract by
|
|
220
|
+
comparing `expected_version` to the stored payload's `persistence_version`.
|
|
221
|
+
Rails' optimistic-locking column remains an independent row-level CAS token,
|
|
222
|
+
so consecutive workflow persists no longer report a false conflict after the
|
|
223
|
+
initial insert.
|
|
224
|
+
- Fail closed when an Active Record host model does not have optimistic locking
|
|
225
|
+
enabled on the adapter's configured `version_column`. Custom locking columns
|
|
226
|
+
remain host-owned and must be configured on the model explicitly.
|
|
227
|
+
- Use Rails' native create-or-find savepoint for concurrent initial inserts,
|
|
228
|
+
preserving callback rollbacks and distinguishing key collisions from other
|
|
229
|
+
unique-constraint failures.
|
|
230
|
+
- Keep malformed payload-version handling consistent across versioned adapters,
|
|
231
|
+
reject scalar state documents, fail closed when an explicit persisted version
|
|
232
|
+
is invalid, and never replay an uncertain versioned write below a host-owned
|
|
233
|
+
transaction boundary.
|
|
234
|
+
- Resolve string-backed Active Record models on each operation so host framework
|
|
235
|
+
reloads cannot leave the adapter holding a stale class object.
|
|
236
|
+
- Add a generic strict split-step persistence contract for hosts that coordinate
|
|
237
|
+
pre/post transition state without holding a transaction across provider or
|
|
238
|
+
tool execution. Mutable execution state and non-expiring persistence policy
|
|
239
|
+
are pinned for the boundary, subclass entry points remain guarded, and a
|
|
240
|
+
proven transaction rollback can retry from the exact committed preparation.
|
|
241
|
+
Same-transaction atomicity remains adapter- and host-owned.
|
|
242
|
+
- Prevent Memory, Redis, and Active Record versioned adapters from recreating a
|
|
243
|
+
missing key when the caller expects a nonzero logical version. Missing state
|
|
244
|
+
now reports `PersistenceVersionConflict` with `actual: :missing`.
|
|
245
|
+
- Bound split-step transition contract capture to cycle-aware `O(V + E)` traversal with explicit
|
|
246
|
+
node, byte, and depth limits; reject opaque mutable values; freeze supported
|
|
247
|
+
structured configuration; and preserve execution guards when workflow
|
|
248
|
+
subclasses receive later prepends.
|
|
249
|
+
- Keep prepared transition execution on Smith's owned `advance!` path so host
|
|
250
|
+
wrappers cannot run as transition authority inside an active boundary.
|
|
251
|
+
- Make the split-step aggregate own its internal require order so direct loading
|
|
252
|
+
does not depend on `smith.rb` preloading implementation files.
|
|
253
|
+
- Require reconciliation before retrying an ambiguously acknowledged
|
|
254
|
+
checkpoint and retain a single checkpoint witness, keeping retry state in
|
|
255
|
+
constant space.
|
|
256
|
+
- Make Memory expiry atomic with version comparison, isolate mutable payload
|
|
257
|
+
strings at its boundary, and pin Active Record column configuration.
|
|
258
|
+
|
|
259
|
+
### Verification
|
|
260
|
+
|
|
261
|
+
- Default suite: 1,045 examples, 0 failures.
|
|
262
|
+
- Focused split-step and versioned-adapter suite: 107 examples, 0 failures;
|
|
263
|
+
changed files pass RuboCop and `git diff --check`.
|
|
264
|
+
- Practical gem execution: 30 distinct 20-step workflow classes, 600 Memory
|
|
265
|
+
split steps, 1,000 Memory compare-and-swap writes, and 200 Active Record
|
|
266
|
+
split steps with restore after every committed checkpoint.
|
|
267
|
+
- Smith Runtime host acceptance on Ruby 4.0.1 and Rails 8.1.3: 251 tests,
|
|
268
|
+
816 assertions, 0 failures; 20 practical signed-package compiles produced
|
|
269
|
+
valid Smith reports and cleaned every generated namespace.
|
|
270
|
+
|
|
9
271
|
## [0.4.4] - 2026-07-10
|
|
10
272
|
|
|
11
273
|
Patch release for provider-safe workflow handoffs. Smith keeps accepted agent
|
data/README.md
CHANGED
|
@@ -17,7 +17,7 @@ environment before calling the slice complete.
|
|
|
17
17
|
|
|
18
18
|
```ruby
|
|
19
19
|
# Gemfile
|
|
20
|
-
gem "smith-agents", "0.
|
|
20
|
+
gem "smith-agents", "0.5.0", require: "smith"
|
|
21
21
|
```
|
|
22
22
|
|
|
23
23
|
```bash
|
|
@@ -175,6 +175,12 @@ Smith.configure do |config|
|
|
|
175
175
|
config.persistence_ttl = 1.day.to_i
|
|
176
176
|
config.persistence_retry_policy = { attempts: 3, base_delay: 0.1, max_delay: 1.0 }
|
|
177
177
|
|
|
178
|
+
# Generic runtime resource bounds
|
|
179
|
+
config.parallel_branch_limit = 1_000
|
|
180
|
+
config.parallel_concurrency = 8
|
|
181
|
+
config.parallel_nesting_limit = 64
|
|
182
|
+
config.retry_attempt_limit = 100
|
|
183
|
+
|
|
178
184
|
# OpenAI /v1/responses routing for gpt-5 + tools + thinking. :auto (default) or :off.
|
|
179
185
|
config.openai_api_mode = :auto
|
|
180
186
|
|
|
@@ -200,10 +206,21 @@ result = ReplyWorkflow.run_persisted!(
|
|
|
200
206
|
key: "ticket:T-1042",
|
|
201
207
|
adapter: Smith.persistence_adapter
|
|
202
208
|
)
|
|
209
|
+
|
|
210
|
+
# Admit host-supplied conversation input at a stable workflow boundary.
|
|
211
|
+
workflow = ReplyWorkflow.restore("ticket:T-1042", adapter: Smith.persistence_adapter)
|
|
212
|
+
admission = workflow.append_session_messages!(role: :user, content: "Continue")
|
|
203
213
|
```
|
|
204
214
|
|
|
215
|
+
`append_session_messages!` validates and owns a bounded canonical copy of the
|
|
216
|
+
message batch, appends it under Smith's workflow lifecycle lock, and returns an
|
|
217
|
+
immutable digest witness. The host still owns session identity, transaction
|
|
218
|
+
coordination, persistence, idempotency, and resume policy.
|
|
219
|
+
|
|
205
220
|
Built-in adapters (all support TTL where the backend allows; `Redis`,
|
|
206
|
-
`ActiveRecord`, and `Memory` support optimistic locking via `store_versioned
|
|
221
|
+
`ActiveRecord`, and `Memory` support optimistic locking via `store_versioned`
|
|
222
|
+
and exact dispatch claims via `replace_exact`; `ActiveRecord` participates in
|
|
223
|
+
the caller's transaction for host-record coordination;
|
|
207
224
|
`Redis` and `Memory` also support heartbeat probes via `record_heartbeat` /
|
|
208
225
|
`last_heartbeat`):
|
|
209
226
|
|
|
@@ -211,9 +228,51 @@ Built-in adapters (all support TTL where the backend allows; `Redis`,
|
|
|
211
228
|
- `:redis` — Redis client; uses WATCH/MULTI/EXEC for CAS
|
|
212
229
|
- `:rails_cache`, `:solid_cache` — Rails cache backends
|
|
213
230
|
- `:cache_store` — any object responding to `write/read/delete`
|
|
214
|
-
- `:active_record` — keyed ActiveRecord model with
|
|
215
|
-
|
|
216
|
-
|
|
231
|
+
- `:active_record` — keyed ActiveRecord model with an enabled optimistic-locking
|
|
232
|
+
column for row-level CAS; Smith's logical version remains in the JSON payload
|
|
233
|
+
|
|
234
|
+
See [`docs/PERSISTENCE.md`](docs/PERSISTENCE.md) for schema versioning,
|
|
235
|
+
seed-drift validation, the `idempotency_mode :strict` step-in-progress contract,
|
|
236
|
+
host-coordinated message admission, and opt-in restart-safe prepared recovery.
|
|
237
|
+
Hosts that need to commit a durable attempt before provider or tool work can
|
|
238
|
+
separate verification from execution with
|
|
239
|
+
`authorize_prepared_step_execution!`, commit their own attempt ledger, and then
|
|
240
|
+
consume the exact process-local capability through
|
|
241
|
+
`execute_authorized_prepared_step!`. Smith does not own the host lease, attempt
|
|
242
|
+
record, scheduler, or unknown-outcome policy. Authorization discovers the exact
|
|
243
|
+
agent bindings reachable by that transition with a bounded `O(V + E)` graph
|
|
244
|
+
walk, then resolves the bounded binding set in one registry epoch. A concurrent
|
|
245
|
+
registry writer therefore cannot produce a mixed authorization containing
|
|
246
|
+
bindings from two registry states or replace a captured binding between
|
|
247
|
+
authorization and work. Every public Dry::Container mutation path participates
|
|
248
|
+
in that epoch. Execution authorization accepts only concrete registered
|
|
249
|
+
`Smith::Agent` classes; lazy container callbacks are not executed while
|
|
250
|
+
authority is captured.
|
|
251
|
+
While the capability is active, a host may call `each_agent_binding` with a
|
|
252
|
+
block to inspect the exact captured `(registry_name, agent_class)` pairs before
|
|
253
|
+
external work. Enumeration is process-local, performs no dispatch, and is
|
|
254
|
+
rejected after release or consumption; it deliberately does not return a lazy
|
|
255
|
+
enumerator that could escape the authorization scope.
|
|
256
|
+
Reachable nested workflow transition contracts are captured and revalidated
|
|
257
|
+
before child execution. The typed result owns a bounded snapshot of JSON-like
|
|
258
|
+
Ruby values; unsupported mutable output values fail closed.
|
|
259
|
+
During authorized execution, Smith also seals its private execution path against
|
|
260
|
+
workflow subclass method-name collisions, including nested child workflows.
|
|
261
|
+
Ordinary non-authorized runs retain normal Ruby override behavior.
|
|
262
|
+
|
|
263
|
+
Hosts that generate agent classes dynamically may call
|
|
264
|
+
`register_as :name, publish: false` while constructing the class. This records
|
|
265
|
+
the class identity without exposing it through `Smith::Agent::Registry`. After
|
|
266
|
+
the host has validated and sealed the class, it may publish the exact class with
|
|
267
|
+
`agent_class.publish_registration!`. Publication always uses the staged,
|
|
268
|
+
immutable identity; callers cannot provide a second key. Registry collision and
|
|
269
|
+
mutation epoch guarantees remain unchanged.
|
|
270
|
+
|
|
271
|
+
Hosts that persist or cache generated executable definitions should include
|
|
272
|
+
`Smith::EXECUTION_SEMANTICS_VERSION` in their definition identity. The gem
|
|
273
|
+
version describes the package release; the execution-semantics version changes
|
|
274
|
+
when Smith's executable workflow contract changes and must participate in host
|
|
275
|
+
cache invalidation.
|
|
217
276
|
|
|
218
277
|
## Tools and Guardrails
|
|
219
278
|
|
|
@@ -239,6 +298,30 @@ end
|
|
|
239
298
|
|
|
240
299
|
Budgets reserve serially at each step and reconcile after the agent call. Parallel branches reserve scoped envelopes that release back to the parent ledger. The `Workflow::RunResult` carries `total_tokens`, `total_cost`, and per-call `usage_entries`.
|
|
241
300
|
|
|
301
|
+
Direct ledger integrations settle the exact receipt returned by reservation:
|
|
302
|
+
|
|
303
|
+
```ruby
|
|
304
|
+
reservation = ledger.reserve_many!(total_tokens: 1_000, total_cost: 0.10)
|
|
305
|
+
ledger.reconcile_many!(reservation, actual: { total_tokens: 640, total_cost: 0.06 })
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
`release!` / `release_many!` accept the same receipt when work never starts.
|
|
309
|
+
Receipts are ledger-owned and one-shot: another ledger, a replay, or an
|
|
310
|
+
amount-only settlement is rejected.
|
|
311
|
+
|
|
312
|
+
Budget amounts accept finite, non-negative `Integer` and `Float` values. Smith
|
|
313
|
+
uses an exact `BigDecimal` representation derived from each value's canonical
|
|
314
|
+
decimal text and converts ledger snapshots back to ordinary JSON-safe numerics,
|
|
315
|
+
avoiding cumulative binary Float drift without changing persisted workflow
|
|
316
|
+
state shapes. Ledger arithmetic temporarily uses unlimited decimal precision and
|
|
317
|
+
restores the host's existing `BigDecimal` precision limit before returning.
|
|
318
|
+
|
|
319
|
+
Budget operations are atomic for normal concurrent callers and synchronous
|
|
320
|
+
exceptions. Smith uses cooperative cancellation and never terminates branch
|
|
321
|
+
threads with `Thread#raise` or `Thread#kill`. Arbitrary asynchronous thread
|
|
322
|
+
termination can make any in-process return value outcome-ambiguous; hosts must
|
|
323
|
+
use cancellation signals, bounded I/O, and process isolation for hard stops.
|
|
324
|
+
|
|
242
325
|
## Doctor
|
|
243
326
|
|
|
244
327
|
After adding Smith, verify the integration:
|
|
@@ -295,13 +378,40 @@ When no classes are passed, `retry_on` uses `Smith::Errors.retryable?`.
|
|
|
295
378
|
This is a bounded local transition retry policy. Durable scheduling, long waits,
|
|
296
379
|
and external idempotency guarantees remain host-owned.
|
|
297
380
|
|
|
381
|
+
## Runtime Complexity
|
|
382
|
+
|
|
383
|
+
- Executable graph reachability is iterative `O(V + E)` time and `O(V)` space,
|
|
384
|
+
using indexed outgoing transitions and runtime-equivalent successor rules.
|
|
385
|
+
- Parallel execution is `O(B)` scheduling and result space for `B` branches,
|
|
386
|
+
with at most `C = parallel_concurrency` active worker threads per top-level
|
|
387
|
+
execution. Re-entrant fan-out inherits the same cancellation signal and uses
|
|
388
|
+
only idle workers from the top-level execution context, falling back to the
|
|
389
|
+
current worker when no capacity remains. Nested calls fail closed before the
|
|
390
|
+
configurable nesting bound can exhaust Ruby's stack. Cancellation prevents
|
|
391
|
+
queued branch bodies from starting;
|
|
392
|
+
already-running branches are drained before control returns so work is never
|
|
393
|
+
detached. Branch adapters must use bounded I/O and honor the cancellation
|
|
394
|
+
signal. Hosts continue to own process-level concurrency and hard deadlines.
|
|
395
|
+
- Reservation and settlement across `L` declared budget dimensions and `D`
|
|
396
|
+
operation dimensions is `O(L + D)` time and space. Receipt lookup is expected
|
|
397
|
+
`O(1)`; no operation scans or copies the active receipt registry. Integer and
|
|
398
|
+
Float amounts use exact decimal arithmetic internally.
|
|
399
|
+
- Retry delay calculation is `O(1)` time and space per attempt. A policy with
|
|
400
|
+
`A` attempts performs at most `A` guarded operations and never materializes an
|
|
401
|
+
exponential schedule.
|
|
402
|
+
|
|
298
403
|
## Development
|
|
299
404
|
|
|
405
|
+
Smith changes must not use brute-force graph search, validation, scheduling, or
|
|
406
|
+
class-generation algorithms. Document the relevant time and space complexity,
|
|
407
|
+
prefer indexed, linear, logarithmic, or otherwise bounded approaches, and treat
|
|
408
|
+
an avoidable asymptotic regression as a release blocker.
|
|
409
|
+
|
|
300
410
|
```bash
|
|
301
411
|
bundle install
|
|
302
412
|
bundle exec rspec
|
|
303
413
|
bundle exec rubocop
|
|
304
414
|
```
|
|
305
415
|
|
|
306
|
-
|
|
416
|
+
1,175 examples, MIT licensed. See [`CHANGELOG.md`](CHANGELOG.md) for the current
|
|
307
417
|
release surface.
|
data/docs/CONFIGURATION.md
CHANGED
|
@@ -105,6 +105,10 @@ end
|
|
|
105
105
|
| `persistence_options` | Per-adapter options (client, namespace, model, columns) | See "Built-In Persistence Adapters" |
|
|
106
106
|
| `persistence_ttl` | Global TTL for persisted state (Integer/Float seconds; nil = no expiry) | Set when long-tail abandoned workflows accumulate in storage |
|
|
107
107
|
| `persistence_retry_policy` | Exponential-backoff policy for transient adapter I/O failures | Defaults to `{ attempts: 3, base_delay: 0.1, max_delay: 1.0 }` |
|
|
108
|
+
| `retry_attempt_limit` | Maximum attempts accepted by workflow and persistence retry policies | Defaults to `100` |
|
|
109
|
+
| `parallel_branch_limit` | Maximum branches accepted by one homogeneous or heterogeneous fan-out | Defaults to `1_000` |
|
|
110
|
+
| `parallel_concurrency` | Maximum active branch threads within one parallel execution | Defaults to `8` |
|
|
111
|
+
| `parallel_nesting_limit` | Maximum re-entrant fan-out depth; hard-capped at `256` | Defaults to `64` |
|
|
108
112
|
| `test_mode` | Auto-select `:memory` adapter when `persistence_adapter` is nil | Enable in `spec_helper.rb` to skip Redis/cache wiring in tests |
|
|
109
113
|
| `openai_api_mode` | `:auto` routes (gpt-5 family + tools + thinking) via `/v1/responses` using Smith's vendored Responses adapter (sync only; streaming over `/v1/responses` is not yet supported); `:off` drops incompatible tools instead | Leave `:auto` (default) unless you need streaming with the (gpt-5 + tools + thinking) combo, in which case set `:off` for graceful tool-dropping |
|
|
110
114
|
| `trace_normalizer` | Emit `:normalizer_decision` trace events from `Smith::Models::Normalizer` | Useful when debugging cross-provider request shaping |
|
|
@@ -120,4 +124,3 @@ Add settings in this order:
|
|
|
120
124
|
4. `config.pricing`
|
|
121
125
|
|
|
122
126
|
Do not start by configuring every advanced switch at once.
|
|
123
|
-
|
data/docs/PATTERNS.md
CHANGED
|
@@ -234,10 +234,17 @@ end
|
|
|
234
234
|
Why this is valuable:
|
|
235
235
|
|
|
236
236
|
- Smith treats each branch as a real invocation
|
|
237
|
+
- results preserve branch declaration order while active branch work is bounded by `parallel_concurrency`
|
|
237
238
|
- workflow budgets remain cumulative outer limits
|
|
238
239
|
- agent budgets still narrow each branch call
|
|
239
240
|
- branch failures discard step output and route through normal failure handling
|
|
240
241
|
- prepared input is reused consistently across branches
|
|
242
|
+
- cancellation prevents queued branch bodies from starting and drains any
|
|
243
|
+
already-running branch before returning; branch adapters should use bounded
|
|
244
|
+
I/O and honor the cancellation signal
|
|
245
|
+
- nested fan-out shares its top-level execution context and cancellation signal;
|
|
246
|
+
it uses only idle workers and falls back to its current worker when no capacity
|
|
247
|
+
remains, preserving the configured concurrency ceiling without pool deadlocks
|
|
241
248
|
|
|
242
249
|
## Example 6: Heterogeneous Fan-Out
|
|
243
250
|
|