phronomy 0.32.0 → 0.34.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.
Files changed (58) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +139 -0
  3. data/CONTRIBUTING.md +5 -5
  4. data/README.md +20 -1
  5. data/RELEASE_CHECKLIST.md +7 -1
  6. data/SECURITY.md +1 -1
  7. data/VERIFY.sh +1 -9
  8. data/docs/architecture/error-api-review.md +6 -2
  9. data/docs/architecture/structured-output-and-scope.md +8 -8
  10. data/docs/architecture/trust-acceptance-followup.md +15 -0
  11. data/docs/context-policy-budgeting.md +108 -0
  12. data/docs/decisions/016-semantic-multi-agent-handoff.md +6 -5
  13. data/docs/examples/shared_state_custom_sharing.rb +145 -0
  14. data/docs/features.md +4 -2
  15. data/docs/mcp-client.md +4 -2
  16. data/docs/migrations/cleanup-audit.md +89 -0
  17. data/docs/migrations/legacy-format-removal.md +55 -0
  18. data/docs/migrations/r8-unit6.md +4 -2
  19. data/docs/migrations/shared-state-multi-agent.md +5 -0
  20. data/docs/migrations/vector-async-clients.md +6 -1
  21. data/docs/runtime-and-concurrency.md +36 -0
  22. data/docs/shared-state-sharing.md +233 -0
  23. data/lib/phronomy/agent/base.rb +53 -0
  24. data/lib/phronomy/agent/execution/agent_invocation.rb +0 -4
  25. data/lib/phronomy/agent/execution/execution_coordinator.rb +11 -0
  26. data/lib/phronomy/agent/handoff/transfer_projection.rb +0 -9
  27. data/lib/phronomy/agent/journal/journal_record.rb +2 -2
  28. data/lib/phronomy/agent/lifecycle/hosted_invocation.rb +2 -5
  29. data/lib/phronomy/agent/recovery/recovery_rules.rb +0 -12
  30. data/lib/phronomy/agent/store.rb +3 -8
  31. data/lib/phronomy/agent/tool_execution/tool_approval_request.rb +3 -2
  32. data/lib/phronomy/agent/tool_execution/tool_invocation.rb +9 -36
  33. data/lib/phronomy/context/context_policy.rb +13 -8
  34. data/lib/phronomy/context/context_policy_input.rb +26 -0
  35. data/lib/phronomy/context/llm_input_manifest.rb +3 -3
  36. data/lib/phronomy/engine/runtime/shutdown_result.rb +0 -5
  37. data/lib/phronomy/execution/execution.rb +0 -11
  38. data/lib/phronomy/multi_agent/shared_state/binding.rb +44 -0
  39. data/lib/phronomy/multi_agent/shared_state/member_lifecycle_error.rb +32 -0
  40. data/lib/phronomy/multi_agent/shared_state.rb +152 -10
  41. data/lib/phronomy/tool/base.rb +69 -3
  42. data/lib/phronomy/tool/schema.rb +23 -11
  43. data/lib/phronomy/version.rb +1 -1
  44. data/scripts/api_snapshot.rb +6 -3
  45. data/sig/phronomy/agent.rbs +1 -0
  46. data/sig/phronomy/context.rbs +3 -0
  47. data/sig/phronomy/multi_agent.rbs +26 -2
  48. data/sig/phronomy/tool.rbs +1 -0
  49. metadata +9 -11
  50. data/lib/phronomy/execution/scheduler_reentrancy_error.rb +0 -9
  51. data/lib/phronomy/persistence/migration/initial_format_migration.rb +0 -226
  52. data/scripts/add_to_h_to_token_doubles.rb +0 -33
  53. data/scripts/add_to_h_unnamed_doubles.rb +0 -27
  54. data/scripts/migrate_generator_verifier_result.rb +0 -18
  55. data/scripts/migrate_spec_agent_definition.rb +0 -108
  56. data/scripts/migrate_spec_agent_definition_pass2.rb +0 -53
  57. data/scripts/migrate_spec_inline_pass3.rb +0 -24
  58. data/sig/phronomy/persistence/migration.rbs +0 -13
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8eeef6dddca310da11fcdcaf7ffc2490191151cfa070384be8d04f84366efb08
4
- data.tar.gz: 2b43c66ff8c5484360b2a953bcdd4aa754ef99c3b7bc944285993d866ee34007
3
+ metadata.gz: 4c67e91b9abb65c5ab2ddc5b657afa6843b916a5ba84aefcd8f808ae297c3236
4
+ data.tar.gz: b5894936fa47d493da81c650edbf3c330eaeec7101c68ec91acf96bec00d1a79
5
5
  SHA512:
6
- metadata.gz: 9ebee2313bc7d60b27c6e42e0909fbb1102cca3c220f25575d1c755b18f3d1c00b7ea0dfec36646155a7aff325ba47c99af202c9b7fbf226d45c4fbb07b8bff9
7
- data.tar.gz: 3b089fdc8447a0a56c66d10d64c809eb26ddc31be6800a55c095409e87e649047afbc42fa1872eab8fd68b7939bd6845edc194b5ec4ab03beca602408f320596
6
+ metadata.gz: ebec13bc657697922f3f487eefe809a35570b36a574e8a3ea95d5124459035ca263ad39b89419e8042621e4971f550cbb7d21ab206d2961f8a7f01bec6b9514e
7
+ data.tar.gz: b1d930edb100e7782522a9d666d555756fcc50e5be46881c58c035f2e021631093132b4bde4cec8134f296f068ddb9cb0fd17548ea356afad32652d484b7da91
data/CHANGELOG.md CHANGED
@@ -12,6 +12,145 @@ Release history for 0.14.0 and earlier is archived in
12
12
 
13
13
  ## [Unreleased]
14
14
 
15
+ ## [0.34.0] - 2026-11-16
16
+
17
+ ### Changed
18
+
19
+ - Route ContextPolicy item estimates directly through the protected
20
+ `estimate_tokens` extension operation, removing the redundant private wrapper.
21
+ - Exercise Tool approval redaction through the display/snapshot path used by
22
+ production approval requests instead of internal convenience getters.
23
+ - Replace references to an absent repository language-check script with an
24
+ external maintainer check and recorded evidence. Correct the Storage phase
25
+ status: P4 is implemented; the separate final P5 review remains pending.
26
+
27
+ ### Fixed
28
+
29
+ - Derive default SharedState members through `Agent::Base.with_tools`, retaining
30
+ Phronomy authoring settings and filters as custom sharing already does. A
31
+ member's iteration limit, temperature and other configuration no longer reset
32
+ when built-in sharing Tools are added. Preserve Tool aliases, the generated
33
+ definition identity and default findings-store behavior.
34
+
35
+ ### Removed
36
+
37
+ - Remove Handoff's old selection-unit metadata fallback and Agent Store's raw
38
+ argument fallback. Current conversation groups and exact validated arguments
39
+ remain the authoritative saved facts; current record writers are unchanged.
40
+ - Retire `Persistence::Migration::InitialFormatMigration` and the old
41
+ GeneratorVerifier Result JSON conversion command, including their dedicated
42
+ migration tests and active usage instructions. No replacement converter or
43
+ compatibility entry point is supplied. Current codecs, Result validation and
44
+ the separate Unit4 snapshot migration remain. See the
45
+ [legacy-format cleanup](docs/migrations/legacy-format-removal.md).
46
+ - Remove `Runtime::ShutdownResult#success?`; use the equivalent
47
+ `cleanup_complete?`. Keep `clean?`, which additionally requires a graceful
48
+ shutdown.
49
+ - Remove the obsolete `SchedulerReentrancyError` subclass; use
50
+ `EventLoopReentrancyError`, which is already raised by current execution paths.
51
+ No compatibility aliases are retained. See the
52
+ [cleanup migration](docs/migrations/cleanup-audit.md).
53
+ - Remove unused internal Invocation, RecoveryRules and Execution helpers, along
54
+ with five completed development-only spec migration scripts. Keep the public
55
+ `JournalProjection#llm_call_records` and
56
+ `LLMInputManifest#referenced_content_refs` operations and the Scope hooks still
57
+ used for result composition.
58
+
59
+ ### Compatibility and verification
60
+
61
+ - Replace `Runtime::ShutdownResult#success?` with `cleanup_complete?` and rescue
62
+ `EventLoopReentrancyError` instead of the removed `SchedulerReentrancyError`.
63
+ No compatibility aliases are retained. See the
64
+ [cleanup migration](docs/migrations/cleanup-audit.md).
65
+ - Historical pre-S3 and GeneratorVerifier Result conversion support is retired
66
+ without a replacement command. Current record writers and saved representation
67
+ are unchanged; this release does not rewrite existing stores. Handoff no longer
68
+ derives groups from old selection-unit metadata, and authorization requires
69
+ validated argument evidence. Review the
70
+ [legacy-format migration](docs/migrations/legacy-format-removal.md) before
71
+ upgrading applications that still consume those historical records.
72
+ - Previously observed LM Studio/gpt-oss PEG-format failures are not resolved by
73
+ this cleanup. This release does not claim to fix their root cause or prevent
74
+ recurrence; offline persistence/recovery checks do not establish full real-LLM
75
+ acceptance.
76
+
77
+ ## [0.33.0] - 2026-11-14
78
+
79
+ ### Added
80
+
81
+ - Add public `ContextPolicyInput#input_token_limit` and
82
+ `#with_input_token_limit(limit)` for immutable, non-expanding selection limits,
83
+ plus protected `ContextPolicy#estimate_tokens(content)` using the same
84
+ canonical estimator as generated Policy items. Applications can reserve input
85
+ capacity while retaining DefaultPolicy selection without constructing private
86
+ budget objects or calling private token-estimation helpers. These operations
87
+ do not change model capabilities, generation settings or final validation.
88
+ - Add `Agent::Base.with_tools(definitions, id:, version:)` to derive an Agent
89
+ class with a complete replacement Tool map and explicit definition identity.
90
+ The factory preserves Phronomy authoring settings and independently copies
91
+ filter lists; configured callable instances remain shared. Custom SharedState
92
+ uses this public operation after merging member and application Tools. General
93
+ Agent subclass inheritance remains unchanged.
94
+ - Add `MultiAgent::SharedState.sharing(provider, id:, version:)` and the public
95
+ `SharedState::Binding` value. A synchronous provider opens one application-owned
96
+ store for an invocation and supplies Tool definitions for each member/cycle.
97
+ Applications define their own records, search arguments and cleanup without
98
+ overriding SharedState private methods. Custom sharing requires an explicit
99
+ aggregation block and does not inject the default findings Tools or append
100
+ stored records to member prompts.
101
+ - Include the sharing provider's stable ID and semantic version in custom
102
+ generated Agent definition identity. Default SharedState retains its existing
103
+ built-in Tools, prompts and instrumentation revision 1. The extension adds no
104
+ database adapter, ContextPolicy implementation or durable sharing lifecycle.
105
+ - Release terminal custom-sharing members through the public Agent lifecycle
106
+ operation. Normal Agent approval waits keep the invocation and provider open.
107
+ `SharedState::MemberLifecycleError` reports failed release or a custom
108
+ `invoke` override returning a suspended result prematurely, with the Agent and
109
+ available result/error details. It does not automatically answer approvals or
110
+ release a pending execution; application providers retain responsibility for
111
+ resource cleanup and recovery policy.
112
+
113
+ ### Changed
114
+
115
+ - Reuse the immutable argument schema prepared by an explicit `parameters`
116
+ declaration. Each Tool instance compiles its own validator on first use.
117
+
118
+ ### Fixed
119
+
120
+ - Build each human-facing Tool approval item from the Tool's public
121
+ `approval_display(arguments:, facts:)` result. A Tool without this operation
122
+ contributes empty argument and fact maps to new approval requests. Tool
123
+ execution and policy evaluation still receive their original inputs.
124
+ - Apply the default approval display's sensitive-value check to values inside
125
+ a Tool's redacted hash or array arguments, including copies used as fact keys.
126
+
127
+ ### Security
128
+
129
+ - Use patched ActiveRecord 8.0 development dependencies, including ActiveSupport
130
+ 8.0.5.2 or later in the 8.0 series. Remove unused ActiveJob and SQLite3
131
+ development dependencies; the examples retain the dependencies they use.
132
+ The published gem's runtime dependencies are unchanged.
133
+ - Provide Bundler Audit in the development bundle and check the complete
134
+ dependency set in pull-request CI and immediately before release publication.
135
+
136
+ ### Compatibility and verification
137
+
138
+ - The new SharedState sharing, Agent Tool composition and Context budgeting APIs
139
+ require 0.33.0 or later and remain Experimental. Default sharing and ordinary
140
+ Agent subclass inheritance retain their existing behavior. Custom sharing is
141
+ synchronous and invocation-scoped; it does not add durable sharing recovery.
142
+ - Approval display is selected by each Tool. Custom Tool implementations without
143
+ `approval_display` expose empty display maps; execution and policy evaluation
144
+ still use the original arguments. Review custom display overrides for safe
145
+ human-facing fields when upgrading.
146
+ - Observed LM Studio/gpt-oss PEG-format failures remain unresolved.
147
+ The existing adapter identifies known errors and bounds retries; this release
148
+ does not claim to fix their root cause or establish full real-LLM acceptance.
149
+ The latest full examples verification recorded 102 passed, 1 failed and no
150
+ skips (Example 22: HTTP 400). A separate Qwen Example 22 run completed all three
151
+ cycles, but took 2,220 seconds and does not replace the full verifier result
152
+ or satisfy its 240-second per-example limit.
153
+
15
154
  ## [0.32.0] - 2026-11-03
16
155
 
17
156
  ### Added
data/CONTRIBUTING.md CHANGED
@@ -25,11 +25,11 @@ Run the linter:
25
25
  bundle exec standardrb
26
26
  ```
27
27
 
28
- Check that no Japanese characters appear in source files:
29
-
30
- ```bash
31
- ruby scripts/check_japanese.rb
32
- ```
28
+ Check that source, comments, documentation and release notes are in English.
29
+ Use the maintainer's external language checker and record its command, checked
30
+ files and result with the review evidence. Check complete final files, including
31
+ new files; a diff-only check can miss retained non-English content. This
32
+ repository does not bundle a language-check script.
33
33
 
34
34
  ---
35
35
 
data/README.md CHANGED
@@ -37,13 +37,31 @@ tracking `main` directly.
37
37
 
38
38
  See [Features and Application Programming Interface (API) stability](docs/features.md) for the full feature matrix.
39
39
 
40
- The following table describes the application APIs available in v0.32.0.
40
+ The following table describes the application APIs available in v0.34.0.
41
+ The sharing, Tool composition and Context budgeting operations remain
42
+ Experimental; their introduction version is shown below.
41
43
  Worker hosting and completed parent/child release require v0.32.0 or later.
42
44
  Review the [0.31 migration notes](docs/migrations/0.31.md) before upgrading
43
45
  persistent applications or custom Agent Tool subclasses.
44
46
 
47
+ The v0.34.0 cleanup removes obsolete entry points. Use
48
+ `EventLoopReentrancyError` for EventLoop reentrancy failures and
49
+ `Runtime#shutdown`'s `cleanup_complete?` result predicate for resource release;
50
+ `clean?` additionally requires a graceful shutdown. See the
51
+ [cleanup migration](docs/migrations/cleanup-audit.md) before upgrading to v0.34.0.
52
+ The [legacy-format cleanup](docs/migrations/legacy-format-removal.md) separately
53
+ retires old data-conversion entry points and read fallbacks without changing
54
+ the current saved representation.
55
+
45
56
  | API | Stability | Contract |
46
57
  |---|---|---|
58
+ | `Persistence::Migration::InitialFormatMigration` | Removed (v0.34.0) | The pre-S3 conversion API is retired without a replacement. Current domain codecs and the separate Unit4 snapshot migration remain. [Legacy-format cleanup](docs/migrations/legacy-format-removal.md) |
59
+ | `Context::ContextPolicyInput#input_token_limit / #with_input_token_limit` and protected `ContextPolicy#estimate_tokens` | Experimental (since v0.33.0) | Inspect and constrain selection budgets and estimate candidate content through Context-owned operations. [Policy budgeting](docs/context-policy-budgeting.md) |
60
+ | `Agent::Base.with_tools(definitions, id:, version:)` | Experimental (since v0.33.0) | Derive an Agent class with a complete replacement Tool map and explicit definition identity while preserving Phronomy authoring settings and filters. [Tool composition](docs/shared-state-sharing.md#deriving-an-agent-with-a-complete-tool-set) |
61
+ | `MultiAgent::SharedState.sharing(provider, id:, version:)`, `SharedState::Binding` and `SharedState::MemberLifecycleError` | Experimental (since v0.33.0) | Connect application-owned shared data and Tools to sequential coordination through a synchronous invocation-scoped provider. [Custom sharing contract and example](docs/shared-state-sharing.md) |
62
+ | `Tool::Base#approval_display(arguments:, facts:)` | Beta (since v0.33.0) | The Tool selects the arguments and derived facts a person can see in an approval request. [Approval display](docs/runtime-and-concurrency.md#tool-approval-display) |
63
+ | `Runtime#shutdown` result predicates `cleanup_complete?` and `clean?` | Beta | Check resource release with `cleanup_complete?`; `clean?` additionally requires a graceful shutdown. The obsolete `success?` alias is removed in v0.34.0. [Cleanup migration](docs/migrations/cleanup-audit.md#runtime-cleanup-completion) |
64
+ | `Phronomy::EventLoopReentrancyError` | Beta | Raised when a synchronous operation would block the EventLoop control thread. The obsolete `SchedulerReentrancyError` subclass is removed in v0.34.0. [Cleanup migration](docs/migrations/cleanup-audit.md#eventloop-reentrancy-errors) |
47
65
  | `Phronomy::LLMAdapter::LMStudio` | Beta | Explicit known-output-error identification and bounded opt-in retry; [policy and evidence limits](docs/architecture/lm-studio-output-failures.md) |
48
66
  | Agent `output_schema:`, `schema_delivery:`, `Agent::OutputError` and `OutputParser::JsonSchemaParser` | Beta | Agent returns validated immutable JSON or an explicit output failure; [contract and migration](docs/architecture/structured-output-and-scope.md) |
49
67
  | Agent `transcript_messages` / `transcript_messages_async` | Beta | Read immutable conversation Message values with the same restoration rules as execution results; [reading conversation state](docs/getting-started.md) |
@@ -200,6 +218,7 @@ contracts, timeout/cancellation semantics, metrics, and callback rules.
200
218
  - [0.19 unified Persistence migration](docs/migrations/0.19.md).
201
219
  - [0.22 semantic Multi-Agent Handoff migration](docs/migrations/0.22.md).
202
220
  - [0.31 persistence and Agent lifetime migration](docs/migrations/0.31.md).
221
+ - [0.34 cleanup migration](docs/migrations/cleanup-audit.md) — replacement entry points and retained public operations.
203
222
  - [Architecture Decision Records](docs/decisions/) — design rationale and superseding decisions.
204
223
  - [CHANGELOG](CHANGELOG.md) — current development and recent release history.
205
224
  - [Changelog archive: 0.14.0 and earlier](docs/changelog/0.14-and-earlier.md).
data/RELEASE_CHECKLIST.md CHANGED
@@ -24,7 +24,9 @@ Copy it into the GitHub Release draft and check off each item.
24
24
 
25
25
  - [ ] `bundle exec rspec --format documentation` — 0 failures
26
26
  - [ ] `bundle exec rspec --tag integration` — 0 failures, all expected pending
27
- - [ ] `ruby scripts/check_japanese.rb` — exit 0 (no Japanese in source)
27
+ - [ ] External language check passes on the complete release files; source,
28
+ comments, documentation and release notes are in English. Record the checker
29
+ command, file scope and result; no language-check script is bundled here.
28
30
  - [ ] `bundle exec standardrb` — 0 offenses
29
31
  - [ ] `COVERAGE=1 bundle exec rspec` — coverage above configured threshold (Issue #207)
30
32
  - [ ] CI green on all Ruby matrix versions (3.2 / 3.3 / 3.4 / head)
@@ -38,6 +40,10 @@ Copy it into the GitHub Release draft and check off each item.
38
40
  - [ ] No new `requires_approval` tools missing the approval gate
39
41
  - [ ] No secrets, credentials, or PII in tool descriptions, schema strings, or spec fixtures
40
42
  - [ ] Dependency audit passes: `bundle exec bundler-audit check --update`
43
+ Run `bundle install` first; the development Gemfile provides the audit tool.
44
+ Audit the complete resolved bundle, including development and test dependencies,
45
+ without advisory exclusions. A runtime-only audit does not satisfy this gate.
46
+ The dependency-audit CI job and the release workflow enforce the same command.
41
47
 
42
48
  ---
43
49
 
data/SECURITY.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  | Version | Supported |
6
6
  |---------|-----------|
7
- | Latest release (main branch) | ✅ |
7
+ | 0.34.0 (latest release) | ✅ |
8
8
  | Older versions | ❌ — please upgrade |
9
9
 
10
10
  Only the latest released version of `phronomy` receives security patches. If you
data/VERIFY.sh CHANGED
@@ -30,12 +30,10 @@ required_files=(
30
30
  lib/phronomy/persistence/durable_record.rb
31
31
  lib/phronomy/persistence/durable_codec.rb
32
32
  lib/phronomy/persistence/repository_facades.rb
33
- lib/phronomy/persistence/migration/initial_format_migration.rb
34
33
  sig/phronomy/persistence.rbs
35
34
  spec/phronomy/persistence/backend_spi_api_spec.rb
36
35
  spec/phronomy/persistence/durable_codec_spec.rb
37
36
  spec/phronomy/persistence/in_memory_spec.rb
38
- spec/phronomy/persistence/initial_format_migration_spec.rb
39
37
  lib/phronomy/testing/persistence_contract/a_workflow_state_repository.rb
40
38
  spec/phronomy/workflow_identity_contract_spec.rb
41
39
  lib/phronomy/engine/invocation_context.rb
@@ -128,11 +126,9 @@ syntax_files=(
128
126
  lib/phronomy/persistence/durable_record.rb
129
127
  lib/phronomy/persistence/durable_codec.rb
130
128
  lib/phronomy/persistence/repository_facades.rb
131
- lib/phronomy/persistence/migration/initial_format_migration.rb
132
129
  spec/phronomy/persistence/backend_spi_api_spec.rb
133
130
  spec/phronomy/persistence/durable_codec_spec.rb
134
131
  spec/phronomy/persistence/in_memory_spec.rb
135
- spec/phronomy/persistence/initial_format_migration_spec.rb
136
132
  lib/phronomy/testing/persistence_contract/a_workflow_state_repository.rb
137
133
  spec/phronomy/workflow_identity_contract_spec.rb
138
134
  lib/phronomy/engine/invocation_context.rb
@@ -327,17 +323,14 @@ ruby -c lib/phronomy/persistence/durable_record.rb >/dev/null
327
323
  ruby -c lib/phronomy/persistence/durable_codec.rb >/dev/null
328
324
  ruby -c lib/phronomy/persistence/repository_facades.rb >/dev/null
329
325
  ruby -c lib/phronomy/persistence/in_memory.rb >/dev/null
330
- ruby -c lib/phronomy/persistence/migration/initial_format_migration.rb >/dev/null
331
326
  ruby -c spec/phronomy/persistence/backend_spi_api_spec.rb >/dev/null
332
327
  ruby -c spec/phronomy/persistence/durable_codec_spec.rb >/dev/null
333
328
  ruby -c spec/phronomy/persistence/in_memory_spec.rb >/dev/null
334
- ruby -c spec/phronomy/persistence/initial_format_migration_spec.rb >/dev/null
335
329
  bundle exec rbs -I sig validate
336
330
  bundle exec rspec \
337
331
  spec/phronomy/persistence/backend_spi_api_spec.rb \
338
332
  spec/phronomy/persistence/durable_codec_spec.rb \
339
333
  spec/phronomy/persistence/in_memory_spec.rb \
340
- spec/phronomy/persistence/initial_format_migration_spec.rb \
341
334
  spec/phronomy/persistence/backend_contract_spec.rb \
342
335
  spec/phronomy/agent/agent_execution_codec_spec.rb \
343
336
  spec/phronomy/agent/llm_input_manifest_spec.rb \
@@ -530,8 +523,7 @@ legacy_agent_invocation_identity="$(
530
523
  legacy_agent_invocation_identity="$(
531
524
  printf '%s\n' "$legacy_agent_invocation_identity" |
532
525
  grep -v 'lib/phronomy/agent/base.rb' |
533
- grep -v 'lib/phronomy/agent/agent_execution.rb' |
534
- grep -v 'lib/phronomy/persistence/migration/initial_format_migration.rb' || true
526
+ grep -v 'lib/phronomy/agent/agent_execution.rb' || true
535
527
  )"
536
528
  if [[ -n "$legacy_agent_invocation_identity" ]]; then
537
529
  echo "FAIL: active source/RBS reintroduced agent_invocation_id domain identity:" >&2
@@ -175,7 +175,12 @@ unchanged and acyclic; Ruby+RBS architecture validation passes.
175
175
 
176
176
  ## Static exception inventory
177
177
 
178
- Ripper inspected 339 Ruby files. The following 63 definitions include base, private and optional-integration types; this is not a count of public APIs. Ruby declarations establish ownership. Existing RBS definitions and new owning-domain signatures are checked together.
178
+ The original inventory used Ripper to inspect 339 Ruby files and listed 63
179
+ definitions, including base, private and optional-integration types. The table
180
+ below removes the superseded `SchedulerReentrancyError` entry as part of the
181
+ [cleanup migration](../migrations/cleanup-audit.md). These historical scan totals
182
+ are not a current count of source files or public APIs. Ruby declarations
183
+ establish ownership; RBS declarations are checked at their owning boundaries.
179
184
 
180
185
  | Class | Parent | Declaration |
181
186
  |---|---|---|
@@ -208,7 +213,6 @@ Ripper inspected 339 Ruby files. The following 63 definitions include base, priv
208
213
  | `Phronomy::RecursionLimitError` | `Phronomy::Error` | `lib/phronomy/execution/recursion_limit_error.rb` |
209
214
  | `Phronomy::RuntimeShutdownError` | `Phronomy::Error` | `lib/phronomy/execution/runtime_shutdown_error.rb` |
210
215
  | `Phronomy::RuntimeShutdownReentrancyError` | `Phronomy::RuntimeShutdownError` | `lib/phronomy/execution/runtime_shutdown_reentrancy_error.rb` |
211
- | `Phronomy::SchedulerReentrancyError` | `Phronomy::EventLoopReentrancyError` | `lib/phronomy/execution/scheduler_reentrancy_error.rb` |
212
216
  | `Phronomy::TimeoutError` | `Phronomy::Error` | `lib/phronomy/execution/timeout_error.rb` |
213
217
  | `Phronomy::FilterBlockError` | `Phronomy::Error` | `lib/phronomy/filter/filter_block_error.rb` |
214
218
  | `Phronomy::GeneratorVerifier::UnacceptedResultError` | `Phronomy::Error` | `lib/phronomy/generation/generator_verifier/unaccepted_result_error.rb` |
@@ -83,10 +83,11 @@ with lazy grammar` is a known native schema/Tool rejection. It is classified as
83
83
  stream operations. Unrelated string errors and requests without a schema retain
84
84
  their ordinary transport-error behavior. The adapter neither downgrades schemas
85
85
  nor replays requests. RubyLLM's configured transport retry policy is independent.
86
- The minimum Faraday streaming callback omits the HTTP status, so RubyLLM may
87
- surface this same exact rejection as `ServerError`; its cause is preserved too.
88
- That synthetic server status can trigger SDK retries before the adapter receives
89
- the error. Adapter replay tests disable SDK retries to verify their own boundary.
86
+ On the supported HTTP stack, the known schema rejection retains its
87
+ `RubyLLM::BadRequestError` cause even when streaming. An HTTP 500 with the same
88
+ body retains its `RubyLLM::ServerError` cause. The
89
+ [HTTP dependency rationale](trust-acceptance-followup.md#http-dependency-correction)
90
+ documents the tested versions and request counts.
90
91
 
91
92
  `Response` records finish_reason (stop, tool_calls, length, content_filter, refusal,
92
93
  unknown), refusal and raw_finish_reason metadata. Only normal final completion can
@@ -172,10 +173,9 @@ Normal low-quality output follows the configured evaluation loop as before.
172
173
 
173
174
  `Result#failure` is nil except for output_failed, where it carries the owned error
174
175
  diagnostic (without raw_output). Hash/JSON round trips, equality, hashing and freeze
175
- include it. `Result.from_h` requires every new field; migrate old saved Result JSON
176
- explicitly with `scripts/migrate_generator_verifier_result.rb OLD.json NEW.json`.
177
- The script adds failure=nil, validates the result and creates a new file. It does
178
- not overwrite the source or silently alter invalid data. `raise_if_unaccepted`
176
+ include it. `Result.from_h` requires every field, including `failure: nil` for
177
+ results without an output failure. It rejects incomplete serialized results.
178
+ `raise_if_unaccepted`
179
179
  retains the Result and the original OutputError cause in the same process.
180
180
 
181
181
  ## Symmetric lifetime rules
@@ -37,6 +37,21 @@ retain the SDK server-error cause. Configured SDK retries for 429/500 remain;
37
37
  a 400 rejection is one HTTP attempt even with three retries configured.
38
38
  Partial-stream replay and callback identity regressions remain in place.
39
39
 
40
+ RubyLLM owns HTTP transport selection for model calls. Phronomy's LLM and
41
+ Embeddings adapters recognize Faraday exception classes at the SDK boundary.
42
+ The two gemspec declarations are a temporary workaround for the streaming
43
+ status bug. Remove both when all supported RubyLLM versions preserve the
44
+ status or exclude the affected HTTP stack through their own dependencies.
45
+
46
+ Verify removal with the two Phronomy declarations omitted. Resolve the minimum
47
+ supported RubyLLM and its permitted HTTP dependencies without the existing CI
48
+ pins. If the old pair still resolves, test it explicitly. If RubyLLM excludes
49
+ it, confirm resolution rejects it and test the new minimum. In both cases run
50
+ `spec/integration/transport_error_spec.rb` against the minimum and the normal
51
+ bundle. With three retries configured, a streaming HTTP 400 with a string
52
+ error body must retain its 400 cause and send one POST. An HTTP 500 with the
53
+ same body must retain its 500 cause and send four POSTs.
54
+
40
55
  Before and after, application handling uses the same public API:
41
56
 
42
57
  ```ruby
@@ -0,0 +1,108 @@
1
+ # Context Policy budgeting
2
+
3
+ Applications choose how much model input capacity to use for one selection.
4
+ Context exposes that capacity and candidate estimates, while the framework keeps
5
+ its internal budget representation and final input validation.
6
+
7
+ For example, a review Policy can reserve part of the available input for its own
8
+ safety margin, then let DefaultPolicy choose history within the smaller limit.
9
+ No application needs to construct a private budget object for that operation.
10
+
11
+ ## Reading and constraining input limits
12
+
13
+ A Policy input reports its current selection limit and can derive another input
14
+ with a smaller limit. Derivation preserves the original input and all its
15
+ material; it does not change model capabilities or generation settings.
16
+
17
+ For example, an input with a limit of 8192 can produce a selection input capped
18
+ at 5632. The original input still reports 8192. The derived input cannot later
19
+ increase its limit above 5632.
20
+
21
+ ```ruby
22
+ require "phronomy"
23
+
24
+ class ReservedInputPolicy < Phronomy::Context::ContextPolicy
25
+ def call(input)
26
+ limit = input.input_token_limit
27
+ unless limit
28
+ raise Phronomy::ConfigurationError, "Review requires known model input capacity"
29
+ end
30
+
31
+ selection_limit = limit - limit / 4 - limit / 16
32
+ selected_input = input.with_input_token_limit(selection_limit)
33
+ Phronomy::Context::DefaultPolicy.instance.call(selected_input)
34
+ end
35
+ end
36
+ ```
37
+
38
+ `input_token_limit` returns an Integer or `nil` for an unknown limit.
39
+ `with_input_token_limit(limit)` requires a positive Integer without coercion;
40
+ zero, negative values, strings and floating-point values raise `ArgumentError`.
41
+ Increasing a known limit also raises `ArgumentError`. The returned
42
+ `ContextPolicyInput` is immutable, and its other field values are unchanged.
43
+
44
+ When the original limit is unknown, an explicit positive selection cap is
45
+ allowed. That cap expresses application policy and does not establish that the
46
+ model can accept that input. Applications that require known capability must
47
+ reject `nil`, as the example above does.
48
+
49
+ The cap controls selection performed with the derived input. The framework's
50
+ final validation still uses the actual model budget; returning a different Plan
51
+ does not make the derived cap a new global validator. It does not set an output
52
+ token limit, alter a saved Manifest, or affect another Agent call.
53
+
54
+ ## Estimating candidate content
55
+
56
+ A Policy can estimate proposed content through the same canonical encoding and
57
+ estimator used by its generated items. The Policy remains responsible for
58
+ choosing complete items and preserving required conversation groups.
59
+
60
+ For example, a retrieval Policy can fit a JSON page before adding it as
61
+ knowledge. JSON escaping contributes to the estimate because the complete
62
+ serialized page is measured.
63
+
64
+ ```ruby
65
+ require "phronomy"
66
+ require "json"
67
+
68
+ class PageKnowledgePolicy < Phronomy::Context::ContextPolicy
69
+ def initialize(page:, allowance:)
70
+ @page = page
71
+ @allowance = allowance
72
+ end
73
+
74
+ def call(input)
75
+ content = JSON.generate(@page)
76
+ if estimate_tokens(content) > @allowance
77
+ raise Phronomy::Context::BudgetExceededError, "Retrieved page exceeds its allowance"
78
+ end
79
+
80
+ page = knowledge_item(content: content, required: true)
81
+ Phronomy::Context::DefaultPolicy.instance.call(
82
+ input.with(knowledge: input.knowledge + [page])
83
+ )
84
+ end
85
+ end
86
+ ```
87
+
88
+ `estimate_tokens(content)` is a protected subclass helper. Strings are estimated
89
+ as text; other values use the framework's canonical JSON encoding. The helper
90
+ uses the existing estimator and does not introduce a second token-counting
91
+ algorithm. Estimates are approximate unless the configured estimator provides
92
+ model-specific counts. A successful estimate does not guarantee provider
93
+ acceptance or replace final input validation.
94
+
95
+ ## Compatibility and ownership
96
+
97
+ These operations belong to the Context contract and reuse its existing budget
98
+ and estimation mechanisms. Applications own allocation ratios and retrieval
99
+ policy; DefaultPolicy retains its current selection algorithm.
100
+
101
+ For example, Example 22 can budget retrieved findings through these operations
102
+ while SharedState continues to coordinate Agents and application Tools continue
103
+ to own the finding store. No SharedState-to-Context dependency is introduced.
104
+
105
+ The operations are Experimental and available since v0.33.0. Existing private
106
+ budget and estimation classes remain implementation details. Existing ContextPolicy
107
+ factories and DefaultPolicy behavior are unchanged, and the protected helper
108
+ applies equally to normal Agent calls and SharedState member calls.
@@ -61,11 +61,12 @@ Tool-role messages are one indivisible conversation group. When the selected
61
61
  Plan is realized, the Manifest records the Framework conversation-group identity
62
62
  and classifies canonical Tool exchanges as `tool_exchanges` for Handoff.
63
63
 
64
- `HandoffProjection` groups current-format Manifest segments by that conversation
65
- group identity. It may still read legacy `selection_unit_id` metadata from a
66
- finalized pre-ACS-04 Manifest, but new Context assembly does not recreate
67
- `Selection::Unit`. `Selection::Candidate` and `Selection::Constraint` remain
68
- internal Context-input normalization details only.
64
+ The current `Agent::TransferProjection` groups Manifest segments by that
65
+ conversation-group identity. Segments without a group identity are projected
66
+ independently. Historical selection-unit identifiers and kinds are no longer
67
+ interpreted, and Context assembly does not recreate `Selection::Unit`.
68
+ `Selection::Candidate` and `Selection::Constraint` remain internal Context-input
69
+ normalization details only.
69
70
 
70
71
  Handoff Policy answers what may cross the Agent boundary. Target Context Policy
71
72
  remains the final authority for what enters each Target LLM call. Transferred
@@ -0,0 +1,145 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+ require "phronomy"
5
+
6
+ module SharedStateCustomSharingExample
7
+ class IssueBoard
8
+ attr_reader :closed
9
+
10
+ def initialize
11
+ @records = []
12
+ @closed = false
13
+ end
14
+
15
+ def record(content:, category:, status:, author:, cycle:)
16
+ raise "Issue board is closed" if closed
17
+
18
+ row = {id: @records.length + 1, content: content.dup.freeze,
19
+ category: category.dup.freeze, status: status.dup.freeze,
20
+ author: author.dup.freeze, cycle: cycle}.freeze
21
+ @records << row
22
+ row
23
+ end
24
+
25
+ def search(category: nil, status: nil)
26
+ snapshot.select do |row|
27
+ (category.nil? || row.fetch(:category) == category) &&
28
+ (status.nil? || row.fetch(:status) == status)
29
+ end
30
+ end
31
+
32
+ def snapshot
33
+ raise "Issue board is closed" if closed
34
+
35
+ @records.dup.freeze
36
+ end
37
+
38
+ def close
39
+ @records.clear
40
+ @closed = true
41
+ end
42
+ end
43
+
44
+ class IssueSharing
45
+ def open(input:, config:)
46
+ board = IssueBoard.new
47
+ yield Phronomy::MultiAgent::SharedState::Binding.new(
48
+ store: board,
49
+ tools: ->(member:, cycle:) { tool_definitions(board, member, cycle) }
50
+ )
51
+ ensure
52
+ board&.close
53
+ end
54
+
55
+ private
56
+
57
+ def tool_definitions(board, member, cycle)
58
+ {
59
+ record_tool(board, member.agent_definition.fetch(:id), cycle) => nil,
60
+ search_tool(board) => nil
61
+ }
62
+ end
63
+
64
+ def record_tool(board, author, cycle)
65
+ Class.new(Phronomy::Tool::Base) do
66
+ tool_name "record_issue"
67
+ description "Record one issue with an explicit category and status."
68
+ execution_mode :cooperative
69
+ param :content, type: :string, desc: "The issue description."
70
+ param :category, type: :string, enum: %w[security performance],
71
+ desc: "The issue category."
72
+ param :status, type: :string, enum: %w[open closed],
73
+ desc: "The issue status."
74
+
75
+ define_method(:execute) do |content:, category:, status:|
76
+ board.record(content: content, category: category, status: status,
77
+ author: author, cycle: cycle).to_json
78
+ end
79
+ end
80
+ end
81
+
82
+ def search_tool(board)
83
+ Class.new(Phronomy::Tool::Base) do
84
+ tool_name "search_issues"
85
+ description "Read issues matching all supplied category and status filters."
86
+ execution_mode :cooperative
87
+ param :category, type: :string, required: false, enum: %w[security performance],
88
+ desc: "Optional exact category filter."
89
+ param :status, type: :string, required: false, enum: %w[open closed],
90
+ desc: "Optional exact status filter."
91
+
92
+ define_method(:execute) do |category: nil, status: nil|
93
+ board.search(category: category, status: status).to_json
94
+ end
95
+ end
96
+ end
97
+ end
98
+
99
+ class Researcher < Phronomy::Agent::Base
100
+ agent_definition id: "shared-issue-researcher", version: 1
101
+ model ENV.fetch("PHRONOMY_EXAMPLE_MODEL", "configured-model")
102
+ end
103
+
104
+ class ReviewTeam < Phronomy::MultiAgent::SharedState
105
+ sharing IssueSharing.new, id: "shared-issues", version: 1
106
+ member Researcher
107
+ max_cycles 2
108
+ coordination "Use search_issues to inspect relevant issues. " \
109
+ "Use record_issue to record each new issue with its category and status."
110
+ aggregate { |board| board.snapshot }
111
+ end
112
+
113
+ module OfflineDemo
114
+ def self.run
115
+ opened_board = nil
116
+ output = IssueSharing.new.open(input: "Review authentication", config: {}) do |binding|
117
+ opened_board = binding.store
118
+ definitions = binding.tool_definitions(member: Researcher, cycle: 1)
119
+ tools = definitions.keys.to_h { |tool| [tool.tool_name, tool.new] }
120
+ recorder = tools.fetch("record_issue")
121
+ recorder.call({"content" => "Missing token validation", "category" => "security", "status" => "open"})
122
+ recorder.call({"content" => "Slow cache lookup", "category" => "performance", "status" => "open"})
123
+ recorder.call({"content" => "Session expiry corrected", "category" => "security", "status" => "closed"})
124
+
125
+ matches = JSON.parse(tools.fetch("search_issues").call({"category" => "security", "status" => "open"}))
126
+ unless matches.map { |row| row.fetch("content") } == ["Missing token validation"]
127
+ raise "Search did not apply both filters"
128
+ end
129
+ all_records = binding.store.snapshot
130
+ raise "Search changed the board" unless all_records.length == 3
131
+
132
+ {matches: matches, total_records: all_records.length}
133
+ end
134
+ raise "Provider did not close the board" unless opened_board.closed
135
+
136
+ puts JSON.pretty_generate(output.merge(provider_closed: true))
137
+ end
138
+ end
139
+ end
140
+
141
+ if $PROGRAM_NAME == __FILE__
142
+ abort "Run with --offline, or require this file from a configured Phronomy application." unless ARGV == ["--offline"]
143
+
144
+ SharedStateCustomSharingExample::OfflineDemo.run
145
+ end