feat(scoring): sector-keyed cost_of_equity behind config flag (closes #67 prep)#204
Merged
Merged
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
…67 prep) Add GICS-keyed Damodaran 2019 sector cost-of-equity table + Rule 18 observability surface for the value_trap_risk delta before production flip. - compute/scoring/cost_of_equity.py — SECTOR_COST_OF_EQUITY dict (11 GICS sectors, 6%-12%) sourced from Damodaran 2019 *Investment Valuation* 3rd ed. Table 8.4 + Damodaran NYU online betas dataset (January 2025 update). Pure function get_cost_of_equity(ticker_sector) → float; fallback 10%. - compute/config.py — USE_SECTOR_COE: bool = False (OFF by default, Rule 18 data-collection PR; flip follows after ≥ 1 cron delta-count measurement). SCHEMA_VERSION bumped 0.9.7-phase4h.7 → 0.9.8-phase4h.8. - compute/valuation/ensemble.py — rim_fair_price now receives sector-adjusted Ke when USE_SECTOR_COE=True; no behaviour change while flag is False. - compute/output/schemas.py + frontend/lib/types.ts — 3 new Metadata fields: sector_coe_enabled (bool), value_trap_risk_count_without_sector_coe (int|None), value_trap_risk_count_with_sector_coe (int|None). Both counts computed every cron regardless of flag so the flat-10% vs sector-Ke delta is observable. - compute/main.py — per-ticker dual check_rim_applicability pass wires the counters; Metadata constructor passes sector_coe_enabled + both counts. - frontend/lib/schema-snapshot.json — regenerated via schema_check --update-snapshot. - tests/test_scoring/test_cost_of_equity.py — 32 tests: 11-sector dict pin + fallback + Hypothesis band property + ordering sanity (Utilities < Energy). - tests/test_scoring/test_value_trap_risk_sector_coe.py — 8 tests: flat-CoE baseline + Utilities FP-reduction case + Energy true-positive preservation + USE_SECTOR_COE=False default pin + hard-stale short-circuit. - tests/test_config.py — schema version pin updated to 0.9.8-phase4h.8. - CLAUDE.md + AGENTS.md — §Phase status updated per lockstep convention. Tests: 1059 → 1094 (+35 new, +1 rename). ruff clean. schema_check in sync. Methodology-scientist Mode B sign-off required before the flip PR. Part of #67. Behind USE_SECTOR_COE = False default — data-collection PR per Rule 18. Flip-to-True follow-up after ≥ 1 cron measures delta-flag-count. https://claude.ai/code/session_01D6NTyJZa5LWHWakbF5dT29
cedfcf1 to
037c777
Compare
7 tasks
Contributor
Pre-merge production simulation
Diff vs main
Main baseline: Top-10 movers (sorted by |Δcomposite_score|)
|
dackclup
added a commit
that referenced
this pull request
May 22, 2026
…n-out) (#209) Worker session worktrees spawned via the Agent tool with isolation: "worktree" land under .claude/worktrees/. They're per-session and transient, but `git status` on the main worktree flagged the directory as untracked after the 3-PR fan-out (PRs #203 + #204 + #205) left three locked agent worktrees on disk. The harness stop-hook treats untracked files as a session-blocking gate, so this entry keeps `git status` clean without an inline `git update-index --skip-worktree` workaround. CLAUDE.md + AGENTS.md lockstep — both gain a §Layout row noting the new ignore + the per-session-transient nature of the dir. Co-authored-by: Claude <noreply@anthropic.com>
dackclup
pushed a commit
that referenced
this pull request
May 24, 2026
…+ cache + path filter The docstring fix above triggered simulate (path filter matched compute/scoring/**) which cancelled at 45m02s on the timeout cap. ci-triage-engineer deep-dive (session 5, 2026-05-24) identified the recurring pattern across multiple PRs: the QR_SKIP_TIER2 kill-switch fully wired in compute/scoring/tier2.py:158 was NEVER set in .github/workflows/pre-merge-prod-sim.yml. Past mitigations: - PR #165 (1y non-reliance lookback): addressed a specific 5-year EDGAR backfill spike on one PR but left the universe-wide Tier-2 loop intact - PR-form4-2 (FORM4_FETCH_SKIP=1): saved form4 budget but left the Tier-2 (502 tickers × 10-K text fetch + 8-K fetch) running unconditionally (20-35m cold-cache cost) Recurrence tally on last 5 simulate runs: PR #165 19m01s ✅ warm PR #204 19m37s ✅ warm PR #222 16m56s ✅ warm PR #224 17m08s ✅ warm PR #230 45m15s ❌ CANCELLED (cold) Pattern is structural — when GitHub evicts the warm cache after a 7-day gap without a simulate-triggering PR, the next compute/scoring PR hits full cold. The 4-part permanent fix: (a) QR_SKIP_TIER2: "1" added to pre-merge-prod-sim.yml env block PRIMARY fix — eliminates the 20-35m cold-cache Tier-2 cost. Tradeoff: the non_reliance veto (enabled since PR 4g) is suppressed in simulate's diff output. Acceptable because simulate is informational-only (per workflow comment line 24); non_reliance veto correctness is covered by offline pytest. (b) compute/cache/edgar_form4 added to BOTH workflows' cache paths (compute-rankings.yml save+restore, pre-merge-prod-sim.yml restore only). Future-proofs for Phase 4.5e PR 5 when form4 weight promotion enables FORM4_FETCH_SKIP=0 on simulate. (c) Path filter widened in pre-merge-prod-sim.yml from compute/scoring/** + compute/features/** only to also include: - compute/ingest/** (fundamentals fetcher regressions) - compute/valuation/** (RIM / DCF / ensemble regressions) - compute/output/schemas.py (Pydantic schema bumps) - compute/main.py (orchestrator changes) - pyproject.toml (dep bumps affecting compute) (d) compute/scoring/tier2.py:154-155 docstring corrected The old comment said "_EIGHT_K_DEFENSES_ENABLED=False" but PR 4g (2026-05-17) flipped it to True. The non_reliance veto IS the active veto path now; updated comment explains the veto suppression tradeoff in simulate context. Expected outcomes: - Docstring-only PR in compute/scoring/ → simulate completes in ~12-15m warm or ~17-20m partial-cold (well under 45m cap) - Real scoring PR → same budget; composite-score diff unchanged - Cache eviction (7-day gap) → worst case fundamentals cold ~10-20m + no Tier-2 + no form4 = under cap - Weekly cron UNAFFECTED — QR_SKIP_TIER2 not set there; full run continues to populate the warm cache Companion benefit: this PR's re-pushed simulate is the LIVE VALIDATION of the fix — first sub-45m simulate on this branch proves the fix works. Files touched: - .github/workflows/pre-merge-prod-sim.yml (env + cache + paths) - .github/workflows/compute-rankings.yml (cache path only) - compute/scoring/tier2.py (stale comment) - CLAUDE.md + AGENTS.md (§Phase status note) YAML parse-verified; ruff clean; no compute / schema / scoring / valuation / Python behavior change. CLAUDE.md + AGENTS.md lockstep satisfied via the §Phase status notes. https://claude.ai/code/session_01JwntEE4PNAXSMkZxRA9BB4
9 tasks
dackclup
pushed a commit
that referenced
this pull request
May 24, 2026
…+ cache + path filter The docstring fix above triggered simulate (path filter matched compute/scoring/**) which cancelled at 45m02s on the timeout cap. ci-triage-engineer deep-dive (session 5, 2026-05-24) identified the recurring pattern across multiple PRs: the QR_SKIP_TIER2 kill-switch fully wired in compute/scoring/tier2.py:158 was NEVER set in .github/workflows/pre-merge-prod-sim.yml. Past mitigations: - PR #165 (1y non-reliance lookback): addressed a specific 5-year EDGAR backfill spike on one PR but left the universe-wide Tier-2 loop intact - PR-form4-2 (FORM4_FETCH_SKIP=1): saved form4 budget but left the Tier-2 (502 tickers × 10-K text fetch + 8-K fetch) running unconditionally (20-35m cold-cache cost) Recurrence tally on last 5 simulate runs: PR #165 19m01s ✅ warm PR #204 19m37s ✅ warm PR #222 16m56s ✅ warm PR #224 17m08s ✅ warm PR #230 45m15s ❌ CANCELLED (cold) Pattern is structural — when GitHub evicts the warm cache after a 7-day gap without a simulate-triggering PR, the next compute/scoring PR hits full cold. The 4-part permanent fix: (a) QR_SKIP_TIER2: "1" added to pre-merge-prod-sim.yml env block PRIMARY fix — eliminates the 20-35m cold-cache Tier-2 cost. Tradeoff: the non_reliance veto (enabled since PR 4g) is suppressed in simulate's diff output. Acceptable because simulate is informational-only (per workflow comment line 24); non_reliance veto correctness is covered by offline pytest. (b) compute/cache/edgar_form4 added to BOTH workflows' cache paths (compute-rankings.yml save+restore, pre-merge-prod-sim.yml restore only). Future-proofs for Phase 4.5e PR 5 when form4 weight promotion enables FORM4_FETCH_SKIP=0 on simulate. (c) Path filter widened in pre-merge-prod-sim.yml from compute/scoring/** + compute/features/** only to also include: - compute/ingest/** (fundamentals fetcher regressions) - compute/valuation/** (RIM / DCF / ensemble regressions) - compute/output/schemas.py (Pydantic schema bumps) - compute/main.py (orchestrator changes) - pyproject.toml (dep bumps affecting compute) (d) compute/scoring/tier2.py:154-155 docstring corrected The old comment said "_EIGHT_K_DEFENSES_ENABLED=False" but PR 4g (2026-05-17) flipped it to True. The non_reliance veto IS the active veto path now; updated comment explains the veto suppression tradeoff in simulate context. Expected outcomes: - Docstring-only PR in compute/scoring/ → simulate completes in ~12-15m warm or ~17-20m partial-cold (well under 45m cap) - Real scoring PR → same budget; composite-score diff unchanged - Cache eviction (7-day gap) → worst case fundamentals cold ~10-20m + no Tier-2 + no form4 = under cap - Weekly cron UNAFFECTED — QR_SKIP_TIER2 not set there; full run continues to populate the warm cache Companion benefit: this PR's re-pushed simulate is the LIVE VALIDATION of the fix — first sub-45m simulate on this branch proves the fix works. Files touched: - .github/workflows/pre-merge-prod-sim.yml (env + cache + paths) - .github/workflows/compute-rankings.yml (cache path only) - compute/scoring/tier2.py (stale comment) - CLAUDE.md + AGENTS.md (§Phase status note) YAML parse-verified; ruff clean; no compute / schema / scoring / valuation / Python behavior change. CLAUDE.md + AGENTS.md lockstep satisfied via the §Phase status notes. https://claude.ai/code/session_01JwntEE4PNAXSMkZxRA9BB4
dackclup
pushed a commit
that referenced
this pull request
May 24, 2026
…+ cache + path filter The docstring fix above triggered simulate (path filter matched compute/scoring/**) which cancelled at 45m02s on the timeout cap. ci-triage-engineer deep-dive (session 5, 2026-05-24) identified the recurring pattern across multiple PRs: the QR_SKIP_TIER2 kill-switch fully wired in compute/scoring/tier2.py:158 was NEVER set in .github/workflows/pre-merge-prod-sim.yml. Past mitigations: - PR #165 (1y non-reliance lookback): addressed a specific 5-year EDGAR backfill spike on one PR but left the universe-wide Tier-2 loop intact - PR-form4-2 (FORM4_FETCH_SKIP=1): saved form4 budget but left the Tier-2 (502 tickers × 10-K text fetch + 8-K fetch) running unconditionally (20-35m cold-cache cost) Recurrence tally on last 5 simulate runs: PR #165 19m01s ✅ warm PR #204 19m37s ✅ warm PR #222 16m56s ✅ warm PR #224 17m08s ✅ warm PR #230 45m15s ❌ CANCELLED (cold) Pattern is structural — when GitHub evicts the warm cache after a 7-day gap without a simulate-triggering PR, the next compute/scoring PR hits full cold. The 4-part permanent fix: (a) QR_SKIP_TIER2: "1" added to pre-merge-prod-sim.yml env block PRIMARY fix — eliminates the 20-35m cold-cache Tier-2 cost. Tradeoff: the non_reliance veto (enabled since PR 4g) is suppressed in simulate's diff output. Acceptable because simulate is informational-only (per workflow comment line 24); non_reliance veto correctness is covered by offline pytest. (b) compute/cache/edgar_form4 added to BOTH workflows' cache paths (compute-rankings.yml save+restore, pre-merge-prod-sim.yml restore only). Future-proofs for Phase 4.5e PR 5 when form4 weight promotion enables FORM4_FETCH_SKIP=0 on simulate. (c) Path filter widened in pre-merge-prod-sim.yml from compute/scoring/** + compute/features/** only to also include: - compute/ingest/** (fundamentals fetcher regressions) - compute/valuation/** (RIM / DCF / ensemble regressions) - compute/output/schemas.py (Pydantic schema bumps) - compute/main.py (orchestrator changes) - pyproject.toml (dep bumps affecting compute) (d) compute/scoring/tier2.py:154-155 docstring corrected The old comment said "_EIGHT_K_DEFENSES_ENABLED=False" but PR 4g (2026-05-17) flipped it to True. The non_reliance veto IS the active veto path now; updated comment explains the veto suppression tradeoff in simulate context. Expected outcomes: - Docstring-only PR in compute/scoring/ → simulate completes in ~12-15m warm or ~17-20m partial-cold (well under 45m cap) - Real scoring PR → same budget; composite-score diff unchanged - Cache eviction (7-day gap) → worst case fundamentals cold ~10-20m + no Tier-2 + no form4 = under cap - Weekly cron UNAFFECTED — QR_SKIP_TIER2 not set there; full run continues to populate the warm cache Companion benefit: this PR's re-pushed simulate is the LIVE VALIDATION of the fix — first sub-45m simulate on this branch proves the fix works. Files touched: - .github/workflows/pre-merge-prod-sim.yml (env + cache + paths) - .github/workflows/compute-rankings.yml (cache path only) - compute/scoring/tier2.py (stale comment) - CLAUDE.md + AGENTS.md (§Phase status note) YAML parse-verified; ruff clean; no compute / schema / scoring / valuation / Python behavior change. CLAUDE.md + AGENTS.md lockstep satisfied via the §Phase status notes. https://claude.ai/code/session_01JwntEE4PNAXSMkZxRA9BB4
dackclup
pushed a commit
that referenced
this pull request
May 24, 2026
…+ cache + path filter The docstring fix above triggered simulate (path filter matched compute/scoring/**) which cancelled at 45m02s on the timeout cap. ci-triage-engineer deep-dive (session 5, 2026-05-24) identified the recurring pattern across multiple PRs: the QR_SKIP_TIER2 kill-switch fully wired in compute/scoring/tier2.py:158 was NEVER set in .github/workflows/pre-merge-prod-sim.yml. Past mitigations: - PR #165 (1y non-reliance lookback): addressed a specific 5-year EDGAR backfill spike on one PR but left the universe-wide Tier-2 loop intact - PR-form4-2 (FORM4_FETCH_SKIP=1): saved form4 budget but left the Tier-2 (502 tickers × 10-K text fetch + 8-K fetch) running unconditionally (20-35m cold-cache cost) Recurrence tally on last 5 simulate runs: PR #165 19m01s ✅ warm PR #204 19m37s ✅ warm PR #222 16m56s ✅ warm PR #224 17m08s ✅ warm PR #230 45m15s ❌ CANCELLED (cold) Pattern is structural — when GitHub evicts the warm cache after a 7-day gap without a simulate-triggering PR, the next compute/scoring PR hits full cold. The 4-part permanent fix: (a) QR_SKIP_TIER2: "1" added to pre-merge-prod-sim.yml env block PRIMARY fix — eliminates the 20-35m cold-cache Tier-2 cost. Tradeoff: the non_reliance veto (enabled since PR 4g) is suppressed in simulate's diff output. Acceptable because simulate is informational-only (per workflow comment line 24); non_reliance veto correctness is covered by offline pytest. (b) compute/cache/edgar_form4 added to BOTH workflows' cache paths (compute-rankings.yml save+restore, pre-merge-prod-sim.yml restore only). Future-proofs for Phase 4.5e PR 5 when form4 weight promotion enables FORM4_FETCH_SKIP=0 on simulate. (c) Path filter widened in pre-merge-prod-sim.yml from compute/scoring/** + compute/features/** only to also include: - compute/ingest/** (fundamentals fetcher regressions) - compute/valuation/** (RIM / DCF / ensemble regressions) - compute/output/schemas.py (Pydantic schema bumps) - compute/main.py (orchestrator changes) - pyproject.toml (dep bumps affecting compute) (d) compute/scoring/tier2.py:154-155 docstring corrected The old comment said "_EIGHT_K_DEFENSES_ENABLED=False" but PR 4g (2026-05-17) flipped it to True. The non_reliance veto IS the active veto path now; updated comment explains the veto suppression tradeoff in simulate context. Expected outcomes: - Docstring-only PR in compute/scoring/ → simulate completes in ~12-15m warm or ~17-20m partial-cold (well under 45m cap) - Real scoring PR → same budget; composite-score diff unchanged - Cache eviction (7-day gap) → worst case fundamentals cold ~10-20m + no Tier-2 + no form4 = under cap - Weekly cron UNAFFECTED — QR_SKIP_TIER2 not set there; full run continues to populate the warm cache Companion benefit: this PR's re-pushed simulate is the LIVE VALIDATION of the fix — first sub-45m simulate on this branch proves the fix works. Files touched: - .github/workflows/pre-merge-prod-sim.yml (env + cache + paths) - .github/workflows/compute-rankings.yml (cache path only) - compute/scoring/tier2.py (stale comment) - CLAUDE.md + AGENTS.md (§Phase status note) YAML parse-verified; ruff clean; no compute / schema / scoring / valuation / Python behavior change. CLAUDE.md + AGENTS.md lockstep satisfied via the §Phase status notes. https://claude.ai/code/session_01JwntEE4PNAXSMkZxRA9BB4
dackclup
added a commit
that referenced
this pull request
May 24, 2026
…nent 45-min simulate fix (#230) * docs(form4): correct <rule10b5_1> → <aff10b5One> docstring precision per edgar-debugger live-XML verify The literature-searcher proof-of-life on 2026-05-23 (session 4) flagged a precision gap: the form4 module docstrings referenced the colloquial <rule10b5_1> XML tag name (a label, not the actual SEC EDGAR Ownership XML element). edgar-debugger re-audit on 2026-05-24 (session 5) fetched 3 live AAPL Form 4 XMLs from SEC EDGAR and confirmed the actual element is <aff10b5One> at ownershipDocument/aff10b5One — a DOCUMENT-LEVEL boolean (one per filing, covering ALL transactions in that Form 4), NOT a per-transaction attribute. Updated 7 references to use the canonical name: 5 code spots: - compute/scoring/form4_insider.py:20 (module docstring header) - compute/scoring/form4_insider.py:82 (§Footnote resolution) - compute/scoring/form4_insider.py:587 (inline _form4_to_transactions comment) - compute/scoring/form4_signals.py:129 (module docstring) - compute/output/schemas.py:200 (extreme_estimate_majority_count comment context) 2 doc spots: - CLAUDE.md §Phase status PR #224 entry "Access-path caveat" block (line 1352-1359) - AGENTS.md §Phase + version state PR #224 entry "Access-path caveat for cross-tool agents" block (line 947-951) Added §Footnote resolution architectural-gap note in compute/scoring/form4_insider.py: a filer who checks <aff10b5One>true at the document level but does NOT include the per-transaction footnote text (valid — the checkbox IS the formal affirmative defense, footnotes are supplemental) will slip past the current footnote-text fallback path and enter the opportunistic cohort incorrectly. Deferred to a follow-up PR that direct-parses the raw Form 4 XML for ownershipDocument/aff10b5One (edgartools doesn't expose it, so the implementation walks the filing text once per Ownership object). Tracked as the architectural follow-up after PR 4-eq. edgartools 5.31.5 verified as PyPI latest by edgar-debugger 2026-05-24 — no newer release adds a parse path for <aff10b5One>. detect_10b5_1_plan regex set (6 patterns) confirmed complete vs real-world footnote text — pattern "10b5-1" alone covers every common variant including Rule 10b5-1(c) subsection cites. No additions needed. Docstring-only PR — no compute / scoring / valuation / behavior change. schema_check clean (comment-only edit on schemas.py), ruff clean, tests unchanged at 1168+. CLAUDE.md + AGENTS.md lockstep satisfied via §Phase status in-flight notes. Companion stale-header reword: marked PR #229 "Security WARN cleanup" → "merged via PR #229" in CLAUDE.md + AGENTS.md. https://claude.ai/code/session_01JwntEE4PNAXSMkZxRA9BB4 * ci(simulate): permanent 45-min cancellation fix — wire QR_SKIP_TIER2 + cache + path filter The docstring fix above triggered simulate (path filter matched compute/scoring/**) which cancelled at 45m02s on the timeout cap. ci-triage-engineer deep-dive (session 5, 2026-05-24) identified the recurring pattern across multiple PRs: the QR_SKIP_TIER2 kill-switch fully wired in compute/scoring/tier2.py:158 was NEVER set in .github/workflows/pre-merge-prod-sim.yml. Past mitigations: - PR #165 (1y non-reliance lookback): addressed a specific 5-year EDGAR backfill spike on one PR but left the universe-wide Tier-2 loop intact - PR-form4-2 (FORM4_FETCH_SKIP=1): saved form4 budget but left the Tier-2 (502 tickers × 10-K text fetch + 8-K fetch) running unconditionally (20-35m cold-cache cost) Recurrence tally on last 5 simulate runs: PR #165 19m01s ✅ warm PR #204 19m37s ✅ warm PR #222 16m56s ✅ warm PR #224 17m08s ✅ warm PR #230 45m15s ❌ CANCELLED (cold) Pattern is structural — when GitHub evicts the warm cache after a 7-day gap without a simulate-triggering PR, the next compute/scoring PR hits full cold. The 4-part permanent fix: (a) QR_SKIP_TIER2: "1" added to pre-merge-prod-sim.yml env block PRIMARY fix — eliminates the 20-35m cold-cache Tier-2 cost. Tradeoff: the non_reliance veto (enabled since PR 4g) is suppressed in simulate's diff output. Acceptable because simulate is informational-only (per workflow comment line 24); non_reliance veto correctness is covered by offline pytest. (b) compute/cache/edgar_form4 added to BOTH workflows' cache paths (compute-rankings.yml save+restore, pre-merge-prod-sim.yml restore only). Future-proofs for Phase 4.5e PR 5 when form4 weight promotion enables FORM4_FETCH_SKIP=0 on simulate. (c) Path filter widened in pre-merge-prod-sim.yml from compute/scoring/** + compute/features/** only to also include: - compute/ingest/** (fundamentals fetcher regressions) - compute/valuation/** (RIM / DCF / ensemble regressions) - compute/output/schemas.py (Pydantic schema bumps) - compute/main.py (orchestrator changes) - pyproject.toml (dep bumps affecting compute) (d) compute/scoring/tier2.py:154-155 docstring corrected The old comment said "_EIGHT_K_DEFENSES_ENABLED=False" but PR 4g (2026-05-17) flipped it to True. The non_reliance veto IS the active veto path now; updated comment explains the veto suppression tradeoff in simulate context. Expected outcomes: - Docstring-only PR in compute/scoring/ → simulate completes in ~12-15m warm or ~17-20m partial-cold (well under 45m cap) - Real scoring PR → same budget; composite-score diff unchanged - Cache eviction (7-day gap) → worst case fundamentals cold ~10-20m + no Tier-2 + no form4 = under cap - Weekly cron UNAFFECTED — QR_SKIP_TIER2 not set there; full run continues to populate the warm cache Companion benefit: this PR's re-pushed simulate is the LIVE VALIDATION of the fix — first sub-45m simulate on this branch proves the fix works. Files touched: - .github/workflows/pre-merge-prod-sim.yml (env + cache + paths) - .github/workflows/compute-rankings.yml (cache path only) - compute/scoring/tier2.py (stale comment) - CLAUDE.md + AGENTS.md (§Phase status note) YAML parse-verified; ruff clean; no compute / schema / scoring / valuation / Python behavior change. CLAUDE.md + AGENTS.md lockstep satisfied via the §Phase status notes. https://claude.ai/code/session_01JwntEE4PNAXSMkZxRA9BB4 * docs(workflow): rebase-discipline § + parallel-PR §Phase status collision §Gotcha Closes the recurring "merge ไม่ได้ หลังแก้แล้วกลับมาเป็นอีก" pattern surfaced 2026-05-24: PR #230 hit mergeable_state=dirty twice in a single session — first when PR #229 (security WARN cleanup) landed on main mid-iteration, then again when PR #232 + PR #233 (LedgerCraft A1 + A2) landed before Mark-Ready. ROOT CAUSE: every PR adds a "**X in flight (this PR)**" entry at the SAME insertion line in CLAUDE.md §Phase status + AGENTS.md §Phase + version state (just before "**Next deliverables**" / "## Claude-Code-specific tooling"). Two parallel PRs both touch that single line → `git merge` cannot auto-resolve two distinct adds at the same line → `mergeable_state: dirty`. The conflict is BENIGN (both adds are legitimate; resolution is always "keep both in chronological order") but git can't auto-detect that. THREE COMPANION EDITS bundled here: (a) CLAUDE.md §Conventions — new bullet "Rebase onto origin/main before flipping any PR Draft → Ready". Includes the operational `git fetch origin main && git rebase origin/main` recipe + the "keep both entries in chronological order (older PR first, your entry second)" resolution discipline. (b) CLAUDE.md §Gotchas — new "Parallel-PR §Phase status collision pattern" entry recording the recurring symptom (PR #230 ×2 on 2026-05-24 against PR #229 + PR #232 + PR #233) + the local-mitigation workflow + a forward-looking structural follow-up note: consider moving the "in flight" sub-block to a side file (PHASE_STATUS_INFLIGHT.md) that's append-only- per-PR; on merge a housekeeping commit moves the entry into CLAUDE.md proper. Not yet adopted — trade-off is extra discipline at merge time. (c) AGENTS.md mirror — cross-tool equivalent for Copilot / Cursor / Devin contributors, emphasising the rebase-before-Mark-Ready discipline applies to ALL agent runtimes (not Claude-Code- specific). ALSO bundled as a 3rd in-flight entry in the §Phase status block documenting THIS PR's scope expansion (Parts 1+2+3 now). No infrastructure / CI / behavior change — pure doc discipline. Future contributors who hit the same conflict find the §Gotchas entry + the §Conventions recipe + AGENTS.md note and resolve in seconds, instead of re-discovering the pattern. https://claude.ai/code/session_01JwntEE4PNAXSMkZxRA9BB4 * ci(simulate): Part 4 — wire QR_SKIP_FUNDAMENTALS to actually close the 45m cap Part 2's QR_SKIP_TIER2 fix was necessary but NOT sufficient. Re-pushing the rebased branch on 2026-05-24 07:06 UTC still hit simulate cancelled at 45m15s. ci-triage-engineer deep-dive #2 identified the gap: compute/main.py has THREE independent SEC EDGAR ThreadPoolExecutor loops, and Part 2 only killed ONE (Step 4b — Tier-2): Step 2 (compute/main.py:717) — fundamentals snapshot (502 tickers × SEC companyfacts XBRL); NOT skipped by QR_SKIP_TIER2 Step 3 (compute/main.py:785) — annual fundamentals history; same Step 4b (compute/main.py:972) — Tier-2 / 8-K; killed by Part 2 Cold-cache cost of Steps 2+3 alone is 25-50m per CLAUDE.md §Gotchas — enough to fill the entire 45m simulate budget. Worse, the cache freshness gate at compute/ingest/fundamentals.py:917 (_is_fresh()) checks the filed_date INSIDE each cached parquet against FUNDAMENTALS_REFETCH_DAYS=45. On a partial cache hit, any ticker with a > 45d-old most-recent filing still triggers a live EDGAR round-trip. Part 4 fix — QR_SKIP_FUNDAMENTALS=1 escape hatch: (a) compute/ingest/fundamentals.py:fetch_fundamentals — env-var check added at the top of the function (BEFORE _require_identity()), so the cached snapshot is returned unconditionally when set, without a freshness check OR an EDGAR identity requirement. Falls through to live fetch if no cache exists (cold runner has nothing to read). (b) compute/ingest/fundamentals.py:fetch_fundamentals_history — mirror pattern: returns the cached annual parquet without the 180d age check when QR_SKIP_FUNDAMENTALS=1 AND force_refresh=False. Same fallthrough. (c) .github/workflows/pre-merge-prod-sim.yml — adds QR_SKIP_FUNDAMENTALS: "1" to the env block alongside QR_SKIP_TIER2 and FORM4_FETCH_SKIP. Inline comment explains the root cause + the safety reasoning. SAFE for simulate because: - The workflow's purpose is composite-score-DIFF detection vs main's COMMITTED rankings.json - Both sides (PR branch + main) were produced from the same upstream fundamentals input (the cache the weekly cron wrote) - Using that cache without re-fetch is the CORRECT input for the diff - It's NOT a quality compromise — re-fetching live would give the PR branch newer data than main has, making the diff confusing rather than accurate Weekly cron (compute-rankings.yml) does NOT set QR_SKIP_FUNDAMENTALS — full live fetch still runs there and populates the warm cache for future simulate restores. Expected outcome: simulate completes in 8-12m on a cache-hit restore (no live SEC fetch in any of Steps 2, 3, or 4b). Live validation: re-push after this commit → simulate green under 25m on this PR. Tests verified: tests/test_features/test_fundamentals.py + tests/test_workflow_cache_coverage.py = 24 pass / 6 skipped. ruff clean. YAML parse-verified. schema_check in sync. CLAUDE.md §Phase status + AGENTS.md §Phase + version state both updated with the Part 4 explanation. Future contributors editing pre-merge-prod-sim.yml should now keep all THREE escape-hatch env vars set together: FORM4_FETCH_SKIP + QR_SKIP_TIER2 + QR_SKIP_FUNDAMENTALS. https://claude.ai/code/session_01JwntEE4PNAXSMkZxRA9BB4 --------- Co-authored-by: Claude <noreply@anthropic.com>
dackclup
pushed a commit
that referenced
this pull request
May 24, 2026
Addresses release-captain BLOCKED-ON-PRE-FLIGHT blocker #3 from the v1.3.0 tag attempt — PHASE_STATUS.md / SKILL.md / WORKFLOW.md were 3 days + ~32 PRs stale (last touched PR #171, 2026-05-21). Brings all three docs current to main HEAD 1ff6c11 so the release-captain ladder can re-attempt cleanly. PHASE_STATUS.md - Header date 2026-05-21 → 2026-05-24 - Current state table: schema 0.9.4-phase4h.4 → 0.10.2-phase4.5e; defense layer 27 → 32 emitted flags; subagent inventory 14 → 18 (named tier roster — 4 opus / 14 sonnet); skill inventory 42 → 43; production run a16c887 → 9015748 (cron #3 2026-05-23); release- tag line annotated with v1.3.0 target pending - Recently-merged block: refreshed to PR #170 → PR #237 (~36 entries with commit shas, chronological), drops the stale PR #147-#169 block - Next-deliverables list: 5 items updated — Phase 4.5e PR 5 cluster weight promotion / Issue #67 sector-CoE flip / v1.3.0 release tag gate / Phase 4i.1-4j.1-4k.1 factor integrations / Phase 5 ML meta-learner - Open issues line: drops resolved #155 (closed via PR #160), refreshes #41 (15 open advisories, zero exploitability on static-export), refreshes #67 (data-collection merged PR #204) SKILL.md - Schema-version table: 7 new rows added in reverse-chronological order (matches existing 0.9.x convention) for `0.9.5` → `0.9.6` → `0.9.7` → `0.9.8` → `0.10.0` → `0.10.1` → `0.10.2` covering PRs #180/#181/#183/#204/#205/#222/#224. Each row carries PR # + 1-line scope + backward-compat note + literature anchor. WORKFLOW.md - Phase Overview table 4.5 row marked ✅ DONE 2026-05-23 + 10b5-1 filter scope note - SEC Filing Roadmap Form 4 row flipped "planned" → "active" with 4-PR ladder reference (#167/#205/#222/#224 + 100% coverage on cron #3) - Phase 4.5e task list — 5 items flipped `[ ]` → `[x]` with per-PR commits + methodology-scientist Mode B verdicts inline + Aboody et al. 2010 §3.2 weight-promotion gate noted - Phase 4.5 Acceptance Criteria — all 9 items flipped to `[x]` with completion evidence (cron #3 / methodology verdicts / PR refs) - Phase 4.5f tag item — flipped `[ ]` → `[x]` (`v1.2.0-phase4.5` cut 2026-05-17 at 6d414a9) PHASE_STATUS_INFLIGHT.md - Append new "(this PR)" entry under In-flight section per the PR #237 side-file convention. Documents the doc-refresh scope + cross-refs to release-captain blockers 1/2/4/5 still pending. Lockstep - PR #237's PHASE_STATUS_INFLIGHT.md side-file pattern handles the §Conventions "ship with every PR" rule for this doc-only PR - No CLAUDE.md / AGENTS.md substantive change required — the in-flight entry lives in the side-file per the new convention - No compute / schema / scoring / valuation / frontend / Python / TypeScript code change - Unblocks v1.3.0 tag blocker #3; blockers 1 (wrong-branch), 2 (pyproject.toml 0.3.0 → 1.3.0), 4 (production output 1 cron cycle behind code), and 5 (release notes draft scope) still need resolution before tag cut
5 tasks
dackclup
pushed a commit
that referenced
this pull request
May 24, 2026
Addresses release-captain BLOCKED-ON-PRE-FLIGHT blocker #3 from the v1.3.0 tag attempt — PHASE_STATUS.md / SKILL.md / WORKFLOW.md were 3 days + ~32 PRs stale (last touched PR #171, 2026-05-21). Brings all three docs current to main HEAD 1ff6c11 so the release-captain ladder can re-attempt cleanly. PHASE_STATUS.md - Header date 2026-05-21 → 2026-05-24 - Current state table: schema 0.9.4-phase4h.4 → 0.10.2-phase4.5e; defense layer 27 → 32 emitted flags; subagent inventory 14 → 18 (named tier roster — 4 opus / 14 sonnet); skill inventory 42 → 43; production run a16c887 → 9015748 (cron #3 2026-05-23); release- tag line annotated with v1.3.0 target pending - Recently-merged block: refreshed to PR #170 → PR #237 (~36 entries with commit shas, chronological), drops the stale PR #147-#169 block - Next-deliverables list: 5 items updated — Phase 4.5e PR 5 cluster weight promotion / Issue #67 sector-CoE flip / v1.3.0 release tag gate / Phase 4i.1-4j.1-4k.1 factor integrations / Phase 5 ML meta-learner - Open issues line: drops resolved #155 (closed via PR #160), refreshes #41 (15 open advisories, zero exploitability on static-export), refreshes #67 (data-collection merged PR #204) SKILL.md - Schema-version table: 7 new rows added in reverse-chronological order (matches existing 0.9.x convention) for `0.9.5` → `0.9.6` → `0.9.7` → `0.9.8` → `0.10.0` → `0.10.1` → `0.10.2` covering PRs #180/#181/#183/#204/#205/#222/#224. Each row carries PR # + 1-line scope + backward-compat note + literature anchor. WORKFLOW.md - Phase Overview table 4.5 row marked ✅ DONE 2026-05-23 + 10b5-1 filter scope note - SEC Filing Roadmap Form 4 row flipped "planned" → "active" with 4-PR ladder reference (#167/#205/#222/#224 + 100% coverage on cron #3) - Phase 4.5e task list — 5 items flipped `[ ]` → `[x]` with per-PR commits + methodology-scientist Mode B verdicts inline + Aboody et al. 2010 §3.2 weight-promotion gate noted - Phase 4.5 Acceptance Criteria — all 9 items flipped to `[x]` with completion evidence (cron #3 / methodology verdicts / PR refs) - Phase 4.5f tag item — flipped `[ ]` → `[x]` (`v1.2.0-phase4.5` cut 2026-05-17 at 6d414a9) PHASE_STATUS_INFLIGHT.md - Append new "(this PR)" entry under In-flight section per the PR #237 side-file convention. Documents the doc-refresh scope + cross-refs to release-captain blockers 1/2/4/5 still pending. Lockstep - PR #237's PHASE_STATUS_INFLIGHT.md side-file pattern handles the §Conventions "ship with every PR" rule for this doc-only PR - No CLAUDE.md / AGENTS.md substantive change required — the in-flight entry lives in the side-file per the new convention - No compute / schema / scoring / valuation / frontend / Python / TypeScript code change - Unblocks v1.3.0 tag blocker #3; blockers 1 (wrong-branch), 2 (pyproject.toml 0.3.0 → 1.3.0), 4 (production output 1 cron cycle behind code), and 5 (release notes draft scope) still need resolution before tag cut
dackclup
added a commit
that referenced
this pull request
May 24, 2026
…239) Addresses release-captain BLOCKED-ON-PRE-FLIGHT blocker #3 from the v1.3.0 tag attempt — PHASE_STATUS.md / SKILL.md / WORKFLOW.md were 3 days + ~32 PRs stale (last touched PR #171, 2026-05-21). Brings all three docs current to main HEAD 1ff6c11 so the release-captain ladder can re-attempt cleanly. PHASE_STATUS.md - Header date 2026-05-21 → 2026-05-24 - Current state table: schema 0.9.4-phase4h.4 → 0.10.2-phase4.5e; defense layer 27 → 32 emitted flags; subagent inventory 14 → 18 (named tier roster — 4 opus / 14 sonnet); skill inventory 42 → 43; production run a16c887 → 9015748 (cron #3 2026-05-23); release- tag line annotated with v1.3.0 target pending - Recently-merged block: refreshed to PR #170 → PR #237 (~36 entries with commit shas, chronological), drops the stale PR #147-#169 block - Next-deliverables list: 5 items updated — Phase 4.5e PR 5 cluster weight promotion / Issue #67 sector-CoE flip / v1.3.0 release tag gate / Phase 4i.1-4j.1-4k.1 factor integrations / Phase 5 ML meta-learner - Open issues line: drops resolved #155 (closed via PR #160), refreshes #41 (15 open advisories, zero exploitability on static-export), refreshes #67 (data-collection merged PR #204) SKILL.md - Schema-version table: 7 new rows added in reverse-chronological order (matches existing 0.9.x convention) for `0.9.5` → `0.9.6` → `0.9.7` → `0.9.8` → `0.10.0` → `0.10.1` → `0.10.2` covering PRs #180/#181/#183/#204/#205/#222/#224. Each row carries PR # + 1-line scope + backward-compat note + literature anchor. WORKFLOW.md - Phase Overview table 4.5 row marked ✅ DONE 2026-05-23 + 10b5-1 filter scope note - SEC Filing Roadmap Form 4 row flipped "planned" → "active" with 4-PR ladder reference (#167/#205/#222/#224 + 100% coverage on cron #3) - Phase 4.5e task list — 5 items flipped `[ ]` → `[x]` with per-PR commits + methodology-scientist Mode B verdicts inline + Aboody et al. 2010 §3.2 weight-promotion gate noted - Phase 4.5 Acceptance Criteria — all 9 items flipped to `[x]` with completion evidence (cron #3 / methodology verdicts / PR refs) - Phase 4.5f tag item — flipped `[ ]` → `[x]` (`v1.2.0-phase4.5` cut 2026-05-17 at 6d414a9) PHASE_STATUS_INFLIGHT.md - Append new "(this PR)" entry under In-flight section per the PR #237 side-file convention. Documents the doc-refresh scope + cross-refs to release-captain blockers 1/2/4/5 still pending. Lockstep - PR #237's PHASE_STATUS_INFLIGHT.md side-file pattern handles the §Conventions "ship with every PR" rule for this doc-only PR - No CLAUDE.md / AGENTS.md substantive change required — the in-flight entry lives in the side-file per the new convention - No compute / schema / scoring / valuation / frontend / Python / TypeScript code change - Unblocks v1.3.0 tag blocker #3; blockers 1 (wrong-branch), 2 (pyproject.toml 0.3.0 → 1.3.0), 4 (production output 1 cron cycle behind code), and 5 (release notes draft scope) still need resolution before tag cut Co-authored-by: Claude <noreply@anthropic.com>
2 tasks
dackclup
added a commit
that referenced
this pull request
May 28, 2026
… open-issues list (#291) Closes deferred substance items from PR #286 + PR #290 that opted to keep scope tight under the `pointer-to-CLAUDE.md` delegation pattern. Changes (1 file, AGENTS.md): - Line 377 — Production-verified run pointer bumped from cron #51 (`b1588b2a`, 2026-05-14) → cron #69 (`233117ac`, 13m 16s warm-cache, 2026-05-28). Cross-tool agents now have the current known-good baseline; verified by defense-layer-auditor + stock-detail-auditor on schema `0.10.7-phase4.6`. - Lines 380-382 — "Open Phase 4+ issues" list refreshed from 4 → 11 entries. Removed stale qualifier on #67 (PR #204 already landed data-collection; #67 is Phase 4 follow-up not Phase 5+). Added 7 missing issues: #115 (JKP license) · #130 (Q3 cohort) · #137 (9arm-skills license deadline 2026-06-17) · #150 (Phase 2-3 epic) · #287 (FORM4 revert + durable timeout) · #288 (GOOG/GOOGL XBRL) · #289 (NVR DQIC false positive). Added zero-exploitability context to #41. Doc-only PR. No compute / schema / scoring / valuation / frontend / Python / TS code change. CLAUDE.md substance untouched this PR (PR #286 + PR #290 already bumped it); this PR closes the AGENTS.md side of the lockstep. PHASE_STATUS_INFLIGHT.md side-file satisfies §Conventions lockstep per PR #237 convention. Co-authored-by: Claude <noreply@anthropic.com>
This was referenced May 28, 2026
dackclup
added a commit
that referenced
this pull request
May 28, 2026
…ector Ke) (#294) Closes Issue #67 — flips `USE_SECTOR_COE` False → True per methodology- scientist Mode B verdict 2026-05-28 + cron #69 empirical confirmation. Consumes the data-collection module landed in PR #204 (2026-05-22). Empirical gate satisfied (cron #69 metadata): - value_trap_risk_count_without_sector_coe: 132 (flat 10% Ke baseline) - value_trap_risk_count_with_sector_coe: 109 (per-GICS Ke 6-12%) - Delta: -23 tickers, -17.4% reduction - Absolute landing point (109) inside original PR #204 target band [80, 110] - 38%-vs-17% spread vs original projection explained by baseline drift (PR #166 Issue #11 RIM equity-denominator fix already removed ~44 FP from the pre-PR-#204 ~176 baseline; proportional difference is baseline drift, not signal failure) Methodology-scientist verdict (Damodaran 2019 Ch. 8.4 §"Industry Beta" + NYU January 2025 betas dataset): APPROVED. Lower-Ke sectors (Utilities/REIT/Staples) gain leniency; higher-Ke sectors (Tech/Energy) gain stringency; net is negative because S&P 500 has more market-cap exposure to defensives than the flat 10% baseline assumed. Changes (5 files): - compute/config.py:86 — USE_SECTOR_COE flipped True. Docstring rewritten to cite methodology verdict + cron #69 numbers + load-bearing-default semantics (flipping back requires separate methodology-scientist verdict). - compute/scoring/cost_of_equity.py:73-79 — module docstring updated to reflect post-flip state. - tests/test_scoring/test_value_trap_risk_sector_coe.py:134-156 — test renamed (`_flat_coe_path_unchanged_...` → `_use_sector_coe_default_ post_issue_67_flip`); assertion inverted from `not config.USE_SECTOR_COE` → `config.USE_SECTOR_COE`. Would BREAK on flip otherwise. - tests/test_config.py — new `test_use_sector_coe_flipped_true` pin added per methodology-scientist Q5b recommendation (Phase 2.4/2.5 convention: pin every config-value flip in tests/test_config.py). Impact (first methodology-affecting flip post-v1.4.0): - value_trap_risk universe count: 132 → 109 (per cron #69 dual-counter) - 23 newly-not-flagged tickers regain RIM as a contributing valuation method - Composite ranks shift for the cyclical-vs-defensive cohort (magnitude bounded by per-pillar weight + median-of-6 aggregation) - Rule 16 + Top-5 rotation mechanics UNCHANGED (only Ke parameter shift) - No schema change (Metadata dual-counter fields already shipped PR #204) - No new defense flag (defense layer count unchanged at 33 declared) Verification: - `ruff check .` — PASS - `python -m pytest tests/test_config.py tests/test_scoring/test_value_trap_risk_sector_coe.py tests/test_scoring/test_cost_of_equity.py tests/test_valuation/ -q -m "not network"` — 256 passed - `python -m pytest tests/ -q -m "not network" --ignore=tests/test_validation` — 1207 passed - methodology-scientist Mode B verdict — APPROVED (LITERATURE-ANCHORED) Deferred follow-ups (NOT in this PR): - Per-sector delta instrumentation (Metadata.value_trap_risk_delta_by_sector) — separate PR; universe-wide count sufficient for THIS flip per the gate contract - Q3 2026-08-19 cohort audit — natural review point for per-sector shape verification (~12 weekly crons of post-flip data by then; already in the issue #130 pre-prep checklist) Co-authored-by: Claude <noreply@anthropic.com>
7 tasks
dackclup
pushed a commit
that referenced
this pull request
May 28, 2026
… instrumentation Methodology-scientist Mode B Q2 follow-up deferred from PR #294 (sector- CoE flip, 2026-05-28 05:39 UTC). Adds `Metadata.value_trap_risk_delta_by_sector: dict[str, int] | None` so Q3 2026-08-19 quarterly cohort audit has visible per-sector shape evidence — not just the aggregate `value_trap_risk_count_*_sector_coe` scalars that landed in PR #204. Schema PATCH bump 0.10.9 → 0.10.10-phase4.6 (additive Metadata-only). Methodology context (Damodaran 2019 Ch. 8.4 §"Industry Beta"): After `USE_SECTOR_COE = True` per-sector Ke replaces the flat 10% baseline at SECTOR_COST_OF_EQUITY (11 GICS sectors, Ke 6%-12%). Directional predictions: - Lower-Ke sectors (Utilities ~6-7% / Real Estate ~7-8% / Consumer Staples ~7-8%): ROE ≥ Ke threshold relaxed → fewer RIM-skipped → POSITIVE delta (sector DROPPED flags) - Higher-Ke sectors (Information Technology ~11-12% / Energy ~10-12%): ROE ≥ Ke tightened → more RIM-skipped → NEGATIVE delta - Neutral sectors (6 GICS sectors at ~9-11%): small delta near zero Cron #69 + Run #71 universe-wide already confirmed the aggregate: 132 → 109 (−23 tickers, −17.4%). This PR breaks the −23 down by sector. Scope (10 files, additive only): - compute/output/schemas.py — new value_trap_risk_delta_by_sector field with full docstring (methodology-scientist verdict + Damodaran 2019 anchor + direction semantics) - frontend/lib/types.ts — mirror TS field as Record<string, number> | null - frontend/lib/schema-snapshot.json — regenerated via --update-snapshot - compute/config.py — SCHEMA_VERSION = "0.10.10-phase4.6" - compute/main.py — 3 surgical edits mirroring existing scalar dual-counter pattern (init two dict[str, int] counters / per-sector increment co-located with the existing scalar bump in both branches / delta computation in Metadata constructor) - tests/test_config.py — schema version pin bump + docstring rewrite - tests/test_output/test_value_trap_delta_by_sector_schema.py (NEW) — 2 active GREEN schema-contract tests (mirror test_wall_clock_schema.py pattern from PR #297) - CLAUDE.md — §Phase status pointer block refresh - AGENTS.md — open-issues #67 status: flip landed + per-sector follow-up in flight this PR - PHASE_STATUS_INFLIGHT.md — full in-flight entry per PR #237 side-file convention Implementation note: Per-sector dict construction uses `sorted(set(without) | set(with))` for stable key ordering; `.get(sec, 0)` fallback handles sectors appearing in only one path; `{} or None` falls back to None when both dicts are empty (test-mode universe). Co-located with the existing scalar bump in both `_rim_flat` (flat-Ke) and `_rim_sector` (sector-Ke) branches at the same `value_trap_risk_roe_below_cost_of_equity` reason guard — scalar and dict always stay in lockstep. Verification ladder: - ruff check . PASS - python -m compute.output.schema_check PASS (triple in sync 0.10.10) - pytest tests/test_config.py -v 11/11 PASS (pin held) - python -m pytest tests/test_output/ 2/2 NEW PASS - Full offline suite via test-engineer 1367 → 1369 (+2 NEW) Pre-push 3-reviewer gate: - schema-sentinel (sonnet) PASS (52 fields, triple aligned, PATCH bump correct, snapshot alphabetical ordering held) - test-engineer (sonnet) GREEN (2/2 new tests pass, 1367 → 1369, 0 regressions, 0 skipped stubs) - quantrank-reviewer (opus) READY-TO-PUSH (0 FAIL, 4 WARN all pre-existing PR-#297-era drift, defer to next housekeeping PR — incl. SKILL.md/PHASE_STATUS.md schema-table tops still on 0.10.8) Empirical validation gate (post-merge, next cron Run #72): - metadata.value_trap_risk_delta_by_sector populates as non-null dict - Damodaran shape directionally correct: Util/Real Estate/Staples POSITIVE, Information Technology/Energy NEGATIVE - sum(delta.values()) == without_sector_coe_count - with_sector_coe_count (= 23 per Run #71 universe-wide; matches within rounding) Note: per-sector accumulation runs in the Step 8 per-ticker loop, INDEPENDENT of cache-v5 cache busting (PR #298). Field populates on next cron regardless of warm/cold fetch path. Hard constraints honored: - No new defense flag · No scoring formula change · No Rule 16 / Top-5 violation - Additive-only schema change (PATCH bump) - Field nullable per Rule 18 graceful-degradation - Phase 4.5e PR 5 (cluster weight promotion) gate-data UNCHANGED — independent track Methodology decision: methodology-scientist verdict NOT re-requested — this is the EXACT field shape Mode B Q2 verdict from PR #294 explicitly authorized. Future re-trigger only if post-merge cron shows sector breakdown contradicting Damodaran prediction OR Q3 2026-08-19 audit reads ≥ 6 crons of data and per-sector decay pattern needs interpretation. https://claude.ai/code/session_01AGU8d6pm4u2fQQ5cebg9qa
dackclup
added a commit
that referenced
this pull request
May 28, 2026
…ta instrumentation (#300) Methodology-scientist Mode B Q2 follow-up deferred from PR #294 (sector- CoE flip, 2026-05-28 05:39 UTC). Adds `Metadata.value_trap_risk_delta_by_sector: dict[str, int] | None` so Q3 2026-08-19 quarterly cohort audit has visible per-sector shape evidence — not just the aggregate `value_trap_risk_count_*_sector_coe` scalars that landed in PR #204. Schema PATCH bump 0.10.9 → 0.10.10-phase4.6 (additive Metadata-only). Methodology context (Damodaran 2019 Ch. 8.4 §"Industry Beta"): After `USE_SECTOR_COE = True` per-sector Ke replaces the flat 10% baseline at SECTOR_COST_OF_EQUITY (11 GICS sectors, Ke 6%-12%). Directional predictions: - Lower-Ke sectors (Utilities ~6-7% / Real Estate ~7-8% / Consumer Staples ~7-8%): ROE ≥ Ke threshold relaxed → fewer RIM-skipped → POSITIVE delta (sector DROPPED flags) - Higher-Ke sectors (Information Technology ~11-12% / Energy ~10-12%): ROE ≥ Ke tightened → more RIM-skipped → NEGATIVE delta - Neutral sectors (6 GICS sectors at ~9-11%): small delta near zero Cron #69 + Run #71 universe-wide already confirmed the aggregate: 132 → 109 (−23 tickers, −17.4%). This PR breaks the −23 down by sector. Scope (10 files, additive only): - compute/output/schemas.py — new value_trap_risk_delta_by_sector field with full docstring (methodology-scientist verdict + Damodaran 2019 anchor + direction semantics) - frontend/lib/types.ts — mirror TS field as Record<string, number> | null - frontend/lib/schema-snapshot.json — regenerated via --update-snapshot - compute/config.py — SCHEMA_VERSION = "0.10.10-phase4.6" - compute/main.py — 3 surgical edits mirroring existing scalar dual-counter pattern (init two dict[str, int] counters / per-sector increment co-located with the existing scalar bump in both branches / delta computation in Metadata constructor) - tests/test_config.py — schema version pin bump + docstring rewrite - tests/test_output/test_value_trap_delta_by_sector_schema.py (NEW) — 2 active GREEN schema-contract tests (mirror test_wall_clock_schema.py pattern from PR #297) - CLAUDE.md — §Phase status pointer block refresh - AGENTS.md — open-issues #67 status: flip landed + per-sector follow-up in flight this PR - PHASE_STATUS_INFLIGHT.md — full in-flight entry per PR #237 side-file convention Implementation note: Per-sector dict construction uses `sorted(set(without) | set(with))` for stable key ordering; `.get(sec, 0)` fallback handles sectors appearing in only one path; `{} or None` falls back to None when both dicts are empty (test-mode universe). Co-located with the existing scalar bump in both `_rim_flat` (flat-Ke) and `_rim_sector` (sector-Ke) branches at the same `value_trap_risk_roe_below_cost_of_equity` reason guard — scalar and dict always stay in lockstep. Verification ladder: - ruff check . PASS - python -m compute.output.schema_check PASS (triple in sync 0.10.10) - pytest tests/test_config.py -v 11/11 PASS (pin held) - python -m pytest tests/test_output/ 2/2 NEW PASS - Full offline suite via test-engineer 1367 → 1369 (+2 NEW) Pre-push 3-reviewer gate: - schema-sentinel (sonnet) PASS (52 fields, triple aligned, PATCH bump correct, snapshot alphabetical ordering held) - test-engineer (sonnet) GREEN (2/2 new tests pass, 1367 → 1369, 0 regressions, 0 skipped stubs) - quantrank-reviewer (opus) READY-TO-PUSH (0 FAIL, 4 WARN all pre-existing PR-#297-era drift, defer to next housekeeping PR — incl. SKILL.md/PHASE_STATUS.md schema-table tops still on 0.10.8) Empirical validation gate (post-merge, next cron Run #72): - metadata.value_trap_risk_delta_by_sector populates as non-null dict - Damodaran shape directionally correct: Util/Real Estate/Staples POSITIVE, Information Technology/Energy NEGATIVE - sum(delta.values()) == without_sector_coe_count - with_sector_coe_count (= 23 per Run #71 universe-wide; matches within rounding) Note: per-sector accumulation runs in the Step 8 per-ticker loop, INDEPENDENT of cache-v5 cache busting (PR #298). Field populates on next cron regardless of warm/cold fetch path. Hard constraints honored: - No new defense flag · No scoring formula change · No Rule 16 / Top-5 violation - Additive-only schema change (PATCH bump) - Field nullable per Rule 18 graceful-degradation - Phase 4.5e PR 5 (cluster weight promotion) gate-data UNCHANGED — independent track Methodology decision: methodology-scientist verdict NOT re-requested — this is the EXACT field shape Mode B Q2 verdict from PR #294 explicitly authorized. Future re-trigger only if post-merge cron shows sector breakdown contradicting Damodaran prediction OR Q3 2026-08-19 audit reads ≥ 6 crons of data and per-sector decay pattern needs interpretation. https://claude.ai/code/session_01AGU8d6pm4u2fQQ5cebg9qa Co-authored-by: Claude <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Part of #67. Behind
USE_SECTOR_COE = Falsedefault — data-collection PR per Rule 18. Flip-to-True follow-up after ≥ 1 cron measures the delta-flag-count.What this PR does
compute/scoring/cost_of_equity.py— GICS-keyed Damodaran 2019 sector cost-of-equity tableconfig.USE_SECTOR_COE = False(OFF by default — no production change)compute/valuation/ensemble.py→rim_fair_priceDamodaran 2019 Table 8.4 + NYU January 2025 dataset — per-sector values
Default fallback: 10.0% (unchanged from
COST_OF_EQUITYconstant in config.py).Citation: Damodaran, A. (2019). Investment Valuation (3rd ed., Table 8.4). Wiley. + Damodaran NYU Stern "Cost of Equity by Industry — US" (January 2025, https://pages.stern.nyu.edu/~adamodar/).
Rule 18 observability fields added to Metadata (schema 0.9.7-phase4h.7 → 0.9.8-phase4h.8)
Both counts are computed every cron regardless of the flag, so the delta is measurable before the production flip. Expected direction:
value_trap_riskdrops from baseline ~176 toward ~80-110 (cyclical sectors whose 3y-mean ROE sits between sector Ke and flat 10% get un-flagged); Utilities/REITs with sector Ke < 10% may pick up new flags, so net empirical confirmation is required before flipping.Files changed
compute/scoring/cost_of_equity.pycompute/config.pycompute/valuation/ensemble.pycompute/main.pycompute/output/schemas.pyfrontend/lib/types.tsfrontend/lib/schema-snapshot.jsontests/test_scoring/test_cost_of_equity.pytests/test_scoring/test_value_trap_risk_sector_coe.pytests/test_config.pyCLAUDE.md+AGENTS.mdVerification
ruff check .→ cleanpytest tests/ -m "not network"→ 1094 passed (baseline 1059 + 40 new -5 renamed = net +35)python -m compute.output.schema_check→ ✓ in syncPre-merge sim delta (flag=False vs flag=True)
With
USE_SECTOR_COE=False(this PR default): Δscore = 0, Δflag-count = 0 — data-collection only, no production behavior change.With
USE_SECTOR_COE=True(sim override — NOT merged): The per-tickervalue_trap_risk_count_with_sector_coecounter inMetadatawill show the universe-wide count under sector Ke. Threshold for flipping default: ifvalue_trap_risk_count_with_sector_coedrops from baseline ~176 to ≤ 120 on the first cron (≥ 30% reduction confirming the cyclical-sector FP hypothesis), and methodology-scientist Mode B verifies the Damodaran table values — flipUSE_SECTOR_COE = Truein a dedicated follow-up PR.Constraints respected
compute_composite()/PHASE3_WEIGHTSuntouched ✓manipulation_index.pyweights untouched ✓https://claude.ai/code/session_01D6NTyJZa5LWHWakbF5dT29
Generated by Claude Code