Repository navigation
feat(tools): expose the 14 operations ledger v8 deferred — history, tools, drilling, analytics (#195) - #200
Conversation
…rage, macro calendar (#195) - opa_get_continuous_futures_history: GET /v1/futures/continuous/{brent,wti}/historical. States the bars are revisable public snapshots, not settlements, and any entitlement start-date cap the API applied. - opa_get_futures_history gains contract=gasoil: GET /v1/futures/ice-gasoil/historical (USD per tonne). - opa_get_natural_gas_storage: GET /v1/natural-gas/storage (plan-gated, 403; weeks bounded 1-52 by the tool). - opa_get_macro_releases: GET /v1/macro/releases with the calendar coverage caveat. Ledger 8 -> 9 (this family's five rows), catalog entries, live-smoke shapes, README rows, tool counts 43/39. Verified live 2026-11-10 with the Scale smoke key. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01B3sTPV81755NVtvVwb9Cd8
…status (#195) - opa_calculate_fuel_surcharge: GET /v1/fuel-surcharge/calculate (percent or per_mile, seven EIA regions, 1-52 weeks; checks per-method inputs first; states a stale index). - opa_test_price_alert: POST /v1/alerts/{alert_id}/test. Read scope, automation category; dry run that sends nothing, described as metered. Covered in the live price-alert lifecycle on the synthetic alert. - opa_get_service_status: GET /v1/status. Keyless, core category, entitlement none; a 503 is reported as health unknown, not retried. Ledger v9 rows for this family (same v9 change entry), catalog entries, README rows, tool counts 46/42. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01B3sTPV81755NVtvVwb9Cd8
- opa_list_well_permits: GET /v1/ei/well-permits (states, permit-date range, page, per_page 1-100; Well Permits subscription, 403 otherwise). - opa_preview_well_permits: GET /v1/ei/well-permits/preview (one state, 1-90 days, at most 25 rows; any key including Free; renders as_of, data age, stale flag and per-state date coverage). Ledger v9 rows for this family (same v9 change entry), catalog entries, README rows, tool counts 48/44. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01B3sTPV81755NVtvVwb9Cd8
…n, spread (#195) - opa_get_price_statistics: GET /v1/analytics/statistics. - opa_get_price_trend: GET /v1/analytics/trend (analysis, sma, ema, rsi, levels). - opa_get_price_correlation: GET /v1/analytics/correlation (analysis, rolling, matrix of 2-10 codes). - opa_get_spread_statistics: GET /v1/analytics/spread (list, current, historical). Plan gate stated as an entitlement gate (HTTP 403); the 403 sample_data is never rendered; the body's `tier` field is not rendered (reads "free" for Gen-2 paid plans, oilpriceapi-api#10761). Ledger v9 rows for this family (same v9 change entry; analytics family exposed), catalog entries, live-smoke shape, README rows, tool counts 52/48. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01B3sTPV81755NVtvVwb9Cd8
|
Important
This repository does not receive automatic reviews because it has fewer than 10 stars.
|
Adversarial review: PR #200 at
|
| Check | Result |
|---|---|
npm ci && npm run build |
pass. Manifest lists 52 tools. |
npm test |
40 files, 742 passed, 1 skipped |
npm run check:capability-ledger |
OK v9: 136 published ops, 65 exposed, 52 tools, 72 REST paths |
npm run smoke:public-claims |
passed across 86 surfaces |
npm run check:readme-tools |
all 52 tools documented |
npm run test:live (smoke key, OILPRICEAPI_LIVE_REQUIRED=1 SMOKE_WRITE_CONTRACTS=1) |
PASSED 51, failed 0, cannot-check 0 |
Note that test:live without SMOKE_WRITE_CONTRACTS=1 fails by design. The runner refuses to skip stateful tools, and CI sets that flag in live-tests.yml. On this PR the CI "Live API smoke test" job was skipped, so the local run above is the live evidence.
1. Endpoints and inputs
-
Every new tool calls the path and method that
https://api.oilpriceapi.com/openapi.jsonpublishes. Query parameter names match the spec:from/to/interval,historical/weeks,days/importance/source, the fullbase_price/method/increment/percent_per_increment/rounding/mpg/region/weeksset,states/start_date/end_date/page/per_page,state/days,code/period/type/window,code1/code2/codes,spread/period/typeandinclude=commodities. -
The zod bounds match the spec bounds: per_page 1-100, days 1-90, weeks 1-52, analytics period 1-365, and the enums. Handlers reject an invalid calendar date, a reversed range, a future
toand an unknown state before any request is sent. The alert id must match^\d+$. -
No new tool writes data.
opa_test_price_alertsendsPOST /v1/alerts/:id/test. I readV1::PriceAlertsController#testonoilpriceapi-apiorigin/main, and it is a pure read:- it looks the alert up with
current_user.price_alerts.find; alert.evaluateis a comparison with no side effects, andin_cooldown?is a time check;- it never calls
evaluate_and_record!, never calls a mailer and never fires a webhook.
It cannot notify anyone, the caller included. The read-only annotation is accurate.
- it looks the alert up with
-
Gasoil:
/v1/futures/ice-gasoil/historicalis in the spec.live-contract-catalogcovers Brent only, so I ran the real handler against production.contract=gasoilfor 2026-10-01 to 10-10 returned 15 contract months and 105 rows in USD per tonne, with settlement values.
2. Descriptions
- I found no invented numbers. Every limit quoted (400 rows, 25 permits, 90 days, 52 weeks, 730 days, 15-minute status staleness) matches the spec or the handler code.
- "realtime" appears only as the name of the API's own status tier.
- Plan gating is stated generically: "plan-gated: the API answers HTTP 403" for analytics and gas storage. Well Permits is described as "an add-on, or included in some plans".
- I checked the specific gating claims against the backend:
- Fuel surcharge calculate has no feature gate. The controller comment says so, and the route is absent from
determine_required_feature. - Macro releases has no gate.
- The permit preview runs
before_action :ensure_well_permits_access, except: :preview, so it is open to free keys.
- Fuel surcharge calculate has no feature gate. The controller comment says so, and the route is absent from
3. Existing tools
- I split
src/index.tsperregisterToolon main and on head. All 40 tools are still present. - The only registration-body diff is the gasoil text in
opa_get_futures_history: the description, the enum and thedescribestring. Every other difference is code that moved position. tsc compiles and no symbol was dropped. - Ledger diff: version 8 to 9. The only changes are status, tools and reason edits for the 14 operations. No operation was added or removed.
4. Errors
- Live with a bogus key: analytics and the permit list return a clear HTTP 401 message, and the key is not echoed.
opa_test_price_alertwith an unknown id returns "No alert found with id ... on this account". - 403, 404 and 429 go through the shared
describeUnavailable,describeRequestFailureandalertHttpErrorhelpers. They classify a 429 by the API's signal and only suggest an upgrade for a durable quota.
Non-blocking findings
- The new unit tests cover 403 and 404 but not 401 or 429 for the new tools. Those paths depend on the shared helpers' existing tests.
- The live contract catalog does not exercise
contract=gasoil. Adding one gasoil entry would keep that path covered after this PR. - "The API applies no plan gate to this endpoint" (surcharge calculate, macro releases) is true today. It is a specific claim that will go stale silently if the API adds a gate. Wording like "available on every plan today" would date it.
- The count is 12 new tools plus the gasoil extension. That is 14 ledger operations, not 14 tools.
Summary
One PR for all 14 operations that ledger v8 (#193) deferred. It supersedes #196, #197, #198 and #199. Each operation was verified live on 2026-11-10 with the production smoke key (Scale) before its tool was built. Ledger 8 -> 9 is one bump with one change entry, and all 14 rows are flipped to
exposed, leaving 47 deferred rows. There are four commits, one per family, and each is green on its own.opa_get_continuous_futures_history(new),opa_get_futures_history+contract=gasoil,opa_get_natural_gas_storage(new),opa_get_macro_releases(new)opa_calculate_fuel_surcharge,opa_test_price_alert,opa_get_service_status/v1/statusopa_list_well_permits,opa_preview_well_permits/v1/ei/well-permits,/previewopa_get_price_statistics,opa_get_price_trend,opa_get_price_correlation,opa_get_spread_statistics/v1/analyticsoperationsGating, as stated in each description. The public-claims smoke bans plan names, so gates are written as entitlement gates:
sample_data.Behaviour notes:
metadata.official_settlement=falselive).meta.calendar.statusisok.opa_get_service_statustreats a 503 as health unknown and does not retry it.opa_test_price_alertis read scope, automation category, and accepts numeric ids only (live id 134).tierfield: it reads"free"for Scale (oilpriceapi-api#10761, confirmed-live).Counts: 40 -> 52 tools, read scope 36 -> 48, core profile +
opa_get_service_status. Updated intoolRegistry.test,index.test,package-smoke,docker-smokeand the matrix smoke.Verification (local, on main
124c1de, head of this branch)npm test: 742 passed, 1 skipped.npm run build;check:readme-tools: OK, 52 tools.check:capability-ledgeragainst the live OpenAPI: OK v9, 136 operations, 65 exposed, 71 not exposed.smoke:public-claims,smoke:live-contract-matrix,smoke:package,smoke:docker: pass.test:livewith the smoke key andSMOKE_WRITE_CONTRACTS=1: 51 passed, 1 non-network, 0 failed, 0 cannot-check. This includes the alert dry run on the synthetic alert.No npm or registry publish. Auto-merge cannot arm until #194 is fixed. Tracking: #195.
🤖 Generated with Claude Code
https://claude.ai/code/session_01B3sTPV81755NVtvVwb9Cd8