# Offline research integration kit

Every bundled fixture is explicitly synthetic. Identifiers, entities, values and
events were invented to exercise adapters. No sample makes network requests,
uses keys, or describes a Vector Ridge API. The fixed retrieval timestamp is an
assumption inside the fixture exercise, not a claim that retrieval took place.

Run from the kit root using Node.js 18+ or Python 3.10+ (standard libraries only):

```text
node samples/run.mjs fred fixtures/fred-synthetic.json 2024-01-31
node samples/run.mjs worldbank fixtures/worldbank-synthetic.json 2024-03-15
node samples/run.mjs sec fixtures/sec-synthetic.json 2024-02-15
python samples/normalize.py fred fixtures/fred-synthetic.json 2024-01-31
python samples/normalize.py worldbank fixtures/worldbank-synthetic.json 2024-03-15
python samples/normalize.py sec fixtures/sec-synthetic.json 2024-02-15
node --test tests/adapters.test.mjs
python tests/reference.py
```

Keep the `assets`, `fixtures`, `samples` and `tests` directories together.
`assets/adapter.mjs` exposes `normalize`, `selectAsOf`, `validateRecords`,
`retryPlan`, `FIXTURE_OPTIONS` and `VERSION`. `normalize` returns records;
`selectAsOf` returns a selected subset. Neither function calls a remote service.
`normalize` requires `{series, unit, retrievedAt}`. The CLI deliberately uses
the fixed `FIXTURE_OPTIONS`, making the examples reproducible.

| Provider ID | Fixture series option | Unit | Normalized identity |
|---|---|---|---|
| fred | SYNTHETIC_INDEX | index points | SYNTHETIC_INDEX |
| worldbank | SYNTHETIC.DEMO | illustrative units | SYNTHETIC.DEMO:XZZ |
| sec | us-gaap:Assets | USD | 0000000000:us-gaap:Assets |

The common fixture retrieval assumption is `2024-03-15T12:00:00Z`.

## Deliberately narrow contracts

FRED input is one complete observations response, with offset zero if supplied
and count matching the array if supplied. The dot missing marker becomes null;
zero stays zero. `realtime_start` supplies `available_date` with basis
`fred_realtime_date`; it has day precision. Vintage identity includes observation
date, availability date, series and unit. Null/invalid dates, nonfinite numbers,
contradictions and duplicate revision keys fail explicitly.

World Bank input is an array of page envelopes, each `[metadata, records]`.
The adapter checks unique page numbering, full coverage, page lengths, declared
totals, and consistent `lastupdated`. This example supports annual years only.
`observation_period` preserves the original year; `period_kind` is `annual`;
`observation_date = YYYY-12-31` is a sorting convention, not an observation
timestamp. Availability is the supplied UTC retrieval date with basis
`retrieval_time`. The API's `lastupdated` is retained in source provenance but
never treated as a record's publication date or historical vintage. Country is
part of series identity. Blank provider units use the caller's declared unit;
nonempty provider units must match it.

SEC input is companyfacts for one issuer. Only `us-gaap:Assets`, USD and instant
contexts are supported. A context with a `start` field is a duration and excluded.
Issuer is part of series identity; accession is preserved as `source_record` and
in `revision_key`. `filed` supplies `available_date` with basis `sec_filed_date`,
at day precision. No accepted/filing time or intraday ordering is inferred.

As-of selection groups by source, qualified series, observation date and unit,
then selects the latest availability date at or before the cutoff. Different
values on that latest date are ambiguous and rejected. Equal-value ties select
lexicographic provenance deterministically, without claiming a temporal order.
The full normalized output retains original provenance; selected output is not
a complete revision ledger. Identical duplicated revision keys also reject, so
callers must reconcile repeated source records explicitly.

`retryPlan([429,503,200],[2,60],3)` returns inspectable steps with delays 2 and
60 seconds followed by success. Only 429/503 retry. Other statuses fail except
2xx success. At most three attempts and 60 seconds per proposed delay are allowed.
A numeric Retry-After above 60 seconds stops with
`server_delay_exceeds_local_budget`; it is never shortened to retry before the
server's requested delay. For example, `[429,200]` with Retry-After `[120]` stops
on the first response and does not simulate the later success.
Absent numeric Retry-After uses 1/2-second bounded exponential defaults. Numeric
strings are accepted; HTTP-date headers are intentionally unsupported. There is
no sleeping, jitter, network request or claim that this is every provider's
recommended retry policy. A one-status plan may end with a retry proposal;
it does not invent an eventual response.

The examples demonstrate parsing and provenance rules, not a production client.
They do not establish provider completeness, data rights, point-in-time
tradability, a commercial service, or empirical investment results. Any real
deployment must separately verify endpoint policy, licensing, authentication,
rate limits, durable ingestion and coverage. Recurring serving/ingestion belongs
in managed cloud infrastructure, not a personal computer.

## Primary documentation reviewed 2026-10-16

- [FRED series observations](https://fred.stlouisfed.org/docs/api/fred/series_observations.html)
- [FRED real-time periods](https://fred.stlouisfed.org/docs/api/fred/realtime_period.html)
- [World Bank API basic calls and pagination](https://datahelpdesk.worldbank.org/knowledgebase/articles/898581-api-basic-call-structures)
- [World Bank advanced queries and update metadata](https://datahelpdesk.worldbank.org/knowledgebase/articles/1886686-advanced-data-api-queries)
- [SEC EDGAR APIs and companyfacts](https://www.sec.gov/search-filings/edgar-application-programming-interfaces)

These links support provider shape/field context. Date mappings, rejection rules,
the synthetic fixture values and the bounded retry planner are choices of this
educational implementation, not provider certifications.
