open-agent-guide 0.5.0__tar.gz

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.
@@ -0,0 +1,212 @@
1
+ ### Python template
2
+ # Byte-compiled / optimized / DLL files
3
+ __pycache__/
4
+ *.py[cod]
5
+ *$py.class
6
+
7
+ # C extensions
8
+ *.so
9
+
10
+ # Distribution / packaging
11
+ .Python
12
+ build/
13
+ develop-eggs/
14
+ dist/
15
+ downloads/
16
+ eggs/
17
+ .eggs/
18
+ lib/
19
+ lib64/
20
+ parts/
21
+ sdist/
22
+ var/
23
+ wheels/
24
+ *.egg-info/
25
+ .installed.cfg
26
+ *.egg
27
+
28
+ # PyInstaller
29
+ *.manifest
30
+ *.spec
31
+
32
+ # Installer logs
33
+ pip-log.txt
34
+ pip-delete-this-directory.txt
35
+
36
+ # Unit test / coverage reports
37
+ htmlcov/
38
+ .tox/
39
+ .coverage
40
+ .coverage.*
41
+ .cache
42
+ nosetests.xml
43
+ coverage.xml
44
+ *.cover
45
+ .hypothesis/
46
+
47
+ # Translations
48
+ *.mo
49
+ *.pot
50
+
51
+ # Django stuff
52
+ staticfiles/
53
+
54
+ # Sphinx documentation
55
+ docs/_build/
56
+
57
+ # PyBuilder
58
+ target/
59
+
60
+ # Environments
61
+ .venv
62
+ venv/
63
+ ENV/
64
+
65
+ # Rope project settings
66
+ .ropeproject
67
+
68
+ # mkdocs documentation
69
+ /site
70
+
71
+ # mypy
72
+ .mypy_cache/
73
+
74
+ ### Node template
75
+ # Logs
76
+ logs
77
+ *.log
78
+ npm-debug.log*
79
+ yarn-debug.log*
80
+ yarn-error.log*
81
+
82
+ # Runtime data
83
+ pids
84
+ *.pid
85
+ *.seed
86
+ *.pid.lock
87
+
88
+ # Directory for instrumented libs generated by jscoverage/JSCover
89
+ lib-cov
90
+
91
+ # Coverage directory used by tools like istanbul
92
+ coverage
93
+
94
+ # nyc test coverage
95
+ .nyc_output
96
+
97
+ # Bower dependency directory
98
+ bower_components
99
+
100
+ # node-waf configuration
101
+ .lock-wscript
102
+
103
+ # Compiled binary addons
104
+ build/Release
105
+
106
+ # Dependency directories
107
+ node_modules/
108
+ jspm_packages/
109
+
110
+ # Built frontend bundle — generated from frontend/webmcp.js by `npm run build`
111
+ # at image-build time (ADR-0013); never committed. See package.json.
112
+ open_agent_guide/static/js/webmcp.bundle.js
113
+
114
+ # Typescript v1 declaration files
115
+ typings/
116
+
117
+ # Optional npm cache directory
118
+ .npm
119
+
120
+ # Optional eslint cache
121
+ .eslintcache
122
+
123
+ # Optional REPL history
124
+ .node_repl_history
125
+
126
+ # Output of 'npm pack'
127
+ *.tgz
128
+
129
+ # Yarn Integrity file
130
+ .yarn-integrity
131
+
132
+ ### Linux template
133
+ *~
134
+ .fuse_hidden*
135
+ .directory
136
+ .Trash-*
137
+ .nfs*
138
+
139
+ ### VisualStudioCode template
140
+ .vscode/*
141
+ !.vscode/settings.json
142
+ !.vscode/tasks.json
143
+ !.vscode/launch.json
144
+ !.vscode/extensions.json
145
+ *.code-workspace
146
+
147
+ # Local History for devcontainer
148
+ .devcontainer/bash_history
149
+
150
+ # Provided default Pycharm Run/Debug Configurations should be tracked by git.
151
+ ### JetBrains template
152
+ .idea/**/workspace.xml
153
+ .idea/**/tasks.xml
154
+ .idea/dictionaries
155
+ .idea/**/dataSources/
156
+ .idea/**/dataSources.ids
157
+ .idea/**/dataSources.xml
158
+ .idea/**/dataSources.local.xml
159
+ .idea/**/sqlDataSources.xml
160
+ .idea/**/dynamic.xml
161
+ .idea/**/uiDesigner.xml
162
+ .idea/**/gradle.xml
163
+ .idea/**/libraries
164
+ cmake-build-debug/
165
+ .idea/**/mongoSettings.xml
166
+ *.iws
167
+ out/
168
+ .idea_modules/
169
+ atlassian-ide-plugin.xml
170
+ .idea/replstate.xml
171
+ com_crashlytics_export_strings.xml
172
+ crashlytics.properties
173
+ crashlytics-build.properties
174
+ fabric.properties
175
+
176
+ ### Windows template
177
+ Thumbs.db
178
+
179
+ ### Configuration
180
+ .env
181
+ # Local-only reference copies of production values (admin URL, etc.). Never
182
+ # commit these; .env.sample stays tracked because it doesn't match this pattern.
183
+ .env.*.local
184
+
185
+ ### Playwright MCP
186
+ .playwright-mcp/
187
+
188
+ ### Claude Code worktrees
189
+ # Agents using `isolation: worktree` materialize a sibling worktree here;
190
+ # its files belong to the linked branch, not the main checkout.
191
+ .claude/worktrees/
192
+
193
+ ### UI screenshots (agent-generated, not tracked)
194
+ screenshots/*
195
+ !screenshots/.gitkeep
196
+
197
+ ### Impeccable design tooling
198
+ # Commit the shared detector config (team-wide rule/file/value waivers so
199
+ # documented false positives aren't re-litigated in each checkout/worktree).
200
+ # Ignore per-developer state and regenerable caches (hook cache, critique
201
+ # snapshots, live-mode config, and the local per-developer override file).
202
+ .impeccable/*
203
+ !.impeccable/config.json
204
+ # Impeccable also writes per-directory hook caches when it runs against a
205
+ # nested skill bundle; those are regenerable and never committed.
206
+ **/.impeccable/hook.cache.json
207
+
208
+ # Built Tom Select JS/CSS and license notices (ADR-0054).
209
+ open_agent_guide/static/bundles/
210
+
211
+ # Local duplication reports contain source fragments.
212
+ /reports/jscpd/
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Goshawk Solutions, LLC
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,330 @@
1
+ Metadata-Version: 2.5
2
+ Name: open-agent-guide
3
+ Version: 0.5.0
4
+ Summary: Open Agent Guide submission client and CLI
5
+ Project-URL: Documentation, https://www.openagentguide.com/developers/
6
+ Project-URL: Homepage, https://www.openagentguide.com
7
+ Author-email: "Goshawk Solutions, LLC" <support@openagentguide.com>
8
+ License-Expression: MIT
9
+ License-File: LICENSE
10
+ Classifier: Programming Language :: Python :: 3 :: Only
11
+ Classifier: Programming Language :: Python :: 3.12
12
+ Classifier: Programming Language :: Python :: 3.13
13
+ Classifier: Programming Language :: Python :: 3.14
14
+ Requires-Python: >=3.12
15
+ Requires-Dist: httpx<0.29,>=0.28.1
16
+ Requires-Dist: typer<1,>=0.20
17
+ Description-Content-Type: text/markdown
18
+
19
+ # Open Agent Guide client
20
+
21
+ `oag` 0.3.1 requests guidance automatically. For direct API reads, request
22
+ `guidance` alongside `status`, `resolution`, and `check_feedback` when reading
23
+ your submissions. `guidance.next_action=wait` means keep watching;
24
+ `prepare_correction` means prepare the repair now but wait for terminal closure
25
+ before submitting it. `revise_and_resubmit` means inspect the rejection, correct
26
+ the proposal, then validate against the current catalog. `none` means read the
27
+ terminal resolution, including any successor links. Guidance never reserves an
28
+ identity or bypasses create conflicts. If it is absent, keep using lifecycle
29
+ status; never infer rejection from a check error. Preserve submit/watch
30
+ journals.
31
+
32
+ `oag` validates, submits, and watches immutable catalog proposals. The Python
33
+ library runs the same workflows without a subprocess or Django installation.
34
+
35
+ ## Install and upgrade
36
+
37
+ Supported platforms are macOS and Linux with Python 3.12, 3.13, or 3.14. Obtain
38
+ the versioned wheel from your administrator, then install it:
39
+
40
+ ```sh
41
+ uv tool install --force ./open_agent_guide-*.whl
42
+ # Upgrade explicitly when given a newer wheel:
43
+ uv tool install --force ./open_agent_guide-*.whl
44
+ ```
45
+
46
+ For library use, `uv add ./open_agent_guide-*.whl`. Maintainers build with
47
+ `uv build --package open-agent-guide` and can install from
48
+ `./packages/oag-client`. CI smoke-tests clean wheel installation on both
49
+ supported operating systems. The package is MIT-licensed. Public PyPI
50
+ publication as `open-agent-guide` is planned; until then contact
51
+ <support@openagentguide.com> for distribution. Windows and standalone
52
+ executables are not supported yet.
53
+
54
+ Package versions follow semantic versioning. JSON output remains version 1; new
55
+ and saved journals use version 2. Version-1 journals remain readable, but
56
+ unkeyed uncertain rows require manual reconciliation. Older clients reject
57
+ version-2 journals. Upgrading alone never sends requests or rewrites journals;
58
+ explicitly running `submit` resumes keyed uncertain rows. Use oag-client 0.2.0
59
+ or newer with a server that supports the create `Idempotency-Key` contract.
60
+
61
+ ## Commands
62
+
63
+ Export `OAG_TOKEN` with your bearer token. `OAG_BASE_URL` defaults to
64
+ `https://www.openagentguide.com`; set it to your staging origin for testing. The
65
+ base URL cannot contain credentials, a path, query, or fragment. Bearer tokens
66
+ never appear in transport diagnostics. Redirects are not followed.
67
+
68
+ ```sh
69
+ oag --version
70
+ oag validate batch.json --output json
71
+ cat batch.json | oag validate - --output jsonl
72
+ oag submit batch.json --journal batch.journal.json --output json
73
+ oag status 00000000-0000-0000-0000-000000000001 --output json
74
+ oag watch --file ids.txt --journal watch.journal.json --timeout 900 --output jsonl
75
+ oag watch 00000000-0000-0000-0000-000000000001 --journal watch-one.json
76
+ oag retire 00000000-0000-0000-0000-000000000001 --force --reason "stale"
77
+ oag retire 00000000-0000-0000-0000-000000000001 --force --successor UUID --output json
78
+ oag get /api/v1/catalog/products/?page_size=50
79
+ oag get https://www.openagentguide.com/api/v1/catalog/products/?page=2
80
+ ```
81
+
82
+ `get PATH` reads one catalog or submissions read-API path through the shared
83
+ read pacing lane and prints the JSON body. `PATH` is a relative read-API path or
84
+ a same-origin absolute URL, so a pagination `next` link can be passed straight
85
+ back without stripping its origin. An absolute `PATH` is accepted only when its
86
+ scheme and host match `OAG_BASE_URL` with no userinfo; a foreign origin,
87
+ userinfo, an unsupported scheme, or a path outside the catalog or submissions
88
+ read API exits 2 without a request. Catalog reads work anonymously; the
89
+ submissions field contract needs `OAG_TOKEN`.
90
+
91
+ `oag --version` (`-V`) prints the installed version and exits.
92
+
93
+ `validate` and `submit` accept one JSON object or an array, from a file or `-`
94
+ for stdin. Nested relation envelopes and explicit nulls pass through unchanged;
95
+ omitted keys stay omitted. Validation calls the server and returns its
96
+ field-path errors. Validation does not probe external citation URLs; the
97
+ existing batch preflight script remains available for that separate check.
98
+
99
+ Every command accepts `--output text|json|jsonl`. JSON contains `version`,
100
+ `records`, and `summary`; JSONL emits one `type: record` line per result
101
+ followed by a `type: summary` line. Output is emitted at completion, including
102
+ watch cancellation. Version 0.3.1 emits changed next-action guidance to stderr
103
+ during watch. Text includes the complete record with indented evidence, then
104
+ lists any check feedback and absence-attestation guidance as one scannable line
105
+ each so a reader sees why a row needs work without searching the JSON.
106
+ Diagnostics go to stderr. No command prompts.
107
+
108
+ `status` fetches one submission with all checks and reviews pages. In 0.3.1,
109
+ `watch` polls unresolved IDs in batches of up to 100 using
110
+ `ids=...&page_size=100&fields=id,status,review_ready,check_feedback,guidance`.
111
+ Pending records contain this compact submission snapshot. When an ID becomes
112
+ terminal, watch fetches its full detail and all checks and reviews pages,
113
+ preserving claim verdicts, rationales, citations, and resolution reasons.
114
+ Missing batch IDs remain `failed-to-read`. The server must support the `ids`
115
+ filter. Only lifecycle `merged` means `accepted`; ordinary `closed` means
116
+ `rejected`. Closed proposals with retirement dispositions report `withdrawn` or
117
+ `superseded`, with replacement IDs in `resolution.closed.successor_ids`. A check
118
+ error on an open submission remains `pending`. A failed detail or evidence
119
+ request means `failed-to-read`, including when a terminal lifecycle has been
120
+ seen. Watch retries unresolved records in later rounds and stops revisiting
121
+ terminal records only after capturing their evidence. The journal preserves
122
+ those terminal snapshots on restart. Use a fresh journal for a fresh view of
123
+ later evidence.
124
+
125
+ `retire` withdraws or supersedes one of your open submissions through the
126
+ server's retirement operation. With no `--successor` it withdraws the proposal;
127
+ one or more `--successor UUID` options supersede it with those explicit newer
128
+ replacement IDs, and `--reason TEXT` records an optional reason. The command
129
+ makes one request and never retries or runs automatically. `--force` is
130
+ required: without it `retire` refuses, makes no request, and exits 2, so the
131
+ tool never withdraws or supersedes on your behalf. The server still enforces
132
+ author-or-curator permission and newer-only successors. A successful retire
133
+ reports the server's terminal disposition, `withdrawn` or `superseded`, and
134
+ exits 0; a 403 or validation error exits 3. The command does not poll afterward;
135
+ use `status`/`watch` to observe the retired submission and its successors.
136
+
137
+ A failed read keeps the server's structured error. Every `failed-to-read` record
138
+ carries a `failure` object with a `kind` (`http`, `deadline`, `transport`, or
139
+ `client`); an `http` failure also carries `http_status`, and the server's
140
+ `code`, `detail`, and validation `errors` when present. The human-readable
141
+ `error` string stays as well. `coverage` wraps the same object in a
142
+ `{"version": 1, "error": {...}}` envelope on failure and exits 3; its successful
143
+ report is unchanged.
144
+
145
+ ## Pacing and workflow deadlines
146
+
147
+ `--timeout` is an overall seconds budget, default 300 for validate/submit, 60
148
+ for status, and 900 for watch. It includes pacing waits, requests, retries, and
149
+ cooldowns. Watch pauses 15 seconds between rounds. Ctrl-C ends watch with a
150
+ final summary and exit 130; its journal can be resumed. Hard kills cannot emit a
151
+ summary.
152
+
153
+ The client spaces reads by 0.55 seconds and mutations by 3.5 seconds. Validation
154
+ POSTs use the mutation pace too. The `oag` CLI shares reservations and
155
+ Retry-After cooldowns across processes on one host that use the same pacing
156
+ directory, API origin, and token. The maintained population benchmark and pi
157
+ submitter also opt into this coordination. Python `Client` instances use local
158
+ pacing unless you pass `shared_pacer(base_url, token)`.
159
+
160
+ The default directory is `oag-client-pace` under the system temp directory;
161
+ `OAG_PACING_DIR` selects another directory. All cooperating processes must reach
162
+ the same directory and use the same base URL and token. The filename hashes the
163
+ origin and a one-way token fingerprint; the file stores timestamps, never the
164
+ raw token. A process crash releases the file lock, but its recorded reservations
165
+ remain. Separate directories, hosts, origins, or tokens do not share this state.
166
+ Server quotas apply account-wide across all of an account's tokens, so
167
+ coordinate those workers yourself. Shared pacing reduces collisions; it does not
168
+ guarantee that requests avoid throttling.
169
+
170
+ A transient 429 extends the shared cooldown, keeping the longest cooldown
171
+ reported by any cooperating process. Workers check it again after waiting for a
172
+ reserved slot. Numeric and HTTP-date Retry-After values are honored within the
173
+ workflow deadline. This cooldown applies to reads and mutations together. For
174
+ workers outside that shared state, pause every worker using the exhausted
175
+ `X-RateLimit-Scope` for at least `Retry-After` seconds and add jitter before
176
+ resuming. The scopes are `global_auth` for reads, and `submission` and
177
+ `submission_burst` for submission mutations. The open-submission-cap 429 returns
178
+ immediately without retrying.
179
+
180
+ Size batches against the combined workload. Fifty validation POSTs followed by
181
+ fifty create POSTs require about 350 seconds of mutation spacing alone, before
182
+ network time, retries, cooldowns, or other workers' reservations. Those are
183
+ separate command budgets: increase `--timeout` for each command based on its
184
+ share of the work, and allow additional time for watch to capture terminal
185
+ evidence. More concurrent processes do not increase shared throughput. Prefer
186
+ smaller batches, fewer simultaneous validators/submitters, and one watcher per
187
+ account with all unresolved IDs. Use the same pacing directory for concurrent
188
+ CLI validation, submission, and watch commands.
189
+
190
+ Future mutation reservations leave the read schedule unchanged. Create and
191
+ validate use the mutation lane; globally throttled writes such as review, merge,
192
+ close, and retire share the read lane. If a peer extends Retry-After during a
193
+ wait, the worker reserves a fresh slot to preserve spacing after the cooldown.
194
+ Cancelled or expired reservations remain on their lane until their slots pass;
195
+ restart preserves those reservations and cooldowns. Watch budgets also need time
196
+ for server processing, poll intervals, and terminal evidence reads across all
197
+ watchers. Shared pacing neither bounds outstanding server work nor eliminates
198
+ deadline failures. A watch timeout does not mean a submission failed: resume its
199
+ journal and inspect lifecycle and terminal evidence. Reuse the original submit
200
+ journal and idempotency keys after an uncertain create; never start a duplicate
201
+ batch to escape a deadline.
202
+
203
+ Requests have a 45-second total budget capped by the remaining workflow
204
+ deadline. Validation and keyed creates retry transport failures, 5xx, and
205
+ transient 429 responses, at most five attempts. Unkeyed writes retry only
206
+ transient 429 responses. Exhausted HTTP failures retain `http_status` alongside
207
+ the response. Polling makes one attempt per round. Keep terminal evidence in the
208
+ journal and remove those IDs from future polls.
209
+
210
+ A pacing timeout reports the lane, reserved wait, remaining budget, and requests
211
+ sent by that client. Submit journals retain this diagnostic with the original
212
+ idempotency key. Watch adds `deadline_error` to unresolved records and saves it
213
+ in the watch journal, preserving earlier read errors and submission snapshots.
214
+ Resume the same journal with a larger budget after investigating shared load.
215
+ Never clear the pacing file to bypass a cooldown or delete the journal to retry
216
+ uncertain submissions.
217
+
218
+ | Exit | Meaning |
219
+ | ---- | -------------------------------------------------------------------- |
220
+ | 0 | Validation/submission succeeded, or all watched records are terminal |
221
+ | 2 | Invalid arguments, configuration, input, or journal |
222
+ | 3 | At least one failed/ambiguous batch row or failed-to-read record |
223
+ | 4 | Pending records remain at the deadline or in one-shot status |
224
+ | 5 | All records were read, but `--require-all-merged` found a rejection |
225
+ | 130 | Cancelled by Ctrl-C |
226
+
227
+ Failures take precedence over pending records, which take precedence over
228
+ `--require-all-merged`. By default, a fully read rejection exits 0: watching has
229
+ completed. Use `--require-all-merged` on status/watch to require acceptance.
230
+ Summaries always distinguish accepted, rejected, pending, and failed-to-read;
231
+ batch summaries also count valid, created, failed, and ambiguous rows as
232
+ present.
233
+
234
+ ## Resume and reconcile
235
+
236
+ Always reuse the same submit journal for the same batch. It binds the ordered
237
+ input hash, base URL, and token fingerprint; changed inputs, environments, or
238
+ tokens fail rather than replay. Tokens themselves are never stored. The journal
239
+ contains server evidence and may contain private submission data. Keep it
240
+ private.
241
+
242
+ Each new row gets a random UUID `Idempotency-Key`, flushed to the journal before
243
+ its first POST. Every attempt and restart reuses that key and the same input. A
244
+ confirmed `201` records the original submission ID as `created`, even if the
245
+ server has already merged or closed it. Confirmed and definitively failed rows
246
+ are skipped on restart. Keyed ambiguous or pending rows are retried within the
247
+ command's deadline. Exhausted transient failures remain unresolved.
248
+
249
+ Journal writes are atomic and flushed to disk. A lock prevents simultaneous
250
+ writers; after a hard kill, verify no writer remains before deleting the `.lock`
251
+ file. Never delete a journal or change its keys to recover from a timeout. A
252
+ deadline or cancellation after an uncertain attempt preserves its key.
253
+
254
+ Older entries without keys cannot be replayed safely. Inspect your submissions
255
+ on the server, including all statuses, and compare the complete payload and
256
+ submitter. After positively identifying an ID, associate it with the input row:
257
+
258
+ ```sh
259
+ oag reconcile batch.json --journal batch.journal.json --row 2 --id UUID
260
+ ```
261
+
262
+ This checks that the ID is readable; the operator verifies the row-to-ID match.
263
+ It does not create or edit a server submission. If no match can be found,
264
+ absence is not proof of failure. Reconcile with an administrator before creating
265
+ a new batch and journal. A server `409 idempotency_key_reused` also requires
266
+ inspection, not a fresh key for the uncertain write. The effective callback URL
267
+ must remain unchanged for a retry; changing a token's default callback can cause
268
+ that conflict.
269
+
270
+ For a definitive failed row, correct the payload and explicitly submit a new
271
+ batch with a new journal. Existing submissions are immutable. To supersede a
272
+ stalled open create, inspect the existing submission first, then explicitly set
273
+ `"force": true` in a new payload and submit with a new journal. The client never
274
+ adds `force` or edits an existing proposal. Preserve prior journals for audit.
275
+
276
+ ## Python API
277
+
278
+ ```python
279
+ from oag_client import Client, shared_pacer
280
+ from oag_client.transport import HTTPTransport
281
+ from oag_client.workflows import payloads, validate
282
+
283
+ base_url = "https://www.openagentguide.com"
284
+ # Omit `pacer` for in-process pacing; pass `shared_pacer` to coordinate with
285
+ # other processes on this host that share the token.
286
+ client = Client(HTTPTransport(token), base_url, pacer=shared_pacer(base_url, token))
287
+ records = validate(client, payloads(json_text), deadline=client.clock.monotonic() + 60)
288
+ ```
289
+
290
+ `submit`, `status`, `watch`, `summary`, and `exit_code` are also available from
291
+ `oag_client.workflows`; `Journal` lives in `oag_client.journal`. `Clock` and
292
+ `Transport` protocols allow deterministic testing. Custom transports must accept
293
+ the optional `idempotency_key` keyword and forward it as `Idempotency-Key` for
294
+ create requests. The HTTP transport is a synchronous entry point; async callers
295
+ should use a worker thread. Application code supplies authorization. Pacing is
296
+ in-process by default; pass `shared_pacer(base_url, token)` (or any `Pacer`) to
297
+ coordinate processes using the same directory, origin, and token on one host.
298
+ Coordinate other tokens or hosts yourself because server quotas cover the whole
299
+ account.
300
+
301
+ ### Trusted bulk read pacing
302
+
303
+ The default read spacing is 0.55 seconds. If a maintainer has granted your
304
+ account `submissions.bulk_submit_catalogsubmission`, set `OAG_READ_SPACING=0.11`
305
+ for the CLI, or pass `read_spacing=0.11` to
306
+ `shared_pacer(base_url, token, ...)`. This targets about 545 reads/minute under
307
+ the server's default 600/minute trusted read quota. The server setting is
308
+ `BULK_AUTHENTICATED_READ_RATE`.
309
+
310
+ Use the same spacing, token, and `OAG_PACING_DIR` for coordinated workers on one
311
+ host. Keep status reads batched through `watch`; more quota doesn't require
312
+ polling each submission separately. Separate tokens for one account share the
313
+ server quota but need caller-managed pacing coordination. After permission
314
+ revocation, restore 0.55 seconds. The server checks permission on each request
315
+ and clients continue to honor shared Retry-After cooldowns.
316
+
317
+ Create/validate spacing stays at 3.5 seconds. Review, merge, close, and retire
318
+ writes keep a separate 0.55-second minimum interval even with faster reads.
319
+ Custom `Pacer.reserve` implementations must accept `global_mutation=False`.
320
+
321
+ ## Research an existing product's gaps
322
+
323
+ Use `oag coverage PRODUCT --output json` for one compact report of the product's
324
+ coverage and unknown scored fields. The default text output includes evidence
325
+ guidance. The equivalent reads are
326
+ `GET /api/v1/catalog/products/{uuid-or-slug}/coverage/` and the MCP tool
327
+ `get_product_coverage`. Read each gap's shared semantics, schema reference, and
328
+ absence page class before drafting. Keep unsupported values unknown. A confirmed
329
+ absence can improve coverage without improving the AI-native score. Use
330
+ submission validation to inspect projected changes after collecting evidence.