Skip to content

feat(compute): wire country + exchange listing metadata into main.py (PR-A2)#349

Merged
dackclup merged 2 commits into
mainfrom
claude/sharp-turing-q3A5J
Jun 1, 2026
Merged

feat(compute): wire country + exchange listing metadata into main.py (PR-A2)#349
dackclup merged 2 commits into
mainfrom
claude/sharp-turing-q3A5J

Conversation

@dackclup
Copy link
Copy Markdown
Owner

@dackclup dackclup commented Jun 1, 2026

PR-A2 — country + exchange main.py wiring

Second of the 2-PR ingest sequence. PR-A1 (schema + cross_source helpers) merged via #347 5f39d644; PR-A2 populates the fields PR-A1 declared. PR-B (frontend hero chips) is still deferred until ≥ 1 cron confirms coverage (observability-before-wiring).

Display-only — no ranking / scoring / valuation / defense-layer change. No schema changeSCHEMA_VERSION intentionally stays 0.10.12-phase4.6 (the 3 fields already exist from PR-A1, so no snapshot regen / no test_config.py bump / no SKILL.md row).

The 6 edits to run_weekly_compute

  1. Import country_for_exchange / exchange_name / fetch_yfinance_exchange from compute.ingest.cross_source.
  2. Two accumulators exchange_by_ticker / country_by_ticker (dict[str, str | None]) before the Step-8 per-ticker loop.
  3. Inside the loop (piggybacks the existing cross_source_validate_market_cap block): exchange_code = fetch_yfinance_exchange(ticker)exchange_name(...) / country_for_exchange(...). Skip-safe: the helper honors QR_SKIP_CROSS_SOURCE internally (no main.py branch — confirmed no such ref in main.py), so simulate does no live fast_info fetch.
  4. exchange= / country= kwargs on StockDetail(...).
  5. exchange_coverage_pct via a pure _exchange_coverage_pct helper (Rule-18 diagnostic) + an "Exchange coverage" logger.info.
  6. exchange_coverage_pct= kwarg on Metadata(...).

Tests (+19)

  • tests/test_main.py (9): coverage formula (all/partial/none/empty/rounding) + Step-8 mapping contract (known code → NASDAQ/US · None → both None · non-US XLON passthrough · None-safety), monkeypatching compute.main.fetch_yfinance_exchange.
  • tests/test_output/test_exchange_schema.py (10): Metadata.exchange_coverage_pct + StockDetail.exchange/.country round-trip (float/zero/default-None / passthrough / both-populated / both-None).
  • The coverage-formula tests import the production _exchange_coverage_pct rather than copying it (CLAUDE.md §Gotchas PR fix(scoring): inject stale_filing_hard before Top-5 rotation (latent Rule-16 fix) — closes #309 #310 no-verbatim-copy lesson).

Verification (run locally, not just CI)

  • python -m py_compile OK · ruff check . clean · full offline suite 1460 passed / 11 skipped / 0 failures (OSAP files env-gated locally, pass on CI).
  • Pre-push gate: quantrank-reviewer READY-TO-PUSH (0 FAIL) · phase-coordinator LOCKSTEP-SATISFIED · docs-reviewer DOCS-CLEAN.

Known follow-up (non-blocking, from review)

  • quantrank-reviewer WARN-2: no test pins the "snapshot-less ticker still gets an exchange entry + StockDetail" no-continue invariant (the property that keeps len(exchange_by_ticker) == detail_count). Closing it needs the lightweight run_weekly_compute orchestrator harness that the suite defers elsewhere (the test_wall_clock_schema TODOs) — appropriately a separate effort, not this PR.

Doc lockstep: CLAUDE.md + AGENTS.md in-flight entries flipped PR-A1 → PR-A2; full entry in PHASE_STATUS_INFLIGHT.md.

https://claude.ai/code/session_0148EoMmL6zakDWqHXjqQ9yq


Generated by Claude Code

…(PR-A2)

Populate the StockDetail.exchange / .country fields PR-A1 (#347) declared,
plus the Rule-18 Metadata.exchange_coverage_pct diagnostic, from the Step-8
per-ticker cross_source loop. Display-only -- no ranking / scoring /
valuation / defense-layer change; NO schema change (stays 0.10.12-phase4.6,
the fields already exist from PR-A1).

- Import country_for_exchange / exchange_name / fetch_yfinance_exchange.
- Two {exchange,country}_by_ticker accumulators before the Step-8 loop.
- Per-ticker fetch_yfinance_exchange piggybacks the cross_source market-cap
  block; skip-safe via QR_SKIP_CROSS_SOURCE inside the helper (no main.py
  branch -- confirmed no QR_SKIP_CROSS_SOURCE ref in main.py).
- exchange= / country= kwargs on StockDetail (siblings of industry=).
- exchange_coverage_pct via a pure _exchange_coverage_pct helper, imported by
  the tests (NOT copied) per the CLAUDE.md PR #310 no-verbatim-copy lesson;
  + "Exchange coverage" log + the Metadata kwarg.

PR-B (hero country/exchange chips) waits for >= 1 cron confirming coverage
(observability-before-wiring).

Tests +19 (test_main.py orchestrator wiring + test_output/test_exchange_schema.py
schema round-trip); full offline suite 1460 passed, 0 failures. Pre-push gate:
quantrank-reviewer READY-TO-PUSH, phase-coordinator LOCKSTEP-SATISFIED,
docs-reviewer DOCS-CLEAN.

https://claude.ai/code/session_0148EoMmL6zakDWqHXjqQ9yq
@vercel
Copy link
Copy Markdown

vercel Bot commented Jun 1, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
quantrank Ready Ready Preview, Comment Jun 1, 2026 6:45am

@dackclup dackclup marked this pull request as ready for review June 1, 2026 06:36
…3A5J

# Conflicts:
#	PHASE_STATUS_INFLIGHT.md
@dackclup dackclup merged commit 809cd4d into main Jun 1, 2026
5 checks passed
@dackclup dackclup deleted the claude/sharp-turing-q3A5J branch June 1, 2026 06:48
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Jun 1, 2026

Pre-merge production simulation

Field Value
Duration 375s
Universe size 502
Schema version 0.10.12-phase4.6
Compute commit 0cc1cb07cd5b4985cc25fb0eda193de3e3484b20
PR-branch output pr-349-compute-output (14-day retention)

Diff vs main

Field Main PR Δ
Universe size 502 502 +0
Schema version 0.10.11-phase4.6 0.10.12-phase4.6 ⚠️ bumped

Main baseline: 2026-05-29T23:21:23Z (2.3 days old)

Top-10 movers (sorted by |Δcomposite_score|)

Ticker PR rank main rank Δrank PR score main score Δscore
STE 349 267 -82 45.96 50.20 -4.24
ADSK 179 109 -70 53.93 57.52 -3.59
TYL 417 448 +31 41.48 39.13 +2.35
DELL 281 234 -47 49.36 51.53 -2.17
TGT 41 61 +20 63.08 60.99 +2.09
WMT 210 160 -50 52.65 54.65 -2.00
NXPI 167 211 +44 54.33 52.35 +1.98
BF-B 264 223 -41 50.03 51.98 -1.95
HOOD 280 245 -35 49.40 51.24 -1.84
V 194 235 +41 53.34 51.50 +1.84

dackclup added a commit that referenced this pull request Jun 1, 2026
Replace the stock-detail hero #rank-row sector + industry chips with a country
chip (country-flag-icons flag + ISO tag) + an exchange chip (lucide Landmark
generic icon + display name), reading detail.country / detail.exchange
(populated by PR-A2, #347 -> #349). Display-only; no compute / schema / scoring
/ valuation change.

- New frontend/components/ListingChips.tsx -- LedgerCraft neutral-steel chips
  (mirrors SectorChip), null-safe (renders nothing until a cron populates the
  fields), FLAG_BY_COUNTRY static per-country lookup (US today).
- New dep country-flag-icons ^1.6.17 (MIT, 0 transitive, 0 install-script --
  dependency-auditor + security-reviewer both SAFE). Per-country STATIC subpath
  import only; barrel/dynamic = 330KB footgun (new CLAUDE.md Gotcha, mirrors
  lucide).
- frontend/app/stock/[ticker]/page.tsx -- swap SectorChip + industry span for
  ListingChips; drop the now-unused SectorChip import. Sector still shows in the
  HeroAttributeTiles Sector tile.

Held Draft -- merge after the next weekly cron populates exchange/country +
confirms exchange_coverage_pct (observability-before-wiring); current data has
the fields null so the chips render nothing on live until then.

frontend-design-reviewer 0 FAIL (D1 font-medium + D2 flag/icon height parity +
F1 title a11y all fixed). tsc --noEmit clean; next build 506/506 static pages;
flag tree-shaken (~2KB, no barrel bloat).

https://claude.ai/code/session_0148EoMmL6zakDWqHXjqQ9yq

Co-authored-by: Claude <noreply@anthropic.com>
dackclup added a commit that referenced this pull request Jun 2, 2026
…s) (#373)

Frontend (6):
- PriceHistoryChart: add dark: variants to up/down reference chips; ring-rose-300→200
- RawMetricsTable: add font-mono to numeric value cells
- ScoreGauge: add role="img" + aria-label(Math.round) + svg aria-hidden
- RankingTable: 2× loose-null (!== null && !== undefined → != null)
- RiskSummaryCard: ring-amber-300 → ring-amber-200 (globals.css allowlist)

Security (1):
- manual-trigger.yml: route inputs.note through env: var (script injection guard)

Docs (5):
- CLAUDE.md/PHASE_STATUS.md/WORKFLOW.md: schema pointer 0.10.11 → 0.10.12
- METHODOLOGY.md: ~21 currently emit → ~27 currently emit
- SKILL.md: "is PR-A2" → "was PR-A2 (merged as PR #349)" (past tense)

https://claude.ai/code/session_01ELWfJoJp5kMje2j4zoUCQh

Co-authored-by: Claude <noreply@anthropic.com>
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