analytics_ops 0.2.0 → 0.3.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 (72) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +108 -0
  3. data/CONTRIBUTING.md +1 -0
  4. data/README.md +141 -13
  5. data/SECURITY.md +11 -2
  6. data/docs/ai-connections.md +192 -0
  7. data/docs/api-support-matrix.md +25 -8
  8. data/docs/architecture.md +78 -17
  9. data/docs/authentication.md +53 -3
  10. data/docs/bigquery.md +128 -0
  11. data/docs/commands.md +133 -12
  12. data/docs/configuration-schema-v1.json +608 -50
  13. data/docs/configuration.md +118 -5
  14. data/docs/google-client-compatibility.md +21 -1
  15. data/docs/governance.md +93 -0
  16. data/docs/health.md +82 -0
  17. data/docs/live-smoke-test.md +13 -10
  18. data/docs/rails.md +25 -9
  19. data/docs/reports.md +165 -3
  20. data/docs/safety.md +37 -5
  21. data/docs/troubleshooting.md +81 -7
  22. data/lib/analytics_ops/bigquery/definition.rb +173 -0
  23. data/lib/analytics_ops/bigquery.rb +4 -0
  24. data/lib/analytics_ops/cli/local_commands.rb +80 -0
  25. data/lib/analytics_ops/cli/options.rb +282 -9
  26. data/lib/analytics_ops/cli/presenter.rb +263 -28
  27. data/lib/analytics_ops/cli/presenter_csv.rb +92 -0
  28. data/lib/analytics_ops/cli/reporting_commands.rb +129 -0
  29. data/lib/analytics_ops/cli/setup_commands.rb +91 -0
  30. data/lib/analytics_ops/cli.rb +121 -44
  31. data/lib/analytics_ops/clients/admin.rb +28 -2
  32. data/lib/analytics_ops/clients/admin_governance.rb +95 -0
  33. data/lib/analytics_ops/clients/admin_governance_normalization.rb +90 -0
  34. data/lib/analytics_ops/clients/admin_resource_normalization.rb +61 -0
  35. data/lib/analytics_ops/clients/bigquery.rb +313 -0
  36. data/lib/analytics_ops/clients/data.rb +147 -54
  37. data/lib/analytics_ops/clients/data_result_normalization.rb +55 -0
  38. data/lib/analytics_ops/clients/error_translation.rb +13 -1
  39. data/lib/analytics_ops/configuration/schema.rb +175 -1
  40. data/lib/analytics_ops/configuration/validator.rb +45 -7
  41. data/lib/analytics_ops/configuration/validator_experimental.rb +136 -0
  42. data/lib/analytics_ops/configuration/validator_reporting.rb +93 -0
  43. data/lib/analytics_ops/configuration/writer.rb +112 -7
  44. data/lib/analytics_ops/desired_state.rb +28 -3
  45. data/lib/analytics_ops/errors.rb +33 -1
  46. data/lib/analytics_ops/funnels.rb +58 -0
  47. data/lib/analytics_ops/governance.rb +363 -0
  48. data/lib/analytics_ops/health.rb +310 -0
  49. data/lib/analytics_ops/mcp_server/custom_report_tool.rb +101 -0
  50. data/lib/analytics_ops/mcp_server/discovery_tools.rb +79 -0
  51. data/lib/analytics_ops/mcp_server/experimental_tools.rb +89 -0
  52. data/lib/analytics_ops/mcp_server/health_tools.rb +80 -0
  53. data/lib/analytics_ops/mcp_server/overview_tools.rb +49 -0
  54. data/lib/analytics_ops/mcp_server/standard_report_tool.rb +53 -0
  55. data/lib/analytics_ops/mcp_server.rb +402 -0
  56. data/lib/analytics_ops/portfolio.rb +216 -0
  57. data/lib/analytics_ops/rails/railtie.rb +19 -8
  58. data/lib/analytics_ops/redaction.rb +10 -6
  59. data/lib/analytics_ops/reports/csv_export.rb +91 -0
  60. data/lib/analytics_ops/reports/definition.rb +152 -14
  61. data/lib/analytics_ops/reports/metadata.rb +158 -0
  62. data/lib/analytics_ops/reports/period.rb +104 -0
  63. data/lib/analytics_ops/reports/result.rb +8 -3
  64. data/lib/analytics_ops/reports.rb +3 -0
  65. data/lib/analytics_ops/service_account.rb +321 -29
  66. data/lib/analytics_ops/setup.rb +31 -7
  67. data/lib/analytics_ops/version.rb +1 -1
  68. data/lib/analytics_ops/workspace.rb +222 -10
  69. data/lib/analytics_ops.rb +7 -2
  70. data/lib/generators/analytics_ops/templates/analytics_ops.yml +1 -1
  71. data/sig/analytics_ops.rbs +494 -9
  72. metadata +60 -1
@@ -10,10 +10,15 @@ property:
10
10
  analytics-ops setup --service-account /absolute/path/to/service-account.json
11
11
  ```
12
12
 
13
- Setup writes only a quoted property ID under the `production` profile. It
14
- will reuse a matching file but will not overwrite an existing profile that
13
+ Setup writes only a quoted property ID under the selected profile. It reuses a
14
+ matching profile, safely appends a missing profile, and fills the untouched
15
+ Rails-generator placeholder. It will not overwrite an existing profile that
15
16
  targets another property.
16
17
 
18
+ ```bash
19
+ analytics-ops setup --profile client_b --connection client_b
20
+ ```
21
+
17
22
  ## Smallest valid file
18
23
 
19
24
  ```yaml
@@ -38,7 +43,7 @@ analytics-ops audit
38
43
  The default path is `config/analytics_ops.yml`; the default profile is
39
44
  `production`. Change them with `--config` and `--profile`.
40
45
 
41
- ## Complete example
46
+ ## Managed-settings example
42
47
 
43
48
  All identifiers below are fake:
44
49
 
@@ -55,7 +60,7 @@ profiles:
55
60
  default_uri: "https://www.example.test"
56
61
 
57
62
  # Accepted only as an explicit experimental declaration.
58
- # Version 0.2.0 reports a finding and does not apply this setting.
63
+ # Version 0.3.0 reports a finding and does not apply this setting.
59
64
  enhanced_measurement:
60
65
  enabled: true
61
66
  experimental: true
@@ -95,7 +100,7 @@ profiles:
95
100
  - email_redaction_enabled
96
101
  - consent_mode_reviewed
97
102
 
98
- # Explicit declaration only in 0.2.0; never silently applied.
103
+ # Explicit declaration only in 0.3.0; never silently applied.
99
104
  google_signals:
100
105
  state: disabled
101
106
  experimental: true
@@ -109,6 +114,8 @@ profiles:
109
114
  `stream_id`. For a web stream, `default_uri` may be an absolute HTTP or
110
115
  HTTPS URI without embedded credentials. Analytics Ops can update an existing
111
116
  web stream URI; it does not automatically create or delete streams.
117
+ Two local stream names cannot target the same `stream_id`; that ambiguity is
118
+ rejected while loading configuration.
112
119
 
113
120
  ### Retention
114
121
 
@@ -157,6 +164,112 @@ Identity is `parameter_name`. Scope, unit, and restricted-data classification
157
164
  are treated as immutable; Analytics Ops will not archive and recreate a
158
165
  conflicting metric.
159
166
 
167
+ ## Optional reporting and organization fields
168
+
169
+ These fields improve reporting but do not create an ordinary mutation plan.
170
+
171
+ ### Labels
172
+
173
+ Labels help identify profiles in a multi-property portfolio:
174
+
175
+ ```yaml
176
+ labels:
177
+ client: Example client
178
+ brand: Example store
179
+ region: North America
180
+ ```
181
+
182
+ A profile can contain up to 16 short label pairs. Labels are local and are
183
+ never written to Google.
184
+
185
+ ### Saved reports
186
+
187
+ Save a reusable Data API definition:
188
+
189
+ ```yaml
190
+ reports:
191
+ purchases_by_country:
192
+ dimensions: [country, eventName]
193
+ metrics: [eventCount, totalUsers]
194
+ dimension_filter:
195
+ field: eventName
196
+ match_type: exact
197
+ value: purchase
198
+ order_bys:
199
+ - metric: eventCount
200
+ desc: true
201
+ limit: 500
202
+ ```
203
+
204
+ `kind` defaults to `standard`. Standard reports default to the previous 28
205
+ complete days. A realtime recipe uses `kind: realtime` and cannot contain date
206
+ ranges or offsets.
207
+
208
+ Definitions can contain nested boolean filters, in-list and null filters,
209
+ numeric comparisons, up to four date ranges, and up to ten order clauses.
210
+ Runtime validation is stricter than YAML shape alone: filters and ordering
211
+ must reference selected fields. See [Reports](reports.md).
212
+
213
+ ### Health policy
214
+
215
+ ```yaml
216
+ health:
217
+ expected_events: [page_view, purchase]
218
+ minimum_sessions: 100
219
+ traffic_drop_percent: 40
220
+ key_event_drop_percent: 30
221
+ unassigned_percent: 10
222
+ not_set_percent: 10
223
+ ```
224
+
225
+ Every field is optional. See [Analytics health](health.md) for defaults and
226
+ meaning.
227
+
228
+ ## Optional experimental read features
229
+
230
+ Experimental sections require the literal acknowledgement
231
+ `experimental: true`. They never become ordinary plan operations.
232
+
233
+ ### Governance
234
+
235
+ ```yaml
236
+ governance:
237
+ experimental: true
238
+ enhanced_measurement_required: true
239
+ email_redaction_required: true
240
+ reporting_identity: blended
241
+ bigquery_link_required: true
242
+ expected_channel_groups:
243
+ - Primary channel group
244
+ ```
245
+
246
+ The governance command reads Alpha Admin resources and checks only the
247
+ expectations present here. See [Governance checks](governance.md).
248
+
249
+ ### BigQuery and funnels
250
+
251
+ ```yaml
252
+ bigquery:
253
+ experimental: true
254
+ project_id: example-analytics-project
255
+ dataset: analytics_123456789
256
+ location: US
257
+ maximum_bytes_billed: 1000000000
258
+
259
+ funnels:
260
+ checkout:
261
+ experimental: true
262
+ steps:
263
+ - name: Viewed product
264
+ event_name: view_item
265
+ - name: Purchased
266
+ event_name: purchase
267
+ ```
268
+
269
+ BigQuery is an optional dependency and every query is cost-capped. Funnel
270
+ names are local. Each funnel has 2 through 10 unique, ordered steps. See
271
+ [BigQuery reports and funnels](bigquery.md).
272
+
160
273
  ## Environment variables
161
274
 
162
275
  Only the literal form `${UPPER_CASE_NAME}` is expanded, and interpolation
@@ -10,6 +10,10 @@ second HTTP stack and does not expose generated Google objects publicly.
10
10
  | `google-analytics-admin` | `~> 0.8.0` | 0.8.0 |
11
11
  | `google-analytics-data` | `~> 0.9.0` | 0.9.0 |
12
12
  | `googleauth` | `~> 1.12` | 1.17.1 |
13
+ | `mcp` | `~> 0.25.0` | 0.25.0 |
14
+
15
+ Optional BigQuery support uses `google-cloud-bigquery ~> 1.64`. It is not a
16
+ runtime dependency and is never loaded by normal Analytics commands.
13
17
 
14
18
  The reviewed lockfile resolves these generated transports:
15
19
 
@@ -44,11 +48,27 @@ protobuf classes for:
44
48
  - custom-metric restricted cost/revenue enum translation
45
49
  - update web data-stream default URI
46
50
  - `run_report`, `batch_run_reports`, and `run_realtime_report`
51
+ - recursive Data API filters, metadata, and `check_compatibility`
52
+ - Enhanced Measurement and data-redaction settings
53
+ - reporting identity and attribution settings
54
+ - channel groups, calculated metrics, event create/edit rules, and BigQuery
55
+ links
56
+ - bounded account/property change history
57
+ - aggregate Data Access reports
47
58
 
48
59
  The tests also prove enum mappings and normalization into immutable gem-owned
49
60
  values without making network requests. Service-account tests exercise the
50
61
  official `Google::Auth::ServiceAccountCredentials` loader with generated fake
51
- keys and verify the exact read/edit scopes.
62
+ keys and verify the exact Analytics read/edit and optional BigQuery read-only
63
+ scopes.
64
+
65
+ Optional BigQuery contract tests prove fixed parameterized SQL, dry-run
66
+ estimation, `maximum_bytes_billed`, location selection, bounded result
67
+ normalization, and aggregate funnel output without contacting Google.
68
+
69
+ MCP contract tests use the official Ruby SDK to prove the exact tool allowlist,
70
+ strict input validation, structured output, redacted errors, lazy credential
71
+ loading, and read-only/non-destructive annotations.
52
72
 
53
73
  ## Updating a Google client
54
74
 
@@ -0,0 +1,93 @@
1
+ # Governance checks
2
+
3
+ Governance commands read broader GA4 administration settings. They are
4
+ experimental because Google exposes several of these resources through its
5
+ Alpha Admin API. They do not change GA4.
6
+
7
+ Normal reporting does not require this feature.
8
+
9
+ ## Governance audit
10
+
11
+ ```bash
12
+ analytics-ops governance
13
+ analytics-ops governance --json
14
+ ```
15
+
16
+ The snapshot includes:
17
+
18
+ - Enhanced Measurement and data-redaction settings for each web stream
19
+ - reporting identity and attribution settings
20
+ - channel groups and calculated metrics
21
+ - event create and edit rules for each web stream
22
+ - existing BigQuery links
23
+
24
+ Add only the expectations you care about:
25
+
26
+ ```yaml
27
+ profiles:
28
+ production:
29
+ property_id: "123456789"
30
+
31
+ governance:
32
+ experimental: true
33
+ enhanced_measurement_required: true
34
+ email_redaction_required: true
35
+ query_parameter_redaction_required: true
36
+ reporting_identity: blended
37
+ bigquery_link_required: true
38
+ expected_channel_groups:
39
+ - Primary channel group
40
+ expected_calculated_metrics:
41
+ - Average order value
42
+ expected_event_create_rules:
43
+ - purchase_completed
44
+ expected_event_edit_rules:
45
+ - Normalize campaign
46
+ ```
47
+
48
+ `experimental: true` is required so Alpha behavior is never enabled by
49
+ accident. Exit status `2` means a configured expectation did not match.
50
+
51
+ ## Change history
52
+
53
+ ```bash
54
+ analytics-ops changes
55
+ ```
56
+
57
+ Google requires the `analytics.edit` OAuth scope to read change history even
58
+ though this command performs no write. Analytics Ops requests that scope only
59
+ for this command and guarded `apply`.
60
+
61
+ The result is deliberately de-identified. It returns the time, actor type,
62
+ changed resource, and action. It omits user email addresses and full
63
+ before/after resource bodies. Up to 100 recent events from the previous 30
64
+ days are requested.
65
+
66
+ ## Aggregate Data API access
67
+
68
+ ```bash
69
+ analytics-ops access
70
+ ```
71
+
72
+ This requests only `accessDateHour` and
73
+ `dataApiQuotaPropertyTokensConsumed`. Those aggregate fields work with
74
+ ordinary GA4 properties and do not request user email or IP address.
75
+
76
+ Google limits Data Access reports to GA4 Administrators. A Viewer or Editor
77
+ service account may therefore receive a permission error for this one command.
78
+ Normal reports, health, audit, and governance do not need Administrator.
79
+
80
+ ## Safety and limitations
81
+
82
+ - These commands never create, update, delete, or reorder resources.
83
+ - Event rules are read from each web stream, not from the property root.
84
+ - Alpha methods can change or disappear in a future Google client.
85
+ - A failed capability stops the governance snapshot instead of pretending the
86
+ missing data is compliant.
87
+ - `apply` does not manage any of these experimental settings.
88
+
89
+ Official references:
90
+
91
+ - [Admin API overview](https://developers.google.com/analytics/devguides/config/admin/v1)
92
+ - [Change history](https://developers.google.com/analytics/devguides/config/admin/v1/rest/v1alpha/accounts/searchChangeHistoryEvents)
93
+ - [Data Access reports](https://developers.google.com/analytics/devguides/config/admin/v1/access-api)
data/docs/health.md ADDED
@@ -0,0 +1,82 @@
1
+ # Analytics health
2
+
3
+ `health` is a read-only, deterministic check of whether analytics looks
4
+ healthy. It does not use AI to invent a score, and it never changes GA4.
5
+
6
+ Start with:
7
+
8
+ ```bash
9
+ analytics-ops health
10
+ ```
11
+
12
+ It compares the previous 28 complete days with the 28 days before them and
13
+ checks:
14
+
15
+ - minimum traffic
16
+ - large session or key-event drops
17
+ - unusually high `Unassigned` or `(not set)` acquisition traffic
18
+ - events you expected but did not receive
19
+ - Google thresholding or sampling notices
20
+ - low remaining Data API quota
21
+ - drift in configuration managed by Analytics Ops
22
+
23
+ Exit status `0` means no configured problem was found. Status `2` means one or
24
+ more findings need review; it is not a crash.
25
+
26
+ ## Choose the period
27
+
28
+ ```bash
29
+ analytics-ops health --last 7
30
+ analytics-ops health --from 2026-07-01 --to 2026-07-14
31
+ ```
32
+
33
+ Health always compares the selected period with an equally long preceding
34
+ period, even when `--compare` is not written.
35
+
36
+ ## Set your own policy
37
+
38
+ All settings are optional:
39
+
40
+ ```yaml
41
+ version: 1
42
+
43
+ profiles:
44
+ production:
45
+ property_id: "123456789"
46
+
47
+ health:
48
+ expected_events:
49
+ - page_view
50
+ - purchase
51
+ minimum_sessions: 100
52
+ traffic_drop_percent: 40
53
+ key_event_drop_percent: 30
54
+ unassigned_percent: 10
55
+ not_set_percent: 10
56
+ ```
57
+
58
+ Percent values are from `0` through `100`. A zero drop threshold means “warn
59
+ about any real drop”; equal periods do not produce a false warning.
60
+
61
+ Choose expectations that fit the property. A new site may legitimately have
62
+ few sessions, and a rare purchase event may not occur every week.
63
+
64
+ ## Check every property
65
+
66
+ ```bash
67
+ analytics-ops portfolio health
68
+ analytics-ops portfolio health --last 7 --concurrency 4
69
+ ```
70
+
71
+ Each property uses its own profile and named connection. One inaccessible
72
+ property is reported as an isolated error instead of hiding results from the
73
+ others. Concurrency is bounded from `1` through `8`.
74
+
75
+ ## Important limits
76
+
77
+ Health is an operational signal, not proof of revenue, consent, or tracking
78
+ correctness. GA4 can delay, model, sample, threshold, or omit data. The checks
79
+ cannot see visitors blocked before an event reaches Google.
80
+
81
+ Use `overview`, `report`, and your tagging debugger to investigate a finding.
82
+ Use `audit` to inspect configuration drift in detail.
@@ -46,7 +46,7 @@ gem "analytics_ops",
46
46
  ```
47
47
 
48
48
  Run `bundle install`. `bundle info analytics_ops` must show the local checkout
49
- and version `0.2.0`.
49
+ and version `0.3.0`.
50
50
 
51
51
  ### Preserve an existing configuration
52
52
 
@@ -72,9 +72,10 @@ bundle exec analytics-ops setup \
72
72
  --service-account /absolute/path/outside/repositories/service-account.json
73
73
  ```
74
74
 
75
- Analytics Ops validates the key, verifies both APIs, and remembers only its
76
- absolute path in `~/.config/analytics_ops/connection.json` with mode `0600`.
77
- The key is not copied.
75
+ Analytics Ops validates the key, verifies both APIs, and remembers a named
76
+ connection containing only its absolute path in
77
+ `~/.config/analytics_ops/connection.json` with mode `0600`. The key is not
78
+ copied.
78
79
 
79
80
  Run plain setup once more to verify the remembered connection:
80
81
 
@@ -89,6 +90,8 @@ bundle exec analytics-ops version
89
90
  bundle info analytics_ops
90
91
  bundle exec analytics-ops doctor
91
92
  bundle exec analytics-ops overview
93
+ bundle exec analytics-ops overview --last 7 --compare
94
+ bundle exec analytics-ops portfolio
92
95
  bundle exec analytics-ops report traffic --json
93
96
  bundle exec analytics-ops realtime
94
97
  bundle exec analytics-ops audit
@@ -127,15 +130,15 @@ service account if Analytics Ops will remain in use. For a temporary-only
127
130
  test, remove the connection file and revoke the downloaded key in Google
128
131
  Cloud.
129
132
 
130
- Keep the local path dependency until `0.2.0` is published. After publication,
133
+ Keep the local path dependency until `0.3.0` is published. After publication,
131
134
  replace it with:
132
135
 
133
136
  ```ruby
134
- gem "analytics_ops", "~> 0.2", group: :development
137
+ gem "analytics_ops", "~> 0.3", group: :development
135
138
  ```
136
139
 
137
140
  Run `bundle install`, then confirm `bundle info analytics_ops` resolves
138
- version `0.2.0` from RubyGems.
141
+ version `0.3.0` from RubyGems.
139
142
 
140
143
  ## Release gate
141
144
 
@@ -146,13 +149,13 @@ Require all of the following:
146
149
  - `bin/check`, package inspection, and `git diff --check` passed.
147
150
  - Documentation and changelog describe the released behavior.
148
151
  - The gem worktree is clean and the release commit is on `origin/master`.
149
- - Version and changelog both say `0.2.0`.
152
+ - Version and changelog both say `0.3.0`.
150
153
 
151
154
  Then create and push the annotated tag:
152
155
 
153
156
  ```bash
154
- git tag -a v0.2.0 -m "Release Analytics Ops 0.2.0"
155
- git push origin v0.2.0
157
+ git tag -a v0.3.0 -m "Release Analytics Ops 0.3.0"
158
+ git push origin v0.3.0
156
159
  ```
157
160
 
158
161
  The tag workflow verifies and publishes through RubyGems Trusted Publishing.
data/docs/rails.md CHANGED
@@ -14,10 +14,9 @@ Active Support.
14
14
 
15
15
  ```bash
16
16
  bundle install
17
+ bin/rails generate analytics_ops:install
17
18
  bundle exec analytics-ops setup \
18
- --profile development \
19
19
  --service-account /absolute/path/to/service-account.json
20
- bin/rails generate analytics_ops:install
21
20
  ```
22
21
 
23
22
  The generator creates only:
@@ -25,26 +24,39 @@ The generator creates only:
25
24
  - `config/analytics_ops.yml`
26
25
  - `bin/analytics-ops` with executable mode
27
26
 
28
- If setup already created the configuration, the generator preserves it and
29
- adds only the binstub. It never asks to overwrite the selected property.
27
+ The order is flexible:
28
+
29
+ - Generator first: setup safely replaces the untouched placeholder.
30
+ - Setup first: the generator preserves the valid configuration and adds only
31
+ the binstub.
30
32
 
31
- The generated file has a minimal `development` profile and a fake-safe
32
- property-ID placeholder. It contains no active stream, retention, key-event,
33
- dimension, or metric policy, so installation alone cannot produce a mutation
34
- plan. Add managed settings only after reviewing the configuration guide. Add
35
- a profile matching another Rails environment, or set `ANALYTICS_OPS_PROFILE`.
33
+ The generated file has the same minimal `production` profile used by the CLI.
34
+ It contains no active stream, retention, key-event, dimension, or metric
35
+ policy, so installation alone cannot produce a mutation plan. Add managed
36
+ settings only after reviewing the configuration guide.
36
37
 
37
38
  ## Rake tasks
38
39
 
39
40
  ```bash
40
41
  bin/rake analytics:doctor
41
42
  bin/rake analytics:overview
43
+ bin/rake analytics:portfolio
44
+ bin/rake analytics:health
45
+ bin/rake analytics:governance
46
+ bin/rake analytics:access
42
47
  bin/rake analytics:audit
43
48
  bin/rake analytics:plan
44
49
  bin/rake analytics:verify
45
50
  bin/rake 'analytics:report[traffic_acquisition]'
51
+ bin/rake 'analytics:raw[event_counts]'
52
+ bin/rake 'analytics:funnel[checkout]'
46
53
  ```
47
54
 
55
+ `raw` and `funnel` require the optional BigQuery configuration and
56
+ `google-cloud-bigquery` dependency. `governance` and `access` are experimental
57
+ read-only Admin API tasks. See [Governance checks](governance.md) and
58
+ [BigQuery reports and funnels](bigquery.md).
59
+
48
60
  `analytics:plan` writes `tmp/analytics_ops-plan.json`. Override task inputs
49
61
  without changing application code:
50
62
 
@@ -55,6 +67,10 @@ ANALYTICS_OPS_PLAN=tmp/production-ga4-plan.json \
55
67
  bin/rake analytics:plan
56
68
  ```
57
69
 
70
+ Rails tasks use the profile selected by `analytics-ops use`. Set
71
+ `ANALYTICS_OPS_PROFILE` only for an explicit one-run override; Rails
72
+ environment names no longer silently select a different profile.
73
+
58
74
  There is intentionally no `analytics:apply` Rake task. Apply a reviewed file
59
75
  with the explicit CLI:
60
76