assistant 1.0.0.rc1 → 1.0.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.
@@ -1,17 +1,53 @@
1
- ---
2
- title: Execute callbacks
3
- parent: Examples
4
- nav_order: 5
5
- ---
6
-
7
1
  # Execute callbacks
8
2
 
9
- > **Status:** placeholder ships in
10
- > [P10](https://github.com/ramongr/assistant/blob/main/docs/v1/08-github-pages.md#p6p12-examples-one-pr-per-example) of
11
- > the GitHub Pages plan.
3
+ `before_execute` / `after_execute` / `around_execute` fire around the
4
+ `#execute` body — see the [Composing services guide](../guides/composing-services.md#execute-callbacks)
5
+ for the full contract.
6
+
7
+ An audit logger and a wall-time wrapper, combined:
8
+
9
+ ```ruby
10
+ class AuditedService < Assistant::Service
11
+ input :user_id, type: Integer, required: true
12
+
13
+ before_execute do
14
+ log_item_info(source: :audit, detail: :start, message: "user=#{user_id}")
15
+ end
16
+
17
+ after_execute do |_result|
18
+ log_item_info(source: :audit, detail: :finish, message: "user=#{user_id}")
19
+ end
20
+
21
+ around_execute do |&blk|
22
+ started = Process.clock_gettime(Process::CLOCK_MONOTONIC)
23
+ inner = blk.call
24
+ elapsed_ms = ((Process.clock_gettime(Process::CLOCK_MONOTONIC) - started) * 1000).round(1)
25
+ log_item_info(source: :audit, detail: :timing, message: "#{elapsed_ms}ms")
26
+ inner
27
+ end
28
+
29
+ def execute
30
+ # ... business logic ...
31
+ end
32
+ end
33
+ ```
34
+
35
+ Failure semantics:
12
36
 
13
- `before_execute` audit logger plus an `around_execute` timing wrapper, including failure cases.
37
+ * `before_execute` callbacks run **before** `#execute`. Logging an
38
+ error from a `before_execute` does **not** prevent `#execute` from
39
+ running — it just becomes part of the result hash. Use the
40
+ declarative `valid_*_*?` family or `#validate` when you need to
41
+ short-circuit.
42
+ * `around_execute` blocks receive the inner continuation as the block
43
+ argument (`&blk`) and **must** call it exactly once. Skipping
44
+ `blk.call` silently drops the inner result.
45
+ * `after_execute` blocks receive the `#execute` return value as their
46
+ single positional arg.
47
+ * If a hook itself raises, `Assistant::Service` rescues the
48
+ `StandardError` and logs it as
49
+ `source: :hook, detail: :before_execute|:around_execute|:after_execute`
50
+ — execution of subsequent hooks continues.
14
51
 
15
- When the runnable script under `examples/execute_callbacks/` lands, this
16
- page will include it verbatim via Jekyll `include_relative` so the
17
- prose stays in lockstep with the code.
52
+ > Source: [`examples/execute_callbacks/`](https://github.com/ramongr/assistant/tree/main/examples/execute_callbacks) ·
53
+ > Test: [`test/examples/execute_callbacks_example_test.rb`](https://github.com/ramongr/assistant/blob/main/test/examples/execute_callbacks_example_test.rb)
@@ -1,17 +1,52 @@
1
- ---
2
- title: Instrumentation notifier
3
- parent: Examples
4
- nav_order: 6
5
- ---
6
-
7
1
  # Instrumentation notifier
8
2
 
9
- > **Status:** placeholder ships in
10
- > [P11](https://github.com/ramongr/assistant/blob/main/docs/v1/08-github-pages.md#p6p12-examples-one-pr-per-example) of
11
- > the GitHub Pages plan.
3
+ `Assistant.notifier=` accepts any object responding to `#call(event,
4
+ payload)`. Set it once at boot to wire every service into your
5
+ existing instrumentation pipeline — see the [Composing services
6
+ guide](../guides/composing-services.md#instrumentation-notifier)
7
+ for the contract and the
8
+ [API reference](../api-reference.md#instrumentation-notifier) for the
9
+ event list.
10
+
11
+ Wiring it to an `ActiveSupport::Notifications`-shaped sink:
12
+
13
+ ```ruby
14
+ Assistant.notifier = ->(event, payload) {
15
+ ActiveSupport::Notifications.instrument("assistant.#{event}", payload)
16
+ }
17
+ ```
18
+
19
+ A fake sink for tests:
20
+
21
+ ```ruby
22
+ events = []
23
+ Assistant.notifier = ->(event, payload) { events << [event, payload] }
24
+
25
+ CreateUser.run(email: 'a@b.com', name: 'Alice')
26
+
27
+ events.map(&:first)
28
+ # => [:service_started, :service_validated, :service_executed]
29
+ ```
30
+
31
+ A failure path emits `:service_failed` instead of `:service_executed`:
32
+
33
+ ```ruby
34
+ events.clear
35
+ CreateUser.run(email: nil, name: 'Alice')
36
+
37
+ events.map(&:first)
38
+ # => [:service_started, :service_validated, :service_failed]
39
+ ```
40
+
41
+ Payload shape for every event is exactly two keys: `service_class:`
42
+ (the `Assistant::Service` subclass) and `duration_s:` (a `Float`
43
+ seconds since `#run` started). The event set and its payload contract
44
+ are **Frozen** for 1.0 — see the
45
+ [full payload table](../api-reference.md#instrumentation-notifier).
12
46
 
13
- `Assistant.notifier=` wired to a fake `ActiveSupport::Notifications`-shaped sink.
47
+ > **Warning** Notifier callables are rescued from `StandardError`;
48
+ > any exception they raise is `warn`-ed but doesn't fail the service.
49
+ > Don't put control flow in the notifier.
14
50
 
15
- When the runnable script under `examples/instrumentation_notifier/` lands, this
16
- page will include it verbatim via Jekyll `include_relative` so the
17
- prose stays in lockstep with the code.
51
+ > Source: [`examples/instrumentation_notifier/`](https://github.com/ramongr/assistant/tree/main/examples/instrumentation_notifier) ·
52
+ > Test: [`test/examples/instrumentation_notifier_example_test.rb`](https://github.com/ramongr/assistant/blob/main/test/examples/instrumentation_notifier_example_test.rb)
@@ -1,17 +1,44 @@
1
- ---
2
- title: Rails service
3
- parent: Examples
4
- nav_order: 1
5
- ---
6
-
7
1
  # Rails service
8
2
 
9
- > **Status:** placeholder ships in
10
- > [P6](https://github.com/ramongr/assistant/blob/main/docs/v1/08-github-pages.md#p6p12-examples-one-pr-per-example) of
11
- > the GitHub Pages plan.
3
+ A Rails-shaped controller that calls a service and pattern-matches on
4
+ the result hash:
5
+
6
+ ```ruby
7
+ class UsersController < ApplicationController
8
+ def create
9
+ case CreateUser.run(**user_params.to_h.symbolize_keys)
10
+ in { result: user, status: :ok }
11
+ render json: user, status: :created
12
+ in { result: user, status: :with_warnings, warnings: }
13
+ Rails.logger.warn(warnings.map(&:item))
14
+ render json: user, status: :created
15
+ in { errors:, status: :with_errors }
16
+ render json: { errors: errors.map(&:item) },
17
+ status: :unprocessable_entity
18
+ end
19
+ end
20
+
21
+ private
22
+
23
+ def user_params
24
+ params.require(:user).permit(:email, :name)
25
+ end
26
+ end
27
+ ```
28
+
29
+ Notes:
30
+
31
+ * `Service.run` never raises for application-level failures — it always
32
+ returns a hash. Use `rescue` only for true exceptions (network,
33
+ database, etc.), and let `:with_errors` carry the validation /
34
+ business-rule failures.
35
+ * `LogItem#item` returns a `Hash{Symbol => Object}` that's safe to
36
+ pass straight to `render json:`.
12
37
 
13
- A Rails-shaped controller calling a service and pattern-matching on the result hash.
38
+ See the [Getting started guide](../getting-started.md) for the
39
+ result-shape contract and the
40
+ [API reference](../api-reference.md#result-shape) for the full status
41
+ enum.
14
42
 
15
- When the runnable script under `examples/rails_service/` lands, this
16
- page will include it verbatim via Jekyll `include_relative` so the
17
- prose stays in lockstep with the code.
43
+ > Source: [`examples/rails_service/`](https://github.com/ramongr/assistant/tree/main/examples/rails_service) ·
44
+ > Test: [`test/examples/rails_service_example_test.rb`](https://github.com/ramongr/assistant/blob/main/test/examples/rails_service_example_test.rb)
@@ -1,17 +1,94 @@
1
- ---
2
- title: RBS generator
3
- parent: Examples
4
- nav_order: 7
5
- ---
6
-
7
1
  # RBS generator
8
2
 
9
- > **Status:** placeholder ships in
10
- > [P12](https://github.com/ramongr/assistant/blob/main/docs/v1/08-github-pages.md#p6p12-examples-one-pr-per-example) of
11
- > the GitHub Pages plan.
3
+ `bin/assistant-rbs` (installed as the `assistant-rbs` executable when
4
+ the gem is added to a `Gemfile`) scans Ruby files for
5
+ `Assistant::Service` subclasses and emits per-class RBS sidecar
6
+ signatures into a target directory. See the
7
+ [RBS and types guide](../guides/rbs-and-types.md) for the design
8
+ rationale and Steep setup details.
9
+
10
+ Given a service like:
11
+
12
+ ```ruby
13
+ # examples/rbs_generator/create_user.rb
14
+ module RbsGeneratorExample
15
+ class CreateUser < Assistant::Service
16
+ input :email, type: String, required: true
17
+ input :name, type: String, required: true
18
+ input :role, type: [String, Symbol], allow_nil: true, default: nil
19
+
20
+ def validate
21
+ return if email.include?('@')
22
+
23
+ log_item_error(source: :validate, detail: :email, message: 'must contain @')
24
+ end
25
+
26
+ def execute
27
+ { email:, name:, role: role || :member }
28
+ end
29
+ end
30
+ end
31
+ ```
32
+
33
+ Run the generator against that file:
34
+
35
+ ```text
36
+ $ bundle exec exe/assistant-rbs examples/rbs_generator/create_user.rb \
37
+ --output examples/rbs_generator/sig
38
+ ```
39
+
40
+ The committed output lives at
41
+ `examples/rbs_generator/sig/rbs_generator_example/create_user.rbs`:
42
+
43
+ ```rbs
44
+ # Generated by assistant-rbs; do not edit.
45
+
46
+ module RbsGeneratorExample
47
+ class CreateUser < Assistant::Service
48
+
49
+ def email: () -> String
50
+ def email?: () -> bool
51
+ def name: () -> String
52
+ def name?: () -> bool
53
+ def role: () -> (String | Symbol)?
54
+ def role?: () -> bool
55
+
56
+ end
57
+ end
58
+ ```
59
+
60
+ Steep can now type-check call sites that read
61
+ `RbsGeneratorExample::CreateUser#email`, `#name`, and `#role`, even though
62
+ those methods are defined via `define_method` at class-load time:
63
+
64
+ ```ruby
65
+ # examples/rbs_generator/type_probe.rb
66
+ service = RbsGeneratorExample::CreateUser.new(email: 'ada@example.com', name: 'Ada', role: :admin)
67
+
68
+ service.email.upcase
69
+ service.name.length
70
+ service.role&.to_s
71
+ ```
72
+
73
+ Run the example-local check with:
74
+
75
+ ```sh
76
+ cd examples/rbs_generator
77
+ bundle exec steep check --steepfile Steepfile --jobs=1
78
+ ```
79
+
80
+ Notes:
12
81
 
13
- Service definition → `bin/assistant-rbs --output sig` Steep proving the per-input return type.
82
+ * `--output sig` writes alongside any existing `sig/` tree; the
83
+ generator only owns files it tagged with its `# Generated by
84
+ assistant-rbs; do not edit.` marker — hand-written sidecars are left
85
+ untouched.
86
+ * Re-run after each `Service.input` change. The output is fully
87
+ derived from the live constant graph, so CI can compare generated
88
+ output against committed sidecars to catch drift.
89
+ * The generator declares input getters and presence predicates only.
90
+ Hand-write additional signatures for your own domain methods if your
91
+ call sites need them.
14
92
 
15
- When the runnable script under `examples/rbs_generator/` lands, this
16
- page will include it verbatim via Jekyll `include_relative` so the
17
- prose stays in lockstep with the code.
93
+ > Source: [`examples/rbs_generator/`](https://github.com/ramongr/assistant/tree/main/examples/rbs_generator) ·
94
+ > Test: [`test/examples/rbs_generator_example_test.rb`](https://github.com/ramongr/assistant/blob/main/test/examples/rbs_generator_example_test.rb)
@@ -1,17 +1,37 @@
1
- ---
2
- title: Sidekiq worker
3
- parent: Examples
4
- nav_order: 3
5
- ---
6
-
7
1
  # Sidekiq worker
8
2
 
9
- > **Status:** placeholder ships in
10
- > [P8](https://github.com/ramongr/assistant/blob/main/docs/v1/08-github-pages.md#p6p12-examples-one-pr-per-example) of
11
- > the GitHub Pages plan.
3
+ A Sidekiq worker that runs a service idempotently and routes warnings
4
+ and errors to separate sinks:
5
+
6
+ ```ruby
7
+ class CreateUserWorker
8
+ include Sidekiq::Worker
9
+ sidekiq_options retry: 5, queue: :default
10
+
11
+ def perform(user_attrs)
12
+ case CreateUser.run(**user_attrs.transform_keys(&:to_sym))
13
+ in { result:, status: :ok }
14
+ # Done. Sidekiq sees no exception, no retry.
15
+ in { result:, status: :with_warnings, warnings: }
16
+ WarningsSink.publish(worker: self.class.name, items: warnings.map(&:item))
17
+ in { errors:, status: :with_errors }
18
+ # Permanent business-rule failure: don't retry, surface to ops.
19
+ ErrorsSink.publish(worker: self.class.name, items: errors.map(&:item))
20
+ end
21
+ end
22
+ end
23
+ ```
24
+
25
+ Notes:
12
26
 
13
- A background job that runs a service idempotently, logging warnings vs errors separately.
27
+ * The worker **never re-raises** for `:with_errors`. Validation /
28
+ business-rule failures are not transient and shouldn't burn Sidekiq
29
+ retries. Let true exceptions (network, database) propagate.
30
+ * `LogItem#item` returns plain symbols/strings, which serialize
31
+ cleanly to JSON / your APM tool.
32
+ * For idempotency, keep `CreateUser` purely declarative: read the
33
+ caller's identifier, look up existing state, no-op when already
34
+ satisfied.
14
35
 
15
- When the runnable script under `examples/sidekiq_worker/` lands, this
16
- page will include it verbatim via Jekyll `include_relative` so the
17
- prose stays in lockstep with the code.
36
+ > Source: [`examples/sidekiq_worker/`](https://github.com/ramongr/assistant/tree/main/examples/sidekiq_worker) ·
37
+ > Test: [`test/examples/sidekiq_worker_example_test.rb`](https://github.com/ramongr/assistant/blob/main/test/examples/sidekiq_worker_example_test.rb)
@@ -1,8 +1,3 @@
1
- ---
2
- title: Getting started
3
- nav_order: 1
4
- ---
5
-
6
1
  <!-- markdownlint-disable MD013 MD024 -->
7
2
  # Getting started
8
3
 
@@ -120,6 +115,17 @@ The `:status` value is exhaustively one of `:ok`, `:with_warnings`,
120
115
  `:with_errors`. No new status values can be introduced in 1.x without a
121
116
  deprecation cycle.
122
117
 
118
+ At a glance, that decision is just:
119
+
120
+ ```mermaid
121
+ flowchart LR
122
+ Run([Service.run]) --> Errors{errors logged?}
123
+ Errors -- Yes --> WithErrors[":with_errors<br/>(result is nil)"]
124
+ Errors -- No --> Warnings{warnings logged?}
125
+ Warnings -- Yes --> WithWarnings[":with_warnings<br/>(result is set)"]
126
+ Warnings -- No --> Ok[":ok<br/>(result is set)"]
127
+ ```
128
+
123
129
  ## What's next
124
130
 
125
131
  - The same example, but with optional / multi-type inputs and a
@@ -1,10 +1,3 @@
1
- ---
2
- title: Guides
3
- nav_order: 2
4
- has_children: true
5
- permalink: /guides/
6
- ---
7
-
8
1
  # Guides
9
2
 
10
3
  Topic-focused walkthroughs of every part of the Assistant DSL.
@@ -16,7 +9,7 @@ tests under `test/docs/<guide>_examples_test.rb`, and ends with a
16
9
  `default:`, `allow_nil:`, `optional:`, `if:` conditional requirement,
17
10
  and the `assistant-rbs` Steep recipe.
18
11
  - [Validation](./validation.md) — auto-checks, `#validate`, warnings
19
- vs errors, conditional requirements, strict `LogItem.new` (M10).
12
+ vs errors, conditional requirements, strict `LogItem.new`.
20
13
  - [Logging and results](./logging-and-results.md) — `LogItem`,
21
14
  levels, `log_item_*` shorthands, `merge_logs`, the result hash.
22
15
  - [Composing services](./composing-services.md) — `call_service`,
@@ -1,9 +1,3 @@
1
- ---
2
- title: Composing services
3
- parent: Guides
4
- nav_order: 4
5
- ---
6
-
7
1
  <!-- markdownlint-disable MD013 MD024 -->
8
2
  # Composing services
9
3
 
@@ -17,8 +11,8 @@ nav_order: 4
17
11
  > collaborator.
18
12
 
19
13
  This guide covers the four composition surfaces shipped in 1.0:
20
- service-to-service calls (M-S2), execute callbacks (M-S1), the
21
- instrumentation notifier (M-S3), and the input snapshot (M-S4).
14
+ service-to-service calls, execute callbacks, the instrumentation
15
+ notifier, and the input snapshot.
22
16
 
23
17
  ## `#call_service`: nest one service inside another
24
18
 
@@ -67,7 +61,7 @@ Notes:
67
61
  - `call_service` always calls `inner.run`, so calling it twice would
68
62
  re-execute the inner service.
69
63
 
70
- ## Execute callbacks (M-S1)
64
+ ## Execute callbacks
71
65
 
72
66
  Three class-level DSL methods register callbacks around `#execute`.
73
67
  Hooks are evaluated in the context of the service instance.
@@ -112,7 +106,7 @@ Behavior:
112
106
  - **Missing block:** registering a hook without a block raises
113
107
  `ArgumentError` at class-definition time.
114
108
 
115
- ## Instrumentation notifier (M-S3)
109
+ ## Instrumentation notifier
116
110
 
117
111
  Assign a callable to `Assistant.notifier =` to receive a fixed set
118
112
  of events for **every** service execution:
@@ -153,7 +147,7 @@ To disable instrumentation entirely, restore the default no-op:
153
147
  Assistant.notifier = Assistant::DEFAULT_NOTIFIER
154
148
  ```
155
149
 
156
- ## `#input_snapshot` (M-S4)
150
+ ## `#input_snapshot`
157
151
 
158
152
  `#input_snapshot` returns a read-only `Data` view of the service's
159
153
  declared inputs (post-`default:` / post-`allow_nil:`). It's the
@@ -218,5 +212,5 @@ Notes:
218
212
  requirements.
219
213
  - [Logging and results](./logging-and-results.md) — what
220
214
  `merge_logs(logs:)` does, the result hash shape.
221
- - [API reference](../api-reference.md#assistantservice) for the
215
+ - [API reference](../api-reference.md#assistant-service) for the
222
216
  full callback / call_service / notifier surfaces.
@@ -1,9 +1,3 @@
1
- ---
2
- title: Inputs
3
- parent: Guides
4
- nav_order: 1
5
- ---
6
-
7
1
  <!-- markdownlint-disable MD013 MD024 -->
8
2
  # Inputs
9
3
 
@@ -40,7 +34,7 @@ Three things to notice:
40
34
  1. **`input` and `inputs` take a leading positional name** (`:email`,
41
35
  `%i[street city]`). Every other DSL option is a keyword argument.
42
36
  This is the only place in the gem where a positional argument
43
- survives the M12 keyword-only sweep — see
37
+ survives the keyword-only DSL — see
44
38
  [`api-reference.md`](../api-reference.md#class-methods).
45
39
  2. **The constructor is keyword-only.** You call
46
40
  `CreateUser.run(email: 'a@b.com', name: 'Alice')`, never
@@ -78,7 +72,7 @@ TouchEmail.run(email: 42)
78
72
  # message: "Service argument with name email is not a String but Integer">] }
79
73
  ```
80
74
 
81
- ### Multi-type inputs (M3)
75
+ ### Multi-type inputs
82
76
 
83
77
  Pass an array of classes when more than one is acceptable:
84
78
 
@@ -126,7 +120,7 @@ The deprecated 0.x name `#valid_require_<name>?` still works in 1.x —
126
120
  calls emit a one-time `Kernel.warn` per call site and delegate to the
127
121
  canonical predicate. See [`docs/deprecations.md`](../deprecations.md).
128
122
 
129
- ## `default:` (M1)
123
+ ## `default:`
130
124
 
131
125
  Provide a fallback when the caller omits an input. Pass a callable
132
126
  (method, lambda, or proc) to compute the default lazily — `assistant`
@@ -155,7 +149,7 @@ input :token, type: String, default: -> { SecureRandom.uuid }
155
149
  A `default:` provider that takes arguments raises `ArgumentError` at
156
150
  class-definition time.
157
151
 
158
- ## `allow_nil:` (M2)
152
+ ## `allow_nil:`
159
153
 
160
154
  By default, `nil` for a typed input logs a type-mismatch error.
161
155
  `allow_nil: true` makes `nil` a legal value:
@@ -177,7 +171,7 @@ TouchAge.run(age: 30).fetch(:result) # => 30
177
171
  Combine with `default:` to express "optional integer that defaults to
178
172
  nil and may be set to nil explicitly".
179
173
 
180
- ## `optional: true` (M7)
174
+ ## `optional: true`
181
175
 
182
176
  `optional: true` is a shorthand for "skip the presence check entirely;
183
177
  do not generate `#valid_required_name?`". It is mutually exclusive
@@ -278,7 +272,7 @@ generic `.rbs` for `Service` can't know that your `CreateUser#email`
278
272
  returns `String`. That's R1 in
279
273
  [`docs/v1/05-quality-and-tooling.md`](https://github.com/ramongr/assistant/blob/main/docs/v1/05-quality-and-tooling.md).
280
274
 
281
- The bundled `assistant-rbs` CLI (M11) closes the gap by emitting
275
+ The bundled `assistant-rbs` CLI closes the gap by emitting
282
276
  per-class `.rbs` files. Run it once after editing your services:
283
277
 
284
278
  ```sh
@@ -1,9 +1,3 @@
1
- ---
2
- title: Logging and results
3
- parent: Guides
4
- nav_order: 3
5
- ---
6
-
7
1
  <!-- markdownlint-disable MD013 MD024 -->
8
2
  # Logging and results
9
3
 
@@ -12,7 +6,7 @@ nav_order: 3
12
6
  > add entries, `#logs` / `#infos` / `#warnings` / `#errors` to read
13
7
  > them, and the result hash returned by `.run` to consume the
14
8
  > service from outside. `LogItem.new` raises `ArgumentError` for
15
- > invalid attributes (M10) — prefer the helpers.
9
+ > invalid attributes — prefer the helpers.
16
10
 
17
11
  This guide covers the data model, the writer helpers, the reader
18
12
  predicates, and the shape of the result hash. See the
@@ -32,7 +26,7 @@ fields:
32
26
  | `message` | `String` | Human-readable text. |
33
27
  | `trace` | `Array<String>` or `nil` | Optional backtrace captured at construction. |
34
28
 
35
- Constraints (enforced strictly in 1.0 — M10):
29
+ Constraints (enforced strictly in 1.0):
36
30
 
37
31
  - `source != detail`.
38
32
  - `source` and `detail` must each be non-empty.
@@ -46,7 +40,7 @@ Assistant::LogItem::VALID_LEVELS
46
40
 
47
41
  ## Writing log entries
48
42
 
49
- The three shorthand helpers (M5) are the recommended call sites
43
+ The three shorthand helpers are the recommended call sites
50
44
  inside `#validate` and `#execute`:
51
45
 
52
46
  ```ruby
@@ -162,7 +156,7 @@ def execute
162
156
  end
163
157
  ```
164
158
 
165
- > **M12.** `#merge_logs` is keyword-only in 1.0. Passing positional
159
+ > **Keyword-only.** `#merge_logs` is keyword-only in 1.0. Passing positional
166
160
  > arguments raises `ArgumentError`. The
167
161
  > [migration guide](https://github.com/ramongr/assistant/blob/main/docs/v1/06-migration-0x-to-1.md) covers the
168
162
  > mechanical rewrite.
@@ -181,14 +175,14 @@ service.errors.first.item
181
175
  ## Common pitfalls
182
176
 
183
177
  - **Pushing onto `@logs` directly.** Don't — always go through the
184
- helpers so the M10 strict construction runs and so future
178
+ helpers so the strict construction runs and so future
185
179
  middleware (e.g. an instrumentation hook around `#add_log`) can
186
180
  see the entry.
187
181
  - **Using `LogItem.new` with `source == detail`.** Raises
188
182
  `ArgumentError`. Pick distinct symbols.
189
183
  - **Treating `#infos` as part of the contract.** They're for
190
184
  introspection only; the result hash never includes them.
191
- - **Calling `merge_logs(other.logs)` (positional).** M12 requires
185
+ - **Calling `merge_logs(other.logs)` (positional).** 1.0 requires
192
186
  the keyword form: `merge_logs(logs: other.logs)`.
193
187
 
194
188
  ## See also
@@ -197,6 +191,6 @@ service.errors.first.item
197
191
  conditional checks, `#validate` mechanics.
198
192
  - [Composing services](./composing-services.md) — how `#call_service`
199
193
  merges inner logs into the outer timeline.
200
- - [API reference: LogItem](../api-reference.md#assistantlogitem).
201
- - [API reference: LogList](../api-reference.md#assistantloglist).
202
- - [Migration guide](https://github.com/ramongr/assistant/blob/main/docs/v1/06-migration-0x-to-1.md) for M10 + M12.
194
+ - [API reference: LogItem](../api-reference.md#assistant-logitem).
195
+ - [API reference: LogList](../api-reference.md#assistant-loglist).
196
+ - [Migration guide](https://github.com/ramongr/assistant/blob/main/docs/v1/06-migration-0x-to-1.md) for the 1.0 breaking changes.