julewire-core 1.0.1 → 1.1.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 +18 -0
- data/docs/configuration.md +14 -7
- data/docs/context-and-propagation.md +20 -0
- data/docs/contracts.md +23 -49
- data/docs/extensions-and-api.md +78 -82
- data/docs/health-schema.md +12 -2
- data/docs/internals.md +21 -20
- data/docs/outputs-and-lifecycle.md +10 -6
- data/docs/records-and-data-policy.md +36 -5
- data/lib/julewire/core/cli/doctor.rb +20 -16
- data/lib/julewire/core/cli/line_helpers.rb +23 -12
- data/lib/julewire/core/cli/log_formats/core_json_decoder.rb +3 -3
- data/lib/julewire/core/cli/log_formats.rb +9 -9
- data/lib/julewire/core/cli/tail.rb +19 -14
- data/lib/julewire/core/cli/transcode.rb +4 -5
- data/lib/julewire/core/cli.rb +1 -1
- data/lib/julewire/core/configuration.rb +1 -1
- data/lib/julewire/core/context_store.rb +51 -87
- data/lib/julewire/core/destinations/chaos_output.rb +2 -2
- data/lib/julewire/core/destinations/collection.rb +8 -16
- data/lib/julewire/core/destinations/definition.rb +1 -9
- data/lib/julewire/core/destinations/destination.rb +13 -63
- data/lib/julewire/core/destinations/processor_handling.rb +78 -0
- data/lib/julewire/core/destinations/registry.rb +1 -1
- data/lib/julewire/core/destinations/sink.rb +0 -1
- data/lib/julewire/core/destinations/synchronized_output.rb +14 -13
- data/lib/julewire/core/destinations/tail_sampling.rb +18 -27
- data/lib/julewire/core/destinations/write_step.rb +5 -5
- data/lib/julewire/core/destinations.rb +0 -7
- data/lib/julewire/core/diagnostics/callback_notifier.rb +4 -2
- data/lib/julewire/core/diagnostics/doctor.rb +16 -9
- data/lib/julewire/core/diagnostics/failure_snapshot.rb +6 -2
- data/lib/julewire/core/diagnostics/health.rb +41 -57
- data/lib/julewire/core/diagnostics/integration_health_store.rb +11 -24
- data/lib/julewire/core/diagnostics/internal_records.rb +8 -12
- data/lib/julewire/core/diagnostics/invalid_severity_reporter.rb +16 -55
- data/lib/julewire/core/diagnostics/meta_observer.rb +37 -44
- data/lib/julewire/core/diagnostics/process_integration_health.rb +0 -5
- data/lib/julewire/core/diagnostics/tail/renderer.rb +1 -1
- data/lib/julewire/core/diagnostics/tail.rb +18 -22
- data/lib/julewire/core/execution/boundary.rb +2 -8
- data/lib/julewire/core/execution/handle.rb +12 -17
- data/lib/julewire/core/execution/lineage.rb +16 -56
- data/lib/julewire/core/execution/measurement_handle.rb +6 -10
- data/lib/julewire/core/execution/scope.rb +15 -20
- data/lib/julewire/core/execution/scope_fields.rb +3 -12
- data/lib/julewire/core/execution/scope_identity.rb +1 -9
- data/lib/julewire/core/execution/scope_snapshot.rb +18 -39
- data/lib/julewire/core/execution/summary_state.rb +16 -19
- data/lib/julewire/core/execution/view.rb +8 -17
- data/lib/julewire/core/facade_methods.rb +67 -65
- data/lib/julewire/core/fields/attribute_keys.rb +0 -2
- data/lib/julewire/core/fields/bags.rb +23 -27
- data/lib/julewire/core/fields/field_set.rb +29 -19
- data/lib/julewire/core/fields/field_stack.rb +68 -134
- data/lib/julewire/core/fields/internal/deletion.rb +6 -9
- data/lib/julewire/core/fields/internal.rb +19 -38
- data/lib/julewire/core/fields/lookup.rb +9 -13
- data/lib/julewire/core/fields/section_proxy.rb +1 -9
- data/lib/julewire/core/fields/stack_set.rb +7 -12
- data/lib/julewire/core/fields/static_labels.rb +1 -1
- data/lib/julewire/core/integration/configurable.rb +4 -3
- data/lib/julewire/core/integration/destination_health.rb +17 -7
- data/lib/julewire/core/integration/event_subscriber.rb +1 -1
- data/lib/julewire/core/integration/facade.rb +12 -15
- data/lib/julewire/core/integration/fork_hooks.rb +12 -36
- data/lib/julewire/core/integration/health.rb +0 -3
- data/lib/julewire/core/integration/ivar_state.rb +0 -5
- data/lib/julewire/core/integration/protocol.rb +20 -0
- data/lib/julewire/core/integration/settings.rb +4 -4
- data/lib/julewire/core/integration/subscriber_install.rb +2 -2
- data/lib/julewire/core/integration/subscription.rb +3 -2
- data/lib/julewire/core/integration/values.rb +0 -17
- data/lib/julewire/core/local_storage.rb +3 -19
- data/lib/julewire/core/processing/invalid_result_failure.rb +23 -0
- data/lib/julewire/core/processing/level_threshold.rb +7 -14
- data/lib/julewire/core/processing/match.rb +6 -4
- data/lib/julewire/core/processing/pipeline.rb +46 -40
- data/lib/julewire/core/processing/processor_chain.rb +22 -11
- data/lib/julewire/core/processing/processor_registry.rb +3 -4
- data/lib/julewire/core/processing/record_field_transform.rb +0 -4
- data/lib/julewire/core/processing/sampling.rb +1 -3
- data/lib/julewire/core/processing.rb +2 -2
- data/lib/julewire/core/propagation/carrier.rb +14 -14
- data/lib/julewire/core/propagation.rb +15 -2
- data/lib/julewire/core/records/build_input.rb +75 -0
- data/lib/julewire/core/records/console_formatter.rb +6 -6
- data/lib/julewire/core/records/deconstruct.rb +1 -1
- data/lib/julewire/core/records/display_message.rb +2 -4
- data/lib/julewire/core/records/draft.rb +125 -262
- data/lib/julewire/core/records/lazy_emit_input.rb +2 -12
- data/lib/julewire/core/records/metadata.rb +4 -3
- data/lib/julewire/core/records/public_projection.rb +2 -2
- data/lib/julewire/core/records/raw_input.rb +1 -1
- data/lib/julewire/core/records/record.rb +19 -37
- data/lib/julewire/core/records/severity.rb +7 -15
- data/lib/julewire/core/runtime.rb +67 -71
- data/lib/julewire/core/runtime_registry.rb +20 -15
- data/lib/julewire/core/runtime_state.rb +5 -3
- data/lib/julewire/core/scheduling/shared_scheduler.rb +28 -22
- data/lib/julewire/core/serialization/backtrace_limiter.rb +13 -9
- data/lib/julewire/core/serialization/bounded_transform.rb +2 -12
- data/lib/julewire/core/serialization/bounded_traversal.rb +49 -66
- data/lib/julewire/core/serialization/deep_compact_empty.rb +9 -11
- data/lib/julewire/core/serialization/deep_freeze.rb +45 -6
- data/lib/julewire/core/serialization/encoding_sanitizer.rb +2 -3
- data/lib/julewire/core/serialization/exception_shape.rb +7 -6
- data/lib/julewire/core/serialization/json_encoder.rb +10 -13
- data/lib/julewire/core/serialization/serializer.rb +23 -50
- data/lib/julewire/core/serialization/serializer_pool.rb +9 -0
- data/lib/julewire/core/serialization/text_encoder.rb +5 -5
- data/lib/julewire/core/serialization/truncation_metadata.rb +63 -48
- data/lib/julewire/core/serialization/value_copy.rb +60 -227
- data/lib/julewire/core/serialization/value_traversal.rb +10 -127
- data/lib/julewire/core/testing.rb +6 -56
- data/lib/julewire/core/validation.rb +2 -3
- data/lib/julewire/core/version.rb +1 -1
- data/lib/julewire/core.rb +2 -7
- metadata +6 -19
- data/lib/julewire/core/scheduling/deadline_scheduler.rb +0 -207
- data/lib/julewire/core/testing/chaos/catalog.rb +0 -72
- data/lib/julewire/core/testing/chaos/core_runtime.rb +0 -120
- data/lib/julewire/core/testing/chaos/destination.rb +0 -55
- data/lib/julewire/core/testing/chaos/emitter.rb +0 -20
- data/lib/julewire/core/testing/chaos/raising_output.rb +0 -42
- data/lib/julewire/core/testing/chaos.rb +0 -80
- data/lib/julewire/core/testing/contracts/component.rb +0 -162
- data/lib/julewire/core/testing/contracts/deadline_scheduler.rb +0 -59
- data/lib/julewire/core/testing/contracts/integration.rb +0 -166
- data/lib/julewire/core/testing/contracts/integration_fields.rb +0 -36
- data/lib/julewire/core/testing/contracts/record_draft.rb +0 -37
- data/lib/julewire/core/testing/contracts/runtime.rb +0 -178
- data/lib/julewire/core/testing/contracts/wire.rb +0 -60
- data/lib/julewire/core/testing/contracts.rb +0 -24
- data/lib/julewire/core/testing/coverage.rb +0 -58
- data/lib/julewire/core/testing/test_reports.rb +0 -78
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 8dcf4adb34508c1956a296d585981b88af27d8be7d0e63e9a3fe130b2c835f3b
|
|
4
|
+
data.tar.gz: 66cf51de3387a96f280c23badc8d847b07c53f069bd1aa5bcc7d15f72bfb4cff
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e5f73d78908ae3a654c69f91eb05aa97d804e5ad9d087bb709044b7b8f0273c0babc5b4094096d9136378e7cc43a2ad850766cb8307e7883747408551be87424
|
|
7
|
+
data.tar.gz: eef32910880411f8760142726eb4453b938f073124688ff992bbf3d59a0c6f9a3f3d70a95a020946015d970e7694ba551785436d9755b0faf215bf599769b47e
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
## Unreleased
|
|
2
2
|
|
|
3
|
+
## 1.1.0 - 2026-07-20
|
|
4
|
+
|
|
5
|
+
- Make carrier extraction status public via `Carrier::Extracted` and rename the
|
|
6
|
+
convenience reader to `extract_envelope`.
|
|
7
|
+
- Enforce recursive Symbol keys for owned records and protocols; reject invalid
|
|
8
|
+
processor results and malformed propagation sections instead of normalizing
|
|
9
|
+
or ignoring them.
|
|
10
|
+
- Require canonical lowercase severity Symbols while retaining case-insensitive
|
|
11
|
+
String and standard-library Logger integer ingress.
|
|
12
|
+
- Harden bounded serialization, `RecordDraft` isolation/finalization, and
|
|
13
|
+
processor, summary, and lifecycle failure health; remove reusable bounded
|
|
14
|
+
transforms and the undocumented `freeze_sections` mode.
|
|
15
|
+
- Remove `Julewire::Testing::Contracts` and `Julewire::Testing::Chaos`; the
|
|
16
|
+
testing API now contains only capture and null-output observation fixtures.
|
|
17
|
+
- Remove the generic deadline-scheduler SPI; main-process integrations now use
|
|
18
|
+
only the process-owned shared scheduler, leaving Ractor-safe timeout ownership
|
|
19
|
+
to the Ractor integration.
|
|
20
|
+
|
|
3
21
|
## 1.0.1 - 2026-06-25
|
|
4
22
|
|
|
5
23
|
- Harden bounded traversal, ingress copying, and carrier extraction against
|
data/docs/configuration.md
CHANGED
|
@@ -38,6 +38,10 @@ end
|
|
|
38
38
|
| `on_failure` | Best-effort callback for contained core failures. |
|
|
39
39
|
| `on_drop` | Best-effort callback for operational drops after a record reaches destination/output handling. |
|
|
40
40
|
|
|
41
|
+
Severity Symbols must use canonical lowercase values. String configuration
|
|
42
|
+
values are case-insensitive, and standard-library Logger severity integers are
|
|
43
|
+
accepted.
|
|
44
|
+
|
|
41
45
|
Timeout values must be `nil` or non-negative finite numerics.
|
|
42
46
|
`error_backtrace_lines` must be a non-negative integer.
|
|
43
47
|
|
|
@@ -117,10 +121,11 @@ processors. Output-specific shape belongs in destination formatters. Processors
|
|
|
117
121
|
are the mutation stage.
|
|
118
122
|
|
|
119
123
|
Processors receive the current `Julewire::RecordDraft`. Mutate the draft
|
|
120
|
-
directly. Return `:drop` to stop delivery or a
|
|
121
|
-
replace the current draft
|
|
122
|
-
|
|
123
|
-
|
|
124
|
+
directly and return `nil` to keep it. Return `:drop` to stop delivery or a
|
|
125
|
+
different `Julewire::RecordDraft` to replace the current draft. Unsupported
|
|
126
|
+
non-`nil` return values keep the current draft but increment `processor_invalid`
|
|
127
|
+
and record last-failure metadata. Processors own the draft until the final
|
|
128
|
+
record boundary, so direct mutation is the normal hot path:
|
|
124
129
|
|
|
125
130
|
```ruby
|
|
126
131
|
class AddTenantLabel
|
|
@@ -155,11 +160,13 @@ Processor exceptions use the registration policy:
|
|
|
155
160
|
| `on_error` | Behavior |
|
|
156
161
|
| --- | --- |
|
|
157
162
|
| `:fail_closed` | Default. Emit a `julewire.processor_error` replacement record and stop the chain. |
|
|
158
|
-
| `:fail_open` | Record the failure
|
|
163
|
+
| `:fail_open` | Record the failure and continue only when the current draft still satisfies the owned record contract. |
|
|
159
164
|
| `:drop` | Record the failure and suppress the record. |
|
|
160
165
|
|
|
161
|
-
Public emit input remains defensive.
|
|
162
|
-
|
|
166
|
+
Public emit input remains defensive. Core validates the draft after every
|
|
167
|
+
processor, so a malformed mutation is attributed and dropped before a later
|
|
168
|
+
processor or destination sees it. The final immutable record boundary still
|
|
169
|
+
validates the Julewire record shape.
|
|
163
170
|
|
|
164
171
|
Destination-local processors run after the global processor chain and before
|
|
165
172
|
one destination formats the record. Use them for sink-specific policy such as
|
|
@@ -225,9 +225,22 @@ Execution metadata is restored as ordinary execution fields. Use
|
|
|
225
225
|
`Julewire.with_execution(..., fields: { trace_id: "..." })` for custom
|
|
226
226
|
execution fields at the public API boundary.
|
|
227
227
|
|
|
228
|
+
Direct `Propagation.restore` treats its envelope as public/unowned input:
|
|
229
|
+
String keys are normalized, but `_julewire_truncation` remains reserved and is
|
|
230
|
+
rejected. `Carrier.restore` is the owned decoded-wire path; it preserves
|
|
231
|
+
Julewire-generated truncation metadata while restoring the carrier envelope.
|
|
232
|
+
Bridge code may also call `Propagation.restore(..., owned: true)` for a local
|
|
233
|
+
Julewire-owned envelope. That strict path requires a recursively Symbol-keyed
|
|
234
|
+
Hash and requires every present propagation section to be a Hash; it does not
|
|
235
|
+
replace malformed owned sections with empty hashes.
|
|
236
|
+
|
|
228
237
|
Propagation serializes values through the core serializer. That means values
|
|
229
238
|
cross a log-safe boundary, not an object-identity boundary.
|
|
230
239
|
|
|
240
|
+
Carrier JSON decode is a key-normalization boundary: decoded String keys become
|
|
241
|
+
Symbols before restore. Once restored, context and carry are owned Symbol-key
|
|
242
|
+
data; integrations must not add String-key overlays.
|
|
243
|
+
|
|
231
244
|
Core does not redact propagation envelopes. Do not put secrets in context or
|
|
232
245
|
carry unless your app or processor policy handles them.
|
|
233
246
|
|
|
@@ -278,6 +291,11 @@ headers = Julewire::Core::Propagation::Carrier.inject({}, max_bytes: 8 * 1024)
|
|
|
278
291
|
# => nil when the serialized carrier is too large; the carrier key is removed
|
|
279
292
|
```
|
|
280
293
|
|
|
294
|
+
Integrations that need failure status should call `Carrier.extract_result` and
|
|
295
|
+
inspect its public `Carrier::Extracted` fields: `status`, `reason`, `error`,
|
|
296
|
+
and `envelope`. Use `Carrier.extract_envelope` for convenience-only restore
|
|
297
|
+
paths.
|
|
298
|
+
|
|
281
299
|
The default carrier key is `"julewire"`. Carriers are intentionally
|
|
282
300
|
provider-neutral: core stores only the Julewire propagation envelope. External
|
|
283
301
|
header conventions stay outside core.
|
|
@@ -343,6 +361,8 @@ counters.
|
|
|
343
361
|
- current fiber context store
|
|
344
362
|
- the old active pipeline/output lifecycle
|
|
345
363
|
- live runtime post-close drop and callback-failure state
|
|
364
|
+
- registered named runtimes and their pipelines when called through the main
|
|
365
|
+
`Julewire` facade; a later named lookup creates a fresh runtime
|
|
346
366
|
|
|
347
367
|
`health[:counts]` is different: it is monotonic for the current runtime object
|
|
348
368
|
and survives `reset!`. Use it for process-lifetime runtime lifecycle scrapes.
|
data/docs/contracts.md
CHANGED
|
@@ -60,53 +60,18 @@ general application surface:
|
|
|
60
60
|
- `Julewire::TailSampling` as a destination wrapper for execution-level tail
|
|
61
61
|
sampling.
|
|
62
62
|
- `Julewire::Testing::CaptureDestination`, `Julewire::Testing::NullOutput`,
|
|
63
|
-
`Julewire::Testing.capture`,
|
|
64
|
-
`Julewire::Testing
|
|
65
|
-
|
|
63
|
+
`Julewire::Testing.capture`, and
|
|
64
|
+
`Julewire::Testing.configure_capture_destination` as small extension test
|
|
65
|
+
fixtures.
|
|
66
66
|
- `RuntimeLocator.current.emit_without_level` for host-process integrations
|
|
67
67
|
that already apply their framework's level gate.
|
|
68
68
|
|
|
69
69
|
Extensions should consume these contracts rather than reaching into pipeline,
|
|
70
70
|
runtime, storage, or destination internals.
|
|
71
71
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
`Julewire::Testing::Contracts` currently ships these shared assertions for
|
|
76
|
-
extension and integration gems:
|
|
77
|
-
|
|
78
|
-
- `assert_julewire_bounded_transform_spi_contract`
|
|
79
|
-
- `assert_julewire_deadline_scheduler_spi_contract`
|
|
80
|
-
- `assert_julewire_destination_contract`
|
|
81
|
-
- `assert_julewire_execution_boundary_contract`
|
|
82
|
-
- `assert_julewire_failure_containment_contract`
|
|
83
|
-
- `assert_julewire_formatter_contract`
|
|
84
|
-
- `assert_julewire_integration_failure_contract`
|
|
85
|
-
- `assert_julewire_integration_health_contract`
|
|
86
|
-
- `assert_julewire_integration_ivar_state_contract`
|
|
87
|
-
- `assert_julewire_integration_payload_contract`
|
|
88
|
-
- `assert_julewire_integration_spi_contract`
|
|
89
|
-
- `assert_julewire_integration_timestamp_contract`
|
|
90
|
-
- `assert_julewire_integration_value_contract`
|
|
91
|
-
- `assert_julewire_processor_contract`
|
|
92
|
-
- `assert_julewire_propagation_contract`
|
|
93
|
-
- `assert_julewire_record_draft_transform_contract`
|
|
94
|
-
- `assert_julewire_record_shape_contract`
|
|
95
|
-
- `assert_julewire_record_source_contract`
|
|
96
|
-
- `assert_julewire_runtime_integration_contract`
|
|
97
|
-
- `assert_julewire_truncation_marker_spi_contract`
|
|
98
|
-
- `assert_julewire_validation_spi_contract`
|
|
99
|
-
|
|
100
|
-
`Julewire::Testing::Chaos` currently ships containment helpers for extension
|
|
101
|
-
test suites:
|
|
102
|
-
|
|
103
|
-
- `assert_contained`
|
|
104
|
-
- `assert_core_runtime_containment`
|
|
105
|
-
- `assert_destination_chaos_contract`
|
|
106
|
-
- `assert_discovered_chaos_contracts`
|
|
107
|
-
- `assert_emitter_chaos_contract`
|
|
108
|
-
- `catalog`
|
|
109
|
-
- `raiser`
|
|
72
|
+
Core does not ship an assertion DSL or exception-corpus runner. Integration
|
|
73
|
+
tests exercise their production formatter, destination, processor, subscriber,
|
|
74
|
+
and framework boundary directly.
|
|
110
75
|
|
|
111
76
|
## Integration SPI
|
|
112
77
|
|
|
@@ -118,6 +83,8 @@ but they are not intended as general application API:
|
|
|
118
83
|
integration health and scoped health wrappers.
|
|
119
84
|
- `Julewire::Core::Integration::Facade` for integration-owned emits,
|
|
120
85
|
execution boundaries, field overlays, and summary enrichment.
|
|
86
|
+
- `Julewire::Core::Integration::Protocol` for recursive, non-normalizing
|
|
87
|
+
validation of Symbol-keyed integration protocol data.
|
|
121
88
|
- `Julewire::Core::Integration::Values::Read` for hash/object value reads
|
|
122
89
|
including `value`, `hash_value`, `nested_value`, `path_value`,
|
|
123
90
|
`first_value`, and `blank?`.
|
|
@@ -133,7 +100,10 @@ but they are not intended as general application API:
|
|
|
133
100
|
snapshots without exposing core's internal health cells.
|
|
134
101
|
- `Julewire::Core::Destinations::WriteStep` for destination-style integrations
|
|
135
102
|
that reuse core's format, encode, bound, write, and counter sequence while
|
|
136
|
-
keeping their own lifecycle and failure policy.
|
|
103
|
+
keeping their own lifecycle and failure policy. It returns `true` only when
|
|
104
|
+
the output accepted the encoded record; handled drops and transform failures
|
|
105
|
+
return `false` after recording health/loss metadata through the supplied
|
|
106
|
+
callbacks.
|
|
137
107
|
- `Julewire::Core::CLI::LogFormats` for provider-owned CLI file-tail decoding
|
|
138
108
|
and transcoding formats.
|
|
139
109
|
- `Julewire::Core::Processing.register` for integration-owned processor kinds,
|
|
@@ -148,14 +118,13 @@ but they are not intended as general application API:
|
|
|
148
118
|
- `Julewire::Core.sentinel(:name)` for readable, frozen identity markers when
|
|
149
119
|
an integration needs a private empty or missing-value sentinel.
|
|
150
120
|
- `Julewire::Core.deep_compact_empty` for core-compatible empty-field pruning.
|
|
151
|
-
- `Julewire::Core::Scheduling::DeadlineScheduler` for integration-local timeout callbacks
|
|
152
|
-
that need Julewire's fork-reset behavior.
|
|
153
121
|
- `Julewire::Core::Scheduling::SharedScheduler` for process-wide main-ractor
|
|
154
122
|
timeouts shared by integrations that should not own separate scheduler
|
|
155
123
|
threads.
|
|
156
124
|
- `Julewire::Core::Validation` for shared option and limit validation.
|
|
157
|
-
- `Julewire::Core::Serialization::BoundedTransform` for processors and
|
|
158
|
-
core-compatible bounded traversal before core
|
|
125
|
+
- `Julewire::Core::Serialization::BoundedTransform.call` for processors and
|
|
126
|
+
integrations that need core-compatible bounded traversal before core
|
|
127
|
+
serializes the result.
|
|
159
128
|
- `Julewire::Core::Diagnostics::CallbackNotifier` and
|
|
160
129
|
`Julewire::Core::Diagnostics::FailureSnapshot` for destination-style
|
|
161
130
|
integrations that need core-compatible callback and health failure shape.
|
|
@@ -181,6 +150,8 @@ different Ruby isolation boundary:
|
|
|
181
150
|
- `Julewire::Core::UNSET`, `Julewire::Core.emit_input`, and
|
|
182
151
|
`Julewire::Core::Records::LazyEmitInput` to mirror facade emit semantics
|
|
183
152
|
across an isolation boundary.
|
|
153
|
+
- `Julewire::Core::Records::BuildInput` to canonicalize public emit fields or
|
|
154
|
+
validate integration-owned record input before strict bridge serialization.
|
|
184
155
|
- `Julewire::Core::Execution::Boundary` for shared execution boundary behavior.
|
|
185
156
|
- `Julewire::Core::ContextStore.current`,
|
|
186
157
|
`Julewire::Core::Fields::ContextProxy`,
|
|
@@ -188,14 +159,17 @@ different Ruby isolation boundary:
|
|
|
188
159
|
`Julewire::Core::Fields::CarryProxy`, and
|
|
189
160
|
`Julewire::Core::Fields::SummaryProxy` for bridge-local field bags.
|
|
190
161
|
- `Julewire::Core::Execution::ScopeSnapshot` for detached execution scope transfer.
|
|
162
|
+
- `emit_integration(record, enforce_level:)` on bridge runtimes for strict
|
|
163
|
+
integration-owned emits inside the isolation boundary.
|
|
191
164
|
- Parent-runtime hooks: `emit_envelope`, `emit_summary_record`, and `flush`.
|
|
192
165
|
`emit_envelope` accepts detached input, context, carry, attributes, neutral,
|
|
193
166
|
scope snapshot, `enforce_level:`, and `owned:`. Bridges pass `owned: true`
|
|
194
167
|
only for envelopes that came from Julewire's own wire format.
|
|
195
168
|
|
|
196
|
-
Bridge runtimes may expose `emit_without_level`
|
|
197
|
-
|
|
198
|
-
|
|
169
|
+
Bridge runtimes may expose `emit_without_level` for public bridge-local callers
|
|
170
|
+
that already applied a level gate. Integration code uses `emit_integration` so
|
|
171
|
+
owned data never falls back through public normalization. Parent runtime
|
|
172
|
+
labels, processors, destinations, and outputs remain parent-owned.
|
|
199
173
|
|
|
200
174
|
## Internal implementation
|
|
201
175
|
|
data/docs/extensions-and-api.md
CHANGED
|
@@ -29,7 +29,7 @@ Allowed processor returns:
|
|
|
29
29
|
nil # draft was mutated in place or unchanged
|
|
30
30
|
draft # explicit draft return
|
|
31
31
|
:drop # stop delivery
|
|
32
|
-
anything else #
|
|
32
|
+
anything else # current draft continues and health records it
|
|
33
33
|
```
|
|
34
34
|
|
|
35
35
|
Mutate the draft for ordinary enrichment:
|
|
@@ -46,6 +46,12 @@ primary processor API:
|
|
|
46
46
|
draft.fetch(:context).fetch(:account)[:id] = "changed"
|
|
47
47
|
```
|
|
48
48
|
|
|
49
|
+
Draft data is already normalized. Processors must use Symbol keys for every
|
|
50
|
+
new or replacement hash key; processors are not a String-key normalization
|
|
51
|
+
boundary. Core validates the current draft after every processor. Invalid data
|
|
52
|
+
is attributed to the processor that produced it, reported, and dropped before a
|
|
53
|
+
later processor can observe it.
|
|
54
|
+
|
|
49
55
|
Use transform helpers when replacing values or sections. They invalidate cached
|
|
50
56
|
records and keep execution lineage when execution identity is unchanged:
|
|
51
57
|
|
|
@@ -54,6 +60,11 @@ draft.transform_field!(:severity) { :warn }
|
|
|
54
60
|
draft.transform_section!(:payload) { |payload| payload.merge(sampled: true) }
|
|
55
61
|
```
|
|
56
62
|
|
|
63
|
+
Transform identifiers are strict Symbols. `transform_field!` accepts only
|
|
64
|
+
known record fields, and `transform_section!` accepts only documented Hash
|
|
65
|
+
sections. These processor-owned helpers do not normalize String identifiers or
|
|
66
|
+
create ad hoc top-level fields.
|
|
67
|
+
|
|
57
68
|
Use `transform_record!` for whole-record replacement transforms:
|
|
58
69
|
|
|
59
70
|
```ruby
|
|
@@ -95,6 +106,9 @@ config.processors.use Julewire::Match.new do
|
|
|
95
106
|
end
|
|
96
107
|
```
|
|
97
108
|
|
|
109
|
+
`Match` condition keys are Symbol keys. A condition whose pattern is `nil`
|
|
110
|
+
matches a present field with a nil value; it does not match a missing field.
|
|
111
|
+
|
|
98
112
|
For deterministic head sampling, use the registered `:sampling` processor:
|
|
99
113
|
|
|
100
114
|
```ruby
|
|
@@ -115,13 +129,14 @@ The default is `on_error: :fail_closed`: core attempts to emit a minimal
|
|
|
115
129
|
`julewire.processor_error` record, the original record is not delivered, and
|
|
116
130
|
later processors are not run. Use `on_error: :fail_open` for non-critical
|
|
117
131
|
enrichment processors that should record the failure, keep the current draft,
|
|
118
|
-
and continue
|
|
119
|
-
|
|
120
|
-
|
|
132
|
+
and continue, provided the draft still satisfies the owned record contract.
|
|
133
|
+
Fail-open never forwards malformed data. Use `on_error: :drop` when a failing
|
|
134
|
+
processor should suppress the record. `on_error:` is a registry option, not a
|
|
135
|
+
processor constructor keyword.
|
|
121
136
|
|
|
122
|
-
Non-raising draft corruption is detected
|
|
123
|
-
|
|
124
|
-
|
|
137
|
+
Non-raising draft corruption is detected immediately after the processor and
|
|
138
|
+
reported as `:processor_data` or `:destination_processor_data` before the
|
|
139
|
+
record is dropped.
|
|
125
140
|
|
|
126
141
|
Processors can inspect execution lineage before the default formatter strips it
|
|
127
142
|
from public output. Promote only the pieces you want to expose:
|
|
@@ -162,10 +177,12 @@ Application emit input crosses a few small objects before it becomes a draft:
|
|
|
162
177
|
| Facade merge | `Core.emit_input` | Combine positional input and keyword fields without normalizing app objects. |
|
|
163
178
|
| Lazy block | `Records::LazyEmitInput` | Keep block-built payloads lazy until the level gate passes and preserve eager severity helpers. |
|
|
164
179
|
| Threshold peek | `Records::RawInput` | Read severity, source, and event from raw input without building a record. |
|
|
165
|
-
| Draft build | `
|
|
180
|
+
| Draft build | `Records::BuildInput` | Split public input into known top-level fields plus payload, or validate strict owned input. |
|
|
166
181
|
|
|
167
|
-
This split keeps below-threshold eager input and lazy blocks cheap
|
|
168
|
-
|
|
182
|
+
This split keeps below-threshold eager input and lazy blocks cheap. Owned
|
|
183
|
+
integration input is validated before processors, each processor result is
|
|
184
|
+
validated before the next processor, and the immutable `Record` boundary keeps
|
|
185
|
+
the destination contract explicit.
|
|
169
186
|
|
|
170
187
|
## Formatters
|
|
171
188
|
|
|
@@ -324,7 +341,7 @@ parts with important ownership or boundary rules.
|
|
|
324
341
|
`EncodingSanitizer.call` repairs strings into valid UTF-8. It is intentionally
|
|
325
342
|
string-only; passing other objects is a type error.
|
|
326
343
|
|
|
327
|
-
`FieldSet` is the public helper for
|
|
344
|
+
`FieldSet` is the public helper for defensive field hashes. Its
|
|
328
345
|
documented surface is:
|
|
329
346
|
|
|
330
347
|
- `coerce`
|
|
@@ -336,8 +353,11 @@ documented surface is:
|
|
|
336
353
|
|
|
337
354
|
`coerce`, `merge`, and `merge!` normalize string keys to symbols and
|
|
338
355
|
defensive-copy values before inserting them, so later caller mutation does not
|
|
339
|
-
mutate core field containers.
|
|
340
|
-
|
|
356
|
+
mutate core field containers. They are public-ingress helpers, not repairs for
|
|
357
|
+
integration-owned or cross-gem protocol data. Use `Integration::Protocol` to
|
|
358
|
+
validate owned data and keep Symbol keys after the ingress boundary.
|
|
359
|
+
`VALUE_KEY` is the key used when non-hash field input is wrapped instead of
|
|
360
|
+
dropped.
|
|
341
361
|
|
|
342
362
|
Other `FieldSet` singleton helpers are core-internal implementation support and
|
|
343
363
|
are not part of the extension contract.
|
|
@@ -349,13 +369,17 @@ Encoders and transport boundaries that need pure log-safe data should use
|
|
|
349
369
|
`Serializer.call` there.
|
|
350
370
|
|
|
351
371
|
`RecordFieldTransform` walks core's normalized record containers with
|
|
352
|
-
`BoundedTransform`. It owns record-shape policy only; processors supply the
|
|
372
|
+
`BoundedTransform.call`. It owns record-shape policy only; processors supply the
|
|
353
373
|
actual filtering or replacement policy.
|
|
354
374
|
|
|
355
375
|
`Carrier` serializes propagation envelopes into flat string carriers for
|
|
356
376
|
external boundaries. It is provider-neutral and does not parse or synthesize
|
|
357
377
|
external headers. Use `max_bytes:` to leave a carrier unchanged and return
|
|
358
378
|
`nil` when the serialized envelope is too large for the target boundary.
|
|
379
|
+
Use `Carrier.extract_result` from integrations that need health/status detail.
|
|
380
|
+
It returns a `Carrier::Extracted` value with `status`, `reason`, `error`, and
|
|
381
|
+
`envelope`. Use `Carrier.extract_envelope` only when a best-effort envelope hash
|
|
382
|
+
is enough.
|
|
359
383
|
|
|
360
384
|
`Julewire::RecordDraft.build` is the raw-input construction path used by core and
|
|
361
385
|
integration code. `Julewire::RecordDraft#to_record` freezes the final normalized data into
|
|
@@ -366,6 +390,10 @@ symbol-key normalized record hash and needs the immutable destination shape.
|
|
|
366
390
|
That path validates the strict internal contract; it does not clean up
|
|
367
391
|
JSON-style or user-input hashes. Use `RecordDraft.build` at raw boundaries.
|
|
368
392
|
|
|
393
|
+
Summary finalizer failure callbacks receive the contained error plus
|
|
394
|
+
`phase: :summary_finish` or `phase: :summary_emit`, so integration callbacks
|
|
395
|
+
should accept keyword metadata.
|
|
396
|
+
|
|
369
397
|
## Public Facade
|
|
370
398
|
|
|
371
399
|
`contracts.md` owns the public facade inventory. This section covers usage
|
|
@@ -376,12 +404,15 @@ Integrations that keep process-local state can register a reset hook with
|
|
|
376
404
|
component: :component_name) { ... }`. The hook runs after core has refreshed its
|
|
377
405
|
own process-local state and after the active pipeline has forwarded
|
|
378
406
|
`after_fork!` to destinations.
|
|
407
|
+
Both hook names are strict integration identifiers: pass non-empty Symbols.
|
|
408
|
+
String or non-Symbol names raise at registration and are never normalized.
|
|
379
409
|
|
|
380
410
|
`Julewire.observe_self!(runtime_name = :default, target: :meta)` starts a
|
|
381
411
|
`Julewire::Core::Diagnostics::MetaObserver`. The observer samples one runtime's
|
|
382
412
|
health and emits health-change records into another named runtime. Pass
|
|
383
413
|
`start: false` and call `sample!` manually when deterministic polling is
|
|
384
|
-
preferred.
|
|
414
|
+
preferred. Call `observer.stop!` to cancel scheduled sampling when the
|
|
415
|
+
observer is no longer needed.
|
|
385
416
|
|
|
386
417
|
Framework and provider adapters may also use the core integration SPI. The
|
|
387
418
|
`Julewire::Core::Integration` namespace is split by concern:
|
|
@@ -418,13 +449,25 @@ procs; return a normalized value or raise.
|
|
|
418
449
|
|
|
419
450
|
Runtime access:
|
|
420
451
|
|
|
452
|
+
- `Integration::Protocol.validate_symbol_keys(value)` for recursive validation
|
|
453
|
+
of integration-owned data before it crosses an internal gem, thread, or
|
|
454
|
+
Ractor protocol boundary. It returns the same value and raises `TypeError`
|
|
455
|
+
for every non-Symbol hash key; it never normalizes protocol data. Validation
|
|
456
|
+
is iterative and continues beyond the later copy/serialization depth bound.
|
|
457
|
+
- `Integration::Protocol.validate_symbol_hash(value)` additionally requires the
|
|
458
|
+
owned section itself to be a `Hash`. Direct integration SPI rejects malformed
|
|
459
|
+
sections immediately. Runtime emit boundaries contain, report, and drop that
|
|
460
|
+
failure instead of emitting a record with silently removed data.
|
|
421
461
|
- `Integration::Facade.with_execution` for framework integrations that
|
|
422
462
|
build fresh execution attributes and want the same execution boundary as
|
|
423
463
|
`Julewire.with_execution` without copying already-owned attribute hashes.
|
|
424
464
|
- `Integration::Facade.emit` for framework/provider integrations that
|
|
425
465
|
emit already-normalized, adapter-owned record hashes. This is not the
|
|
426
466
|
app-facing `Julewire.emit` input path; integrations should pass explicit
|
|
427
|
-
record keys such as `:event`, `:source`, `:payload`, and
|
|
467
|
+
Symbol record keys such as `:event`, `:source`, `:payload`, and
|
|
468
|
+
`:attributes`. Every nested hash key must also be a Symbol; this API does
|
|
469
|
+
not normalize String keys. Custom fields belong inside a documented record
|
|
470
|
+
section; unknown top-level fields are rejected rather than ignored.
|
|
428
471
|
|
|
429
472
|
Owned field overlays:
|
|
430
473
|
|
|
@@ -433,7 +476,10 @@ Owned field overlays:
|
|
|
433
476
|
hashes around callback, request, or message processing.
|
|
434
477
|
- `Integration::Facade.add_context`, `add_carry`, `add_attributes`, and
|
|
435
478
|
`add_neutral` for already-normalized, adapter-owned field hashes added to the
|
|
436
|
-
current execution or ambient context.
|
|
479
|
+
current execution or ambient context. Every nested hash key must be a Symbol;
|
|
480
|
+
these owned overlays do not normalize String keys. Non-Hash values are
|
|
481
|
+
contract errors and are never silently ignored, including when no execution
|
|
482
|
+
scope is active.
|
|
437
483
|
`add_carry` and `add_neutral` are deliberate symmetry points for integrations
|
|
438
484
|
that need ambient propagation or formatter-coordination fields outside a
|
|
439
485
|
scoped callback.
|
|
@@ -461,9 +507,9 @@ methods or indexed access.
|
|
|
461
507
|
|
|
462
508
|
Bounded transforms:
|
|
463
509
|
|
|
464
|
-
- `Julewire::Core::Serialization::BoundedTransform` when a processor or
|
|
465
|
-
walk with core-compatible depth, array, hash, string,
|
|
466
|
-
behavior.
|
|
510
|
+
- `Julewire::Core::Serialization::BoundedTransform.call` when a processor or
|
|
511
|
+
adapter needs a bounded walk with core-compatible depth, array, hash, string,
|
|
512
|
+
cycle, and truncation behavior.
|
|
467
513
|
It can insert `_julewire_truncation` metadata before the final encoder sees
|
|
468
514
|
the payload.
|
|
469
515
|
- `Julewire::Serializer.truncation_metadata` and serializer truncation
|
|
@@ -474,67 +520,16 @@ This SPI is documented support for integration gems, but not a compatibility
|
|
|
474
520
|
freeze. It may change when the ecosystem gets cleaner. `contracts.md` is the
|
|
475
521
|
source of truth for the current tier inventory.
|
|
476
522
|
|
|
477
|
-
## Extension
|
|
478
|
-
|
|
479
|
-
Extensions can require `julewire/core/testing` for small
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
These helpers are shipped support for Julewire extension and integration gems,
|
|
489
|
-
not runtime application API.
|
|
490
|
-
|
|
491
|
-
`Julewire::Testing::Chaos.assert_contained(test_context) { |error| ... }`
|
|
492
|
-
runs a small `StandardError` corpus through containment checks. Use it for
|
|
493
|
-
extension paths that promise to absorb formatter, processor, destination, or
|
|
494
|
-
subscriber failures.
|
|
495
|
-
`Julewire::Testing::Chaos.assert_core_runtime_containment(test_context)` runs
|
|
496
|
-
the same corpus through core's curated runtime containment surfaces: processors,
|
|
497
|
-
formatters, encoders, outputs, callbacks, and lifecycle hooks.
|
|
498
|
-
`Julewire::Testing::Chaos.assert_destination_chaos_contract(...)` runs the
|
|
499
|
-
same corpus through a destination's formatter, encoder, output or transport,
|
|
500
|
-
and callback containment paths using destination builders supplied by the
|
|
501
|
-
extension test.
|
|
502
|
-
`Julewire::Testing::Chaos.assert_emitter_chaos_contract(...)` runs the same
|
|
503
|
-
corpus through a subscriber/listener-style entrypoint while the extension test
|
|
504
|
-
keeps ownership of framework-shaped failing inputs.
|
|
505
|
-
`Julewire::Testing::Chaos.catalog { ... }` builds a deterministic component
|
|
506
|
-
catalog, and `assert_discovered_chaos_contracts(...)` runs the corpus through
|
|
507
|
-
registered processor, formatter, encoder, destination, subscriber, and listener
|
|
508
|
-
entries. Use it when an extension can describe its containment surfaces without
|
|
509
|
-
reflecting over framework internals.
|
|
510
|
-
`Julewire::Testing::Chaos.raiser(error)` builds a callable that raises the
|
|
511
|
-
supplied error.
|
|
512
|
-
|
|
513
|
-
`Julewire::Testing::Contracts` contains shared extension assertions.
|
|
514
|
-
`contracts.md` owns the current helper inventory.
|
|
515
|
-
|
|
516
|
-
Contract helper tiers:
|
|
517
|
-
|
|
518
|
-
- Component contracts (`processor`, `formatter`, `destination`,
|
|
519
|
-
`record_draft`, record shape/source) are the documented extension test surface.
|
|
520
|
-
- Runtime, execution, propagation, integration, validation, truncation, bounded
|
|
521
|
-
transform, and scheduler contracts are integration SPI tests.
|
|
522
|
-
- Chaos helpers are shipped support for containment checks. They are intended
|
|
523
|
-
for extension/integration test suites, not app runtime code.
|
|
524
|
-
|
|
525
|
-
The runtime integration helper emits one point record inside an execution,
|
|
526
|
-
adds context, carry, and summary data, flushes, and asserts that destination
|
|
527
|
-
health is visible. Extensions provide their own output decoder and record paths,
|
|
528
|
-
because formatters may move Julewire fields into a different output shape.
|
|
529
|
-
|
|
530
|
-
`Julewire::Testing::Coverage` is shipped test support for Julewire
|
|
531
|
-
extension gems. It only requires SimpleCov when `Coverage.start!` runs with
|
|
532
|
-
`COVERAGE` set, so runtime users do not load coverage dependencies.
|
|
533
|
-
|
|
534
|
-
The execution-boundary helper gives integration and propagation extensions the
|
|
535
|
-
same probe data and lets the extension run it through its own unit of work. The
|
|
536
|
-
failure-containment helper verifies that extension failures do not escape
|
|
537
|
-
application calls and that health reports degradation.
|
|
523
|
+
## Extension Tests
|
|
524
|
+
|
|
525
|
+
Extensions can require `julewire/core/testing` for four small observation
|
|
526
|
+
fixtures: `Julewire::Testing::CaptureDestination`,
|
|
527
|
+
`Julewire::Testing::NullOutput`, `Julewire::Testing.capture`, and
|
|
528
|
+
`Julewire::Testing.configure_capture_destination`.
|
|
529
|
+
|
|
530
|
+
Core does not ship an assertion DSL or failure-corpus harness. Integration gems
|
|
531
|
+
test their production formatter, destination, processor, subscriber, and
|
|
532
|
+
framework boundary directly.
|
|
538
533
|
|
|
539
534
|
## Internal or Advanced
|
|
540
535
|
|
|
@@ -558,7 +553,8 @@ building another internal record.
|
|
|
558
553
|
|
|
559
554
|
`Julewire::Core::RuntimeLocator.current=` is an advanced runtime hook for bridge
|
|
560
555
|
code. A bridge runtime must support the child-side facade methods it exposes and
|
|
561
|
-
the
|
|
556
|
+
the facade reset hook `reset_facade!`, strict `emit_integration`, plus the
|
|
557
|
+
parent-side bridge calls it forwards: `emit_envelope`,
|
|
562
558
|
`emit_summary_record`, and `flush`. It is deliberately duck-typed; incompatible
|
|
563
559
|
runtimes fail when called, so application code should not replace it casually.
|
|
564
560
|
|
data/docs/health-schema.md
CHANGED
|
@@ -3,6 +3,12 @@
|
|
|
3
3
|
`Julewire.health` is an in-process operational snapshot. It is useful for
|
|
4
4
|
metrics, smoke checks, and debug pages. It is not a delivery receipt.
|
|
5
5
|
|
|
6
|
+
Counters and diagnostic references are updated atomically. A health read is
|
|
7
|
+
not a transaction across those independent values: during a concurrent update,
|
|
8
|
+
one snapshot may combine adjacent observations (for example, a new count with
|
|
9
|
+
the preceding failure reference). Counters remain exact, and later reads
|
|
10
|
+
converge on the latest diagnostic state.
|
|
11
|
+
|
|
6
12
|
## Stable Fields
|
|
7
13
|
|
|
8
14
|
These fields are intended for integration dashboards and contract tests:
|
|
@@ -59,9 +65,13 @@ reset on reconfigure. Top-level `generation` increments when configuration
|
|
|
59
65
|
installs a new pipeline.
|
|
60
66
|
`pipeline.counts[:processor_dropped]` counts intentional processor drops such
|
|
61
67
|
as sampling decisions; processor failures are counted separately under
|
|
62
|
-
`pipeline.counts[:processor_error]`.
|
|
68
|
+
`pipeline.counts[:processor_error]`. Unsupported processor return values keep
|
|
69
|
+
the current draft and increment `pipeline.counts[:processor_invalid]`.
|
|
70
|
+
Malformed processor-owned draft data also increments `processor_invalid`; core
|
|
71
|
+
stops the chain and counts the resulting loss under `processor_dropped`.
|
|
63
72
|
Destination-local processors report the same `processor_dropped` and
|
|
64
|
-
`processor_error` counters under that destination's
|
|
73
|
+
`processor_error`/`processor_invalid` counters under that destination's
|
|
74
|
+
`counts`.
|
|
65
75
|
|
|
66
76
|
`status` fields describe current health for the active runtime generation.
|
|
67
77
|
Runtime, pipeline, and destination failure counters plus `last_failure` /
|
data/docs/internals.md
CHANGED
|
@@ -11,12 +11,14 @@ The runtime stores immutable snapshots of:
|
|
|
11
11
|
- pipeline
|
|
12
12
|
- whether the active pipeline has been closed
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
14
|
+
Main-ractor hot-path reads use the current frozen runtime state without taking
|
|
15
|
+
a global runtime lock. State transitions replace that snapshot atomically;
|
|
16
|
+
configure and reset are additionally serialized with the configure mutex while
|
|
17
|
+
close may overlap them. The successful atomic transition owns the previous
|
|
18
|
+
pipeline lifecycle, so concurrent close/reset operations cannot close the same
|
|
19
|
+
pipeline twice. Health and counters use their own synchronization. Non-main
|
|
20
|
+
ractors use ractor-local runtime storage because they cannot touch the main
|
|
21
|
+
runtime object directly.
|
|
20
22
|
|
|
21
23
|
Configuration, reconfigure, reset, and close are state transitions. They should
|
|
22
24
|
stay boring and explicit.
|
|
@@ -67,10 +69,15 @@ extensions do not pay repeated equivalent-key scans.
|
|
|
67
69
|
|
|
68
70
|
`Records::RawInput` reads app-facing emit input before record construction.
|
|
69
71
|
`Integration::Values::Read` extracts best-effort values from framework objects.
|
|
70
|
-
`Integration::Values::Shape` normalizes adapter-built hashes
|
|
71
|
-
owns
|
|
72
|
-
|
|
73
|
-
|
|
72
|
+
`Integration::Values::Shape` normalizes adapter-built hashes at framework
|
|
73
|
+
ingress. `Fields::FieldSet` owns public field-bag copying, merging, and key
|
|
74
|
+
normalization at application or framework ingress. Once data is owned,
|
|
75
|
+
`Integration::Protocol` and strict deep validation require recursively Symbol-
|
|
76
|
+
keyed hashes; cross-gem code does not repair String keys. `Fields::Lookup` is
|
|
77
|
+
for best-effort display reads with the exact requested key and returns `nil` for
|
|
78
|
+
unreadable inputs; it does not bridge Symbol/String key forms inside normalized
|
|
79
|
+
records. Presentation code that reads decoded wire payloads uses its separate
|
|
80
|
+
`wire_value` helper at that restore boundary.
|
|
74
81
|
|
|
75
82
|
## Context Storage
|
|
76
83
|
|
|
@@ -90,10 +97,10 @@ custom destination objects.
|
|
|
90
97
|
|
|
91
98
|
## Scheduling
|
|
92
99
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
100
|
+
Main-process diagnostics and framework integrations use the process-owned
|
|
101
|
+
`Scheduling::SharedScheduler` so they do not each keep a background timer
|
|
102
|
+
thread. Ractor integrations own their stdlib timeout threads because a worker
|
|
103
|
+
Ractor cannot access the main scheduler's concurrent-ruby objects.
|
|
97
104
|
|
|
98
105
|
## Remote Envelope Hook
|
|
99
106
|
|
|
@@ -103,12 +110,6 @@ a normal record from input, context, attributes, carry, neutral, and that
|
|
|
103
110
|
snapshot before emitting through the active pipeline. Bridges pass `owned: true`
|
|
104
111
|
only for Julewire-owned wire data. It is not a public application API.
|
|
105
112
|
|
|
106
|
-
## Test Seams
|
|
107
|
-
|
|
108
|
-
Some private methods are intentionally reachable through `Julewire::Testing`.
|
|
109
|
-
They reset process-global registries or storage that normal applications should
|
|
110
|
-
not touch directly.
|
|
111
|
-
|
|
112
113
|
## Emit Entrypoints
|
|
113
114
|
|
|
114
115
|
| Entrypoint | Caller | Level gate | Input ownership |
|