Skip to content

Phase 34: registry API redesign — align/spine/sources + pairs() quickstart + namespace inversion (1.17.0)#123

Merged
helloiamvu merged 29 commits into
mainfrom
phase34/registry-api-redesign
Jul 14, 2026
Merged

Phase 34: registry API redesign — align/spine/sources + pairs() quickstart + namespace inversion (1.17.0)#123
helloiamvu merged 29 commits into
mainfrom
phase34/registry-api-redesign

Conversation

@helloiamvu

Copy link
Copy Markdown
Member

Summary

Phase 34 replaces the 26-kwarg dataset() composer with a composable three-concept surface — mr.align(spine, *sources), spine constructors, and registered sources — plus a per-domain pairs() quickstart, cross-domain discover(), a namespace inversion with a full deprecation shim train, and the v1.17 migration guide. Ships as 1.17.0, bumped in lockstep across all 9 dists (4 PyPI + 5 npm). Additive-plus-shims: no symbol is removed this release; 2.0 removals are documented only.

Per the pre-signed operator decision (34-08 Task 2): Phase 34 takes 1.17.0, and Phase 33 (reconcile()) is re-sequenced after 34 as 1.18.0, re-scoped to consume align()'s provenance.

The 8 plans

Plan What it ships
34-01 align() pure-join core + SourceContract + ContractError/NoDataError; CLI settlement label composed through align() (Path A falsifier held); docs/error-copy-style-guide.md + shared _errmsg builder
34-02 Registry kind axis + .label. invariant + mr.contracts spine/validators; met_ prefix firewall reconciliation
34-03 Align hardening: collision/prefix policy, window-optional obs(), lazy-spec inference, pit_fidelity refusal, callable provenance verb
34-04 mr.spine() + weather.days()/forecasts()/label.* + markets spine constructors; station_override/qc/tz_override homes
34-05 Cross-domain discover() catalog search: token scoring, alias resolver, language-tagged {py, ts} usage, empty-state + nearest-match
34-06 Namespace inversion: root __all__ flip, legacy shims, error-taxonomy collapse, D-22 default-flip removed, per-domain pairs() quickstart + dataset()pairs() shim train
34-07 TS parity: shared align() conformance fixtures (Python HARD gate, TS reads identical JSON), registry-generated packages-ts/weather/catalog.json codegen (export_schemas.py --check byte-deterministic), TS discover() availability filter, lazy @mostlyrightmd/weather/catalog subpath
34-08 docs/migration/v117-align-spine-sources.md (26-kwarg where-did-each-go table, label 5-jobs→4-shapes, TypeScript-on-1.17, econ-unchanged F-2 follow-up, PIT-transform out-of-scope), error-copy conformance audit across 34-01..07, parity ticket PT-3401 filed, lockstep 1.17.0 bump + release gate

Release-gate results

Gate Result
Fast suite (uv run pytest -m "not live" -q) ✅ green
Wheel grep-gate (scripts/check_no_hosted_calls.py) ✅ exit 0 (249 modules; hosted seams quarantined to 7 allowlisted files)
TS suite (CI=1 pnpm -r run test) ✅ green
TS bundle size (pnpm run size, TS-BUNDLE-01) ✅ meta 42.41/44 kB (catalog stays out of the barrel), weather 26.56/27 kB
4 CWOP firewall files + _cache_schema_version vs main ✅ zero-diff
Live parity (pytest tests/test_parity.py -m live -q) ⚠️ 3/5 — see below
Two-reviewer loop (REVIEW-DISCIPLINE.md) runs on this PR

⚠️ Reviewer/operator decision: live parity 3/5

Cases 1 (KNYC), 2 (KMDW), 5 (KMSY) are byte + dtype green. Cases 3 (KLAX) and 4 (KMIA) fail exclusively on obs_mean_f (column index 7 — the observation sum-then-divide mean): KLAX 56.7826 live vs 56.7400 fixture, KMIA 73.8172 vs 73.7235.

This is environmental fixture drift, not a regression:

  • All cli_* label columns (indices 0–6) match byte-for-byte on every case.
  • The four CWOP firewall files + climate.py are zero-diff vs main; the phase's research.py diff touches only the cli_* attachment path (_compose_cli_by_date), never _obs_aggregates/obs_*.
  • The live IEM/AWC observation set for those settlement days drifted since the May-2026 fixture capture (a late/corrected METAR changes the obs count → the mean moves). 34-01-SUMMARY.md root-caused the identical KLAX signature (same value pair) at phase start, on code the phase does not touch.
  • Re-capturing fixtures against mostlyright==0.14.1 is impossible: api.mostlyright.md was re-pointed in Phase 28, so the May-2026 fixtures are the last ground truth.

Options: accept the drift for the release (label columns — the settlement-critical surface — are byte-exact), pin the parity gate to a cached obs snapshot, or add a documented tolerance for obs_* sum-aggregates on known-drifted days. No tag or publish happens until this is resolved.

TS Parity

TS users keep research()/dataset() unchanged on 1.17. align/spine/pairs are Python-only, deferred behind parity ticket PT-3401 (.planning/parity-tickets/PT-3401-ts-async-align.md) which also records the availability partial-parity policy, the py-only typed-error criterion (R-21), and the markets/econ/meta catalog-subpath follow-up (R-25). The npm weather README points at the migration guide.

Data licensing

No new data source, fetcher, or cache persistence — API-surface redesign over existing sources only.

Planning

Plan + summaries: mostlyrightmd/planningphases/34-registry-api-redesign-align-spine-sources-composition-namesp/ (SUMMARYs 34-01..34-08; UX review record in 34-REVIEW.md).

🤖 Generated with Claude Code

minereda and others added 26 commits July 14, 2026 12:42
…ffolds

- test_align.py: <= as-of boundary, per-entity panel isolation, per-source
  leakage attribution (R-22), LeakageError copy carries source id + cutoff +
  fix (R-17), align returns a plain stamped DataFrame
- test_align_revisions.py: the falsifier — _collapse_revisions with
  revision_order=(('report_type_priority','desc'),) selects final over a later
  correction, resolves equal-priority first-seen (STRICT >), and matches
  merge_climate byte-for-byte on a shared fixture
- test_parity.py: align-based spike path (CLI label through align selects the
  final) + forced-leak error-copy assertion; retains atol=PARITY_ATOL

RED: all three fail with ModuleNotFoundError (mostlyright.align absent).
…NoDataError

- contracts/_source.py: frozen SourceContract (full bitemporal field list);
  revision_order=(('report_type_priority','desc'),) + revision_tie='first_seen';
  prefix must end '_' (ContractError via _errmsg on violation)
- align.py: mr.align(spine, *sources) FROZEN at two concepts + three pure stages
  (_collapse_revisions reproduces merge_climate STRICT> first-seen; equality vs
  backward as-of inferred from spine shape; per-source leakage audit R-22 names
  the offender via _errmsg R-17). Returns plain DataFrame attrs['source']='align'
- _errmsg.py: leakage_message / contract_message single construction path
- core/exceptions.py: ContractError (CONTRACT_ERROR) + NoDataError (NO_DATA)

GREEN: test_align.py + test_align_revisions.py (9 passed). Falsifier holds —
the contract expresses the climate dedup with NO special case in align().
- research._compose_cli_by_date routes the climate rows through
  mr.align(spine, (frame, SourceContract)): the collapse stage reproduces the
  frozen merge_climate dedup via revision_order, an equality join attaches the
  winning record per settlement day. Replaces the bare last-wins climate_by_date
  re-key at the _dataset_impl compose site.
- Byte-parity preserved: raw_climate is already merged at fetch (collapse
  idempotent); the winning row is recovered from the ORIGINAL dict by identity
  (_row_ix), so no DataFrame dtype roundtrip touches cli_* values.
- Spine keyed on the climate rows' own station_code (date-only dedup within a
  single-station call) so the join matches regardless of ICAO K-stripping.

Firewall zero-diff (observations.py / live/_sources / observation_type enum /
merge_climate untouched); _cache_schema_version unchanged; _obs_aggregates not
touched (ULP drift preserved). Fast suite green.
- Enumerates the four required fields every user-facing error carries:
  offending source id, row/entity, both timestamps where temporal, concrete
  fix/next call.
- Names packages/core/src/mostlyright/_errmsg.py as the single construction path
  and 34-08 as the final-audit owner (not the author).
- Worked LeakageError example (source id + cutoff + fix) and ContractError
  example (source spec + field to rename).
…tion (RED)

- test_registry.py: SOURCE/LABEL kind axis, two-directional .label. invariant,
  R-09 declared-prefix firewall (obs_ rejected naming firewall; canonical met_
  succeeds; firewall list unchanged), idempotent/conflict-checked registration,
  one-shot ExperimentalFeatureWarning, static-namespace grep, entries() isolation
- test_contracts.py: SpineContract fields/frozen/requires-y, SourceContract prefix,
  validate_source_contract (bitemporal + pit_fidelity), frame validators, TimePoint
…y specs, pit_fidelity refusal, callable provenance verb

- test_align_collisions.py: collision -> ContractError (both ids + column + rename), prefix override, no _x/_y, default prefix not forbidden, obs_ feature source refused
- test_align_lazy.py: dateless obs() -> LazySourceSpec, dated obs() eager, lazy materialization bounded by spine decision_time, BYO acceptance, latest_only refusal w/ econ pointer, exact/reconstructed pass
- test_provenance_verb.py: callable provenance(frame) + per-column valid_from/coverage, align stamps attrs, .history back-compat
…lazy-spec inference + pit_fidelity refusal + callable provenance verb

- align.py: LazySourceSpec + _materialize (spine decision_time window inference, R-02) + _apply_prefix (source-id namespacing, R-09 forbidden-prefix firewall, R-24 hard-collision ContractError naming both ids + column + which prefix=, never _x/_y) + _gate_pit_fidelity (R-18 latest_only REFUSE w/ econ.vintages()/ALFRED pointer) + _coverage_edge; stamps df.attrs provenance/coverage; signature frozen at (spine, *sources)
- _provenance.py: callable provenance(frame) verb (per-column source id + valid_from/coverage, R-12); history/record/row_window_hash attached as attributes (back-compat)
- __init__.py: mostlyright.provenance __getattr__ resolves to the callable verb
- weather/obs.py: window-optional obs(station, from_date=None, to_date=None, ...) -> LazySourceSpec when dateless, else byte-stable eager frame (R-02); _obs_source_contract (met_ prefix) + _lazy_obs_spec
- kind-aware _apply_prefix byte-passes reconstructed LABELs (CLI parity path) through unchanged
- migrate 34-01 test_align.py (obs_->met_ feature prefix) + test_obs_surface.py (start/end->from_date/to_date) test surfaces (R-09/R-02)

All ContractError copy via the shared 34-01 _errmsg.contract_message helper (R-17). Four CWOP firewall files + merge_climate zero-diff.
…ine/validators

- _contributors.py: additive ContributorSpec.kind field (Literal['source','label'],
  default 'source' — byte-stable for the four Phase-32 built-ins) + RegistryKind alias
- registry/__init__.py: mr.registry.source()/label()/entries()/get(); SOURCE/LABEL
  kind axis written into the shared _REGISTRY (reuse, no fork), two-directional
  .label. invariant, SOURCE-only declared-prefix firewall (R-09: reads
  _FORBIDDEN_LABEL_PREFIXES, rejects obs_ naming the firewall, canonical met_
  succeeds, list untouched), reuse _assert_columns_clean, idempotent/conflict-checked
  writes, one-shot ExperimentalFeatureWarning; plain dict store, NO runtime import-by-id
- contracts/_spine.py: SpineContract frozen dataclass (y_columns, decision_time,
  label_available_time, label source-identity+version) + validate_source_contract
  (bitemporal + pit_fidelity + prefix) + validate_source_frame / validate_spine_frame
- contracts/__init__.py: re-export SpineContract, the validators, TimePoint
- test_registry.py: fix _src helper so pit_fidelity override does not collide
…er, usage, empty-state, nearest-match

- deterministic four-tier field weighting (id > tags > columns > description)
- structured filters (domain/kind/availability)
- city= resolver path + cross-venue note; free-text alias extraction (R-05)
- zero-arg full-catalog listing (R-23); empty-state attrs message (R-06)
- resolve_entity('knc') NoDataError subclass with nearest-match 'KNYC' (R-07)
- weather usage names weather.pairs (R-14); ast.parse-runnable + {py,ts} (R-16/R-20)
- no-fuzzy on misspelled token; city= back-compat attrs envelope
…abel.* + markets label axis

- test_spine.py: mr.spine() BYO column mapping + ContractError on missing decision_time
- test_weather_label_axis.py: weather.days() re-export, two positional dates, tz_override, LIST long-format panel byte-equal to loop, label.cli/daily_extremes y byte-equality, .label. invariant, obs(qc=)
- test_weather_forecasts_source.py: forecasts() fcst_* frame, LazySourceSpec, source/model/models threading, align(label,obs,forecasts) fcst_* byte-consistency
- test_markets_label_axis.py: kalshi.contracts()/label.settlement(), polymarket.contracts(station_override=), .label. registration
…override homes

- mr.spine(df, entity=, decision_time=, y=): BYO bridge into the canonical SpineContract shape (T-34-12 loud reject on missing decision_time; reuses _coerce_date_column)
- weather.days(stations, from_date, to_date, *, tz_override=): public bare per-LST-settlement-day spine (re-export from weather/__init__.py; two positional dates; LIST -> long-format panel byte-equal to the loop; tz_override home)
- weather.forecasts(entity, from_date=None, to_date=None, *, source/model/models): wrap-and-register bitemporal source over dataset(include_forecast=True); window-optional LazySourceSpec; absorbs the four forecast kwargs
- weather.label.cli()/.daily_extremes(): .label.-registered spine factories wrapping the byte-stable recipes (apply_named_recipe / compose_daily_extremes_label)
- weather.obs(qc=): qc orphan-kwarg home threaded through the lazy spec builder
- four CWOP firewall files zero-diff; climate dedup untouched
…s + station_override home

- markets.kalshi.contracts(ticker, from, to): bare spine via parse_ticker station resolution (zero duplicated station logic)
- markets.kalshi.label.settlement(): .label.-registered cli-settled spine (wraps dataset(label='cli') oracle) + source-identity attrs
- markets.polymarket.contracts(event, from, to, *, station_override=): bare spine; station_override orphan-kwarg home (pins station, bypasses event resolution + network)
- markets.polymarket.label.settlement(): .label.-registered daily_extremes-settled spine
- PIT-universe responsibility + rolling-features-out-of-scope documented in factory docstrings
- [Rule 3] test_registry.py: neutralize the 'weather.label.cli' sample id (now a real import-time registration) to a demo id, avoiding cross-test registry collision
- discover(text?, *, city?, **filters): deterministic field-weighted token
  scoring (id > tags > columns > description), NO fuzzy on the local path
- registry/_catalog.py: catalog-as-static-JSON manifest (atomic mkstemp+os.replace,
  sort_keys, malformed-tolerant load, env override); language-tagged {py,ts} usage
- registry.register_alias_resolver/resolve_entity + EntityNotFoundError(NoDataError)
  with deterministic edit-distance nearest-match over the static registry (R-07)
- free-text alias extraction pairs dataset x entity + keeps cross-venue note (R-05)
- zero-arg full-catalog listing (R-23); empty-but-shaped frame + attrs message (R-06)
- rung-appropriate usage: weather rows name weather.pairs (R-14); pip-install hint on
  uninstalled-domain rows (R-16); city= back-compat routes to the resolver path
- discover keeps a stdlib-only import graph (lazy registry import) so a flags-off
  import mostlyright never pulls the contracts->satellite chain
… per-domain pairs()

- test_shims.py: legacy root symbols importable + DeprecationWarning naming new
  weather.* home + 2.0 removal + byte-stable delegation; live canonical home;
  D-22 FutureWarning dead on the bare dataset() shim
- test_namespace_inversion.py: exact inverted __all__ (machinery+domains only),
  root-importable LeakageError/ContractError/NoDataError, NoCWOPDataError re-parented
- test_pairs.py: weather.pairs two positional dates + capped + weather.days()/align
  desugaring docstring + visible graduation error; markets dataset()->pairs() shim
…RED)

- 4 cross-SDK JSON fixtures pinning tie-break authority, boundary <=, ns
  precision, and NaN semantics (single source of truth, both languages read them)
- tests/test_align_conformance.py drives the pure join core (RED: driver stub
  drops joined rows so the semantic assertions fail before wiring)
- packages-ts/meta/src/__tests__/alignConformance.test.ts reads the IDENTICAL
  fixtures; semantic execution deferred to PT-3401 (documented it.skip)
- meta vitest include extended to discover src/__tests__ conformance suite
…talog + discover availability filter (GREEN)

- tests/test_align_conformance.py drives the pure join core against the four
  shared fixtures — GREEN (tie-break authority, boundary <=, ns precision, NaN)
- scripts/export_schemas.py emits packages-ts/weather/catalog.json from the live
  Python registry (one-source-of-truth codegen, R-08); byte-deterministic under
  --check; weather-domain sources are availability=py,ts (R-25 weather-only)
- discover() generalized: availability filter reads the committed manifest and
  surfaces usage.ts, falling back to note/docs — never a bare Python snippet (R-20)
- @mostlyrightmd/weather/catalog lazy subpath export (out of the meta bundle)
- TS async align() deferred behind parity ticket PT-3401
… kill D-22 flip

- Root __all__ = machinery + domains only (align, spine, discover, provenance,
  Leakage/Contract/NoDataError, weather, markets, registry, contracts, __version__)
- research/dataset/Station/CATALOG/StationCatalog/live dropped from __all__ but kept
  importable via once-per-session DeprecationWarning __getattr__ shims naming the new
  mostlyright.weather.* home (removed at 2.0); byte-stable same-object delegation
- weather/stations.py canonical station home; weather.__init__ re-exports
  research/dataset/live/stations; live canonical at mostlyright.weather.live
- exceptions: LiveStreamError re-parented under NoDataError (additive MRO) so
  NoLiveDataError/NoCWOPDataError live under NoDataError; three errors root-importable
- D-22 label-default FutureWarning removed (the 2.0 flip is cancelled); _D22_BRIDGE_WARNING
  constant + both warn sites gone; OMITTED label stays 'cli' byte-stable, no warning
- align/spine import core.exceptions lazily so the root eager-binds these headline verbs
  (stable submodule-name shadowing) WITHOUT a bare import pulling core.schemas (satellite
  covariate) — flags-off import-cost guard stays green
- updated pre-existing D-22 / __all__-membership tests to the inverted surface
…() shim train

- weather.pairs(entity, from_date, to_date, *, label='cli'): capped (<=5 params),
  two positional dates, docstring first line names the label='cli' default and the
  body shows the weather.days(...) -> weather.label.cli(...) -> align(...) desugaring;
  desugars to align(weather.label.<label>(...), weather.obs(entity)); any unsupported
  kwarg raises the visible graduation error naming align + the migration guide
- markets.kalshi.pairs / markets.polymarket.pairs: renamed the thin dataset delegators
  to pairs() (entity first param, two positional dates, zero duplicated join logic)
- markets.kalshi.dataset / markets.polymarket.dataset: deprecation shims -> pairs()
  (DeprecationWarning, 2.0 removal); no NEW function named dataset
- weather.__init__ exports pairs (label='cli' is explicit/stable, not the D-22 flip)
…catalog entity

- specs/pairs.json: the settlement-join OUTPUT schema pairs()/dataset()/research()
  return (v0.14.1 training-pair contract: date, station, cli_*, obs_*, + optional
  fcst_*); distinct from the internal settlement-join.json
- _capabilities.py: _SCHEMA_FILES['pairs']='pairs.json'; removed the 'intentionally
  omitted' comment + the 'pairs is not supported' load_schema note so load_schema('pairs')
  resolves via the normal SPECS_DIR path
- test_capabilities.py: inverted the two intentionally-absent tests
  (test_load_schema_pairs_raises_value_error -> _resolves; test_schema_files_excludes_pairs
  -> _includes_pairs) so function + schema + catalog cohere
…n/spine

The lazy runtime imports (Phase 34 06) made the TYPE_CHECKING ContractError/LeakageError
imports dead (no annotations reference them) — remove to clear ruff F401.
… _errmsg

- docs/migration/v117-align-spine-sources.md: where-did-each-kwarg-go table
  (all 26 dataset() kwargs; the 4 forecast kwargs -> weather.forecasts();
  label's 5 jobs -> 4 call shapes; 3 orphan kwargs homed), ladder walkthrough,
  namespace-inversion symbol map, TypeScript-on-1.17 (PT-3401), econ-UNCHANGED
  (F-2 follow-up), PIT-transform-out-of-scope, D-22-FutureWarning-gone, retrain
  note
- _errmsg.entity_not_found_message: centralize the unknown-entity 'did you mean'
  wording on the single construction path (R-17 audit fix; byte-identical copy)
- registry.EntityNotFoundError routes through the builder; test asserts the
  suggestion appears and the copy == the builder output
- packages-ts/weather/README.md: migration-guide pointer + TS-on-1.17 note

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…lease

Lockstep minor across all 9 dists (4 PyPI + 5 npm) + uv.lock + CHANGELOG + README,
following the 1.16.0 pattern (7d956ba). Phase 34 ships the align/spine/sources
ladder + per-domain pairs() as 1.17.0 (operator-signed 2026-07-14; Phase 33
re-sequenced after 34 to 1.18.0). Additive-plus-shims: default research()/dataset()
output byte-identical to 1.16; every relocated symbol keeps an importable
DeprecationWarning shim; ALL 2.0 removals DOCUMENTED only (nothing removed).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@helloiamvu helloiamvu requested a review from Tarabcak July 14, 2026 20:18
@github-actions

Copy link
Copy Markdown

Docs-required check: PASS

API-surface change includes docs updates — no reminder needed.

API-surface files changed:

packages-ts/meta/src/__tests__/alignConformance.test.ts
packages-ts/meta/src/discover.ts
packages-ts/meta/src/index.ts
packages/core/src/mostlyright/__init__.py
packages/core/src/mostlyright/_contributors.py
packages/core/src/mostlyright/_errmsg.py
packages/core/src/mostlyright/_provenance.py
packages/core/src/mostlyright/align.py
packages/core/src/mostlyright/contracts/__init__.py
packages/core/src/mostlyright/contracts/_source.py
packages/core/src/mostlyright/contracts/_spine.py
packages/core/src/mostlyright/core/exceptions.py
packages/core/src/mostlyright/discover.py
packages/core/src/mostlyright/registry/__init__.py
packages/core/src/mostlyright/registry/_catalog.py
packages/core/src/mostlyright/research.py
packages/core/src/mostlyright/spine.py
packages/markets/src/mostlyright/markets/kalshi.py
packages/markets/src/mostlyright/markets/polymarket.py
packages/weather/src/mostlyright/weather/__init__.py
packages/weather/src/mostlyright/weather/forecasts.py
packages/weather/src/mostlyright/weather/label/__init__.py
packages/weather/src/mostlyright/weather/obs.py
packages/weather/src/mostlyright/weather/pairs.py
packages/weather/src/mostlyright/weather/spine.py
packages/weather/src/mostlyright/weather/stations.py

Docs files changed:

CHANGELOG.md
README.md
docs/error-copy-style-guide.md
docs/migration/v117-align-spine-sources.md
packages-ts/weather/README.md
tests/fixtures/parity/conformance/README.md

@github-actions

Copy link
Copy Markdown

Parity ticket gate: PASSED

parity-ticket-check: PR touches BOTH Python and TypeScript trigger surfaces — paired-language change satisfies the gate.

See CROSS-SDK-SYNC.md §2 for the workflow.

…y windows + declared-column validation + truthful TS catalog

Round-1 three-reviewer findings (codex gpt-5.5 high + Python Architect +
TypeScript Architect on PR #123):

- CRITICAL align.py — two sources sharing a knowledge_time_col name could
  hide the second source's leakage audit behind pandas suffixes (equality
  path) or pollute output columns (as-of path). Each source's temporal keys
  are now isolated under per-source internal names for its join + audit,
  then restored when free / dropped when claimed — pandas _x/_y can never
  appear and a source can never dodge its own audit. Join-key doubles
  (obs's knowledge_time_col == event_time_col == 'date') keep their name.
- CRITICAL align.py — window-less (lazy) sources now materialize over the
  spine's copy of their event_time_col (the settlement date) instead of
  decision_time.date(): the 16:30 LST market close crosses UTC midnight at
  Pacific/Alaska/Hawaii stations, so date-derived windows shifted +1 day
  and KLAX-class spines silently lost the first settlement day's features.
- HIGH align.py — materialized frames are validated against the contract's
  declared entity/temporal/feature columns (ContractError, style-guide
  copy); a promised feature column can no longer vanish silently.
- HIGH catalog codegen — the exporter now imports weather.label (the
  manifest previously shipped ONLY weather.forecasts), the py,ts
  availability upgrade is kind-gated (label recipes stay Python-only,
  PT-3401), and usage.ts is truthful: a real dataset() snippet for TS
  source rows, '' + note/docs fallback otherwise — never pairs()/align().
- HIGH enforcement — schema-drift CI diffs packages-ts/weather/catalog.json
  and triggers on registry/contracts/weather-source paths; a fast-suite
  test pins the committed manifest bytes to the live registry emission.

Fast suite 4607 passed; TS meta 288 passed + weather suite green; size
budgets meta 42.41/44 kB. MEDIUM/LOW candidates not blocked on per
REVIEW-DISCIPLINE severity gate.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
helloiamvu and others added 2 commits July 14, 2026 23:35
… + collision-free isolation names + list-entity lazy obs

Iteration-2 findings (codex gpt-5.5 high + Python Architect independently
converged on the flagship-quickstart bug):

- CRITICAL spine.py/obs.py — weather.pairs() returned EVERY obs feature
  column silently NaN: the days()-built spine stamped the station AS TYPED
  ('KNYC') while the obs daily aggregator stamps the normalized registry
  code ('NYC'), so align's equality join on (station, date) matched zero
  rows. days() now stamps the NORMALIZED code — the v0.14.1 heritage key
  the parity fixtures pin ('NYC') and the form research()/dataset() output
  and every source frame carries — so all sources join without per-source
  restamps. Out-of-registry stations (tz_override path) keep the typed id.
  pairs() now has a run-to-completion offline test asserting obs_high_f is
  non-NaN; the _fake_dataset byte-oracle stamps normalized codes like the
  real dataset().
- HIGH obs.py — list-entity weather.pairs(['KNYC','KLAX'], ...) crashed in
  the lazy builder (eager path is single-station: AttributeError 'list'
  has no attribute 'strip'). The builder now materializes per station and
  concatenates one long-format panel — ONE obs source, met_/obs_ namespace
  claimed once.
- HIGH align.py — the temporal-key isolation names (__mr_kt_<id>) are now
  guaranteed collision-free via _free_internal_name() (a spine already
  carrying __mr_kt_<id> could re-open the pandas-suffix audit-dodge hole).
- pairs() docstring corrected: obs features land as the v0.14.1-heritage
  obs_* aggregates (label-adjacent pass-through), not met_*.

TS Architect iteration-2: PASS (no TS files changed since).
Fast suite 4611 passed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Iteration-3 convergent finding (codex + Python Architect found the identical
HIGH; the Architect additionally verified this exact fix as complete):

- HIGH kalshi.py/_spine_days (reused by polymarket contracts/label) stamped
  the station AS TYPED (ICAO 'KNYC' from parse_ticker), while weather.obs()
  frames carry the normalized registry code ('NYC') — the same all-NaN
  equality-join mismatch iter-2 fixed for weather.days(), left unfixed in
  the markets domain: align(kalshi.label.settlement(ticker, d1, d2),
  weather.obs('KNYC')) matched zero rows. The join-key convention now lives
  ONCE in mostlyright._internal._pairs.station_key() (normalized code,
  as-typed fallback for out-of-registry ids); weather.days() delegates to
  it and markets _spine_days stamps it (market_close_utc still receives the
  raw id — both forms resolve identically). Markets label-axis tests updated
  to the normalized codes.

Reviewer state after this commit: codex iter-3 1 HIGH -> fixed here;
Python Architect iter-3 1 HIGH (same, fix pre-verified) -> fixed here, all
other sweeps clean (intl/tz_override loud-fail consistency, list-entity,
firewall zero-diff); TS Architect PASS (iter-2, no TS changes since).
Fast suite 4611 passed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@helloiamvu helloiamvu merged commit 02580c3 into main Jul 14, 2026
24 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants