Skip to content

feat(scoring): Phase 4.5e PR 4-eq — Form-4 10b5-1 contamination filter#224

Merged
dackclup merged 2 commits into
mainfrom
claude/magical-curie-saWfR
May 23, 2026
Merged

feat(scoring): Phase 4.5e PR 4-eq — Form-4 10b5-1 contamination filter#224
dackclup merged 2 commits into
mainfrom
claude/magical-curie-saWfR

Conversation

@dackclup
Copy link
Copy Markdown
Owner

Summary

Closes footgun #1 documented in compute/scoring/form4_signals.py module docstring — Jagolinzer 2009 §3.2 expected FP rate 40-60% on insider_sell_cluster from 10b5-1 contamination. _is_opportunistic_sell now requires NOT is_rule_10b5_one is True in addition to the transaction_code ∈ {S, D} gate so 10b5-1 scheduled trades are excluded from BOTH cluster + C-suite cohort counts.

  • Filter semantic: LITERATURE-ANCHORED to Cohen 2008 §III routine-vs-opportunistic partition (10b5-1 ⊊ routine) + Jagolinzer 2009 §3.1 (75% lower NEO predictability on scheduled trades) + SEC Release 33-11138 (2023-04-01 mandate).
  • None-handling: option (a) per methodology Mode B — None → not-10b5-1 (let cluster fire); matches CMP 2012 + Jagolinzer 2009 empirical regime under which existing thresholds were calibrated.
  • Both flags inherit: single edit to shared _is_opportunistic_sell applies to both insider_sell_cluster and c_suite_unusual_sell. C-suite inheritance carries STRONGER lit support (Jagolinzer 2009 §5.2 NEO 80% predictability drop; CMP 2012 §V.A CFO 9× ratio vs broad 4×).
  • Weights HOLD at 5.0 / 3.0 — promotion 5.0 → 7.0 (Aboody et al. 2010 §3.2 vesting-residual mid-point) is a separate follow-up PR after ≥ 1 cron's data lands in Metadata.form4_rule10b5_one_excluded_count.

Access-path caveat — read before reviewing the parser

edgartools 5.31.5 does NOT parse the SEC structured <rule10b5_1> XML element added by SEC Release 33-11138 (effective 2023-04-01). Verified by edgar-debugger 2026-05-23 via exhaustive grep of edgar.ownership. The equity_swap: str field on NonDerivativeTransaction carries <equitySwapInvolved> — unrelated SEC concept, do NOT assume it carries 10b5-1.

Resolution path:

  1. NonDerivativeTransaction.footnotes carries newline-joined footnote IDs ("F1\nF2") — calling detect_10b5_1_plan on these directly returns False (silently broken).
  2. Ownership.footnotes: Footnotes is the dict that resolves IDs → text via public .get(id, default) accessor (avoids underscore-prefixed _resolve_footnotes private method).
  3. edgar.ownership.core.detect_10b5_1_plan(resolved_text) regex-matches ["10b5-1", "10b-5-1", "rule 10b5", "rule 10b-5", "10b5 plan", "10b-5 plan"].

New manifest pin _FOOTNOTES_REQUIRED_ATTRS = ("get",); footnotes added to existing _NON_DERIVATIVE_TX_REQUIRED_ATTRS + _OWNERSHIP_REQUIRED_ATTRS manifests.

FP risk: terminated-plan disclosures ("10b5-1 plan terminated 2022") match True. Bias is conservative (over-excludes from opportunistic cohort, never under-excludes); Q3 2026-08-19 audit gates negation guard.

Rule 18 observability

Metadata.form4_rule10b5_one_excluded_count: int | None counts universe-wide transactions excluded by the filter within the 30d cluster window. Schema bump 0.10.1-phase4.5e0.10.2-phase4.5e (PATCH, additive). Defense layer emitted-flag count UNCHANGED at 32 (filter is signal-quality, not new flag). schema_check clean.

Expected firing-rate Δ (Mode B 2026-05-23)

Metric Expected band
insider_sell_cluster firing-rate Δ vs PR #222 baseline -30% to -45%
c_suite_unusual_sell firing-rate Δ vs PR #222 baseline -45% to -65%
Absolute cluster firing rate post-filter 4-10% of universe
Absolute C-suite firing rate post-filter 1-4% of universe

Below/above-band → escalate at Q3 2026-08-19 cohort audit (issue #130).

Test plan

  • ruff check compute/ — clean
  • python -m compute.output.schema_check — clean (snapshot regenerated)
  • In flight: test-engineer sub-agent writing 16+ new cases on tests/test_scoring/test_form4_signals.py + tests/test_scoring/test_form4_insider.py (filter semantics + None-handling + cluster threshold interaction + C-suite inheritance + strict-superset invariant under filter + Hypothesis monotonicity property + parser footnote resolution + drift-detector manifest extension). Will land as a follow-up commit on this branch.
  • pytest -m "not network" against full suite (1144 → 1160+)
  • schema-sentinel final lockstep gate
  • quantrank-reviewer Rules 1-18 pass
  • Pre-merge prod sim (firing-rate Δ verification)

References

https://claude.ai/code/session_01SSg1tZypxdgthAYTnQyd1h


Generated by Claude Code

Production + docs commit. Tests follow in a separate commit (test-engineer
sub-agent in progress on tests/test_scoring/test_form4_*.py).

Closes footgun #1 documented in compute/scoring/form4_signals.py module
docstring — Jagolinzer 2009 §3.2 expected FP rate 40-60% on
insider_sell_cluster from 10b5-1 contamination. _is_opportunistic_sell
now requires NOT `is_rule_10b5_one is True` in addition to the
transaction_code ∈ {S, D} gate so 10b5-1 scheduled trades are excluded
from BOTH cluster + C-suite cohort counts. None and False both pass
(option (a) per methodology-scientist Mode B 2026-05-23 — matches
CMP 2012 + Jagolinzer 2009 empirical regime used to calibrate the
existing thresholds).

Access-path caveat: edgartools 5.31.5 does NOT parse the SEC structured
<rule10b5_1> XML element added by SEC Release 33-11138 (effective
2023-04-01). Verified by edgar-debugger 2026-05-23 via exhaustive grep
of edgar.ownership. Resolution path: Ownership.footnotes.get(id) → text
→ edgar.ownership.core.detect_10b5_1_plan regex match. New manifest
_FOOTNOTES_REQUIRED_ATTRS = ("get",); footnotes added to existing
_NON_DERIVATIVE_TX_REQUIRED_ATTRS + _OWNERSHIP_REQUIRED_ATTRS manifests.
FP risk: terminated-plan disclosures match True; bias is conservative
(over-excludes from opportunistic cohort, never under-excludes); Q3
2026-08-19 audit gates negation guard.

Methodology verdict highlights (Mode B 2026-05-23):
- Q1 filter semantic: LITERATURE-ANCHORED (Cohen 2008 §III routine
  partition + Jagolinzer 2009 §3.1 — 10b5-1 ⊊ routine)
- Q2 None-handling: option (a) — None → not-10b5-1
- Q3 expected Δ: cluster -30% to -45% / C-suite -45% to -65%
- Q4 C-suite inherits filter: YES with stronger lit support
  (Jagolinzer 2009 §5.2 NEO subsample 80% predictability drop)
- Q5 weights HOLD at 5.0 / 3.0; promotion 5.0 → 7.0 (Aboody et al.
  2010 §3.2 vesting-residual mid-point) is a separate follow-up
  after ≥ 1 cron's firing-rate data

Rule 18 observability shipped in same PR:
Metadata.form4_rule10b5_one_excluded_count: int | None counts the
universe-wide total of transactions excluded by the filter within
the 30d cluster window. Gates the Q3 2026-08-19 cohort-acceptance
check (issue #130).

Schema bump 0.10.1-phase4.5e → 0.10.2-phase4.5e (PATCH, additive).
Defense layer emitted-flag count UNCHANGED at 32 (filter is signal-
quality, not new flag). schema_check clean.

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

vercel Bot commented May 23, 2026

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

Project Deployment Actions Updated (UTC)
quantrank Ready Ready Preview, Comment May 23, 2026 1:08pm

…te parser + drift detector

Companion to the production commit (baffb73 / PR #224). Closes the
CI red on `Python (lint + test)` by (a) bumping the test_config.py
schema-version pin 0.10.1-phase4.5e → 0.10.2-phase4.5e and (b)
adding behavioral coverage for the new filter / count helper /
footnote-resolution surface.

Test deltas:
- tests/test_scoring/test_form4_signals.py: 29 → 40 (+11)
  - test_opportunistic_sell_excludes_explicit_10b5_1_true
  - test_opportunistic_sell_includes_explicit_10b5_1_false
  - test_opportunistic_sell_includes_10b5_1_none_pre_mandate_compat
  - test_opportunistic_sell_legacy_row_without_10b5_field_passes
  - test_cluster_with_majority_10b5_1_drops_below_threshold
  - test_cluster_with_minority_10b5_1_still_fires
  - test_c_suite_inherits_10b5_1_filter
  - test_strict_superset_invariant_holds_under_10b5_1_filter
  - test_count_10b5_1_filtered_counts_only_opportunistic_codes
  - test_count_10b5_1_filtered_respects_window
  - test_filter_monotonicity_property (Hypothesis @given)

- tests/test_scoring/test_form4_insider.py: 17 → 24 (+7)
  - test_G1_form4_transaction_parses_is_rule_10b5_one_true_from_footnote_text
  - test_G2_form4_transaction_parses_is_rule_10b5_one_false_when_footnote_has_no_10b5_text
  - test_G3_form4_transaction_is_rule_10b5_one_none_when_no_footnote_ids
  - test_G4_form4_transaction_is_rule_10b5_one_none_when_footnotes_dict_missing
  - test_G5_form4_transaction_from_dict_handles_legacy_row_without_10b5_field
  - test_D3_footnotes_required_attrs_manifest_is_non_empty_and_tupled
  - test_D4_edgar_footnotes_api_surface_locked

- tests/test_config.py: schema-version pin bump 0.10.1 → 0.10.2

Strict-superset invariant verification: when the 10b5-1 filter drops
all insiders below the cluster threshold, c_suite_fires is also False
(c_suite ⊆ cluster). Holds because detect_c_suite_unusual_sell calls
_is_opportunistic_sell (which now gates on NOT is_rule_10b5_one is
True) before checking _is_c_suite — so 10b5-1 filtered rows never
reach the C-suite title regex.

Local full suite: 1162 passed / 7 skipped / 22 deselected (network).

https://claude.ai/code/session_01SSg1tZypxdgthAYTnQyd1h
@github-actions
Copy link
Copy Markdown
Contributor

Pre-merge production simulation

Field Value
Duration 948s
Universe size 502
Schema version 0.10.2-phase4.5e
Compute commit 700a2f2f37177993e0857171e35297f15ad37d24
PR-branch output pr-224-compute-output (14-day retention)

Diff vs main

Field Main PR Δ
Universe size 502 502 +0
Schema version 0.10.0-phase4.5e 0.10.2-phase4.5e ⚠️ bumped

Main baseline: 2026-05-23T03:29:47Z (0.4 days old)

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

Ticker PR rank main rank Δrank PR score main score Δscore
IP 358 362 +4 45.66 45.27 +0.39
RTX 420 424 +4 41.03 40.75 +0.28
SBAC 294 297 +3 48.89 48.68 +0.21
BG 344 350 +6 46.16 45.95 +0.21
MAS 74 75 +1 59.70 59.52 +0.18
TEL 123 124 +1 56.77 56.60 +0.17
NWS 295 294 -1 48.88 48.74 +0.14
EVRG 388 390 +2 42.88 42.78 +0.10
EFX 469 469 +0 36.75 36.66 +0.09
VTRS 256 259 +3 50.71 50.62 +0.09

@dackclup dackclup marked this pull request as ready for review May 23, 2026 13:26
@dackclup dackclup merged commit 98e761e into main May 23, 2026
5 checks passed
@dackclup dackclup deleted the claude/magical-curie-saWfR branch May 23, 2026 13:26
dackclup pushed a commit that referenced this pull request May 23, 2026
…ature-searcher (15 → 18)

Closes three session-observed gaps where main-agent inline work was
draining the "Weekly · all models" pool instead of the underutilized
"Weekly · Sonnet only" pool on Max plans.

(a) `.claude/agents/ci-triage-engineer.md` (Tier 4 Operations, sonnet)
    — Reactive to GitHub Actions check failures via the
    `<github-webhook-activity>` PR-activity event. Knows the CI matrix
    (Python lint+test · Frontend build · simulate · Vercel preview) +
    10-class failure taxonomy (schema-pin-drift / ruff-I001 / F401 /
    F841 / dep-missing-ci-only / real-bug / simulate-45min-cap /
    flaky-transient / vercel-build-skew / schema-drift-CI). Proposes
    one-line fix the user authorizes. Refuses to auto-flip test
    assertions or classify as flaky without re-run evidence. Read +
    Bash + Grep + Glob; no MCP tool access (asks main agent to invoke
    GitHub MCP).

(b) `.claude/agents/vercel-preview-auditor.md` (Tier 2 Lifecycle, sonnet)
    — Wraps Vercel MCP server chain: `list_deployments` →
    `get_deployment_build_logs` → `get_runtime_logs` →
    `web_fetch_vercel_url` 3-route UA probe. Codifies the CLAUDE.md
    §Commands "When Vercel MCP is loaded, list_deployments →
    get_runtime_logs is the cheap pre-Playwright pass" — which today
    depends on main-agent memory. Fires before Mark-Ready on any UI-
    touching PR. Refuses to invoke `deploy_to_vercel` or promote
    preview to production. Knows the QuantRank Vercel project name
    (quantrank), team scope (dackclups-projects), and preview URL
    pattern.

(c) `.claude/agents/literature-searcher.md` (Tier 3 Specialized, sonnet)
    — WebSearch + WebFetch wrapper for academic papers + SEC rule
    releases + EDGAR filings. Carries CLAUDE.md's 17-paper canonical
    anchor list in-prompt (Altman / Sloan / Beneish / Dechow / Mayew
    / BD / HLM / DT / Damodaran / Roychowdhury / Cohen / CMP / JMZ /
    Jagolinzer / Bushman-Smith / Aboody / Huber) and refuses to re-
    fetch those. For new papers: WebSearch → preferred
    author/SSRN/NBER free PDF → WebFetch → locate the section →
    return citation-ready excerpt + suggested docstring format.
    Offloads retrieval from `methodology-scientist` (opus) — judgment
    stays on opus, fetch stays on sonnet. Refuses to make a
    methodology verdict itself (that's methodology-scientist's slot
    exclusively); refuses copyright-violating mirrors (sci-hub /
    libgen) — preprint OR institutional-access workflow only.

Tier counts updated:
- Tier 1 Core: 5 (unchanged)
- Tier 2 Lifecycle: 4 → 5 (+vercel-preview-auditor)
- Tier 3 Specialized: 4 → 5 (+literature-searcher)
- Tier 4 Operations: 2 → 3 (+ci-triage-engineer)
- Total: 15 → 18

4-opus / 14-sonnet split preserved (was 4/11). All 3 new agents are
sonnet to drain the underutilized Sonnet-only pool per PR #219 +
PR #223 token-economy rebalance.

Doc lockstep:
- `.claude/agents/README.md` tier tables (T2 / T3 / T4 add a row each)
  + roster header 15 → 18
- `CLAUDE.md` §Layout `.claude/agents/` row 15 → 18
- `CLAUDE.md` §Auto-routing policy:
  - Delegation patterns table +3 rows (CI fail / Vercel preview /
    literature search)
  - Cue table +3 rows (CI webhook / pre-Mark-Ready UI / methodology
    cite outside anchor list)
  - Spawn discipline split count 4-vs-11 → 4-vs-14
  - "Pattern not in the table" walks 18 agents now (was 15)
- `AGENTS.md` §Project structure agents row 15 → 18; new §Phase +
  version state entry documenting cross-tool routing posture (still
  Claude-Code-only)

Doc-only PR. No compute / schema / scoring / valuation / frontend
code change. CI Python lint+test should pass on this branch with
zero regression (no Python code touched).

Stacked alongside PR #224 (Form-4 10b5-1 filter, on
`claude/magical-curie-saWfR`); both branched from same main SHA
`79bb5ae`. Land order does not matter — no scope overlap.

https://claude.ai/code/session_01SSg1tZypxdgthAYTnQyd1h
dackclup added a commit that referenced this pull request May 23, 2026
…ature-searcher (15 → 18) (#225)

Closes three session-observed gaps where main-agent inline work was
draining the "Weekly · all models" pool instead of the underutilized
"Weekly · Sonnet only" pool on Max plans.

(a) `.claude/agents/ci-triage-engineer.md` (Tier 4 Operations, sonnet)
    — Reactive to GitHub Actions check failures via the
    `<github-webhook-activity>` PR-activity event. Knows the CI matrix
    (Python lint+test · Frontend build · simulate · Vercel preview) +
    10-class failure taxonomy (schema-pin-drift / ruff-I001 / F401 /
    F841 / dep-missing-ci-only / real-bug / simulate-45min-cap /
    flaky-transient / vercel-build-skew / schema-drift-CI). Proposes
    one-line fix the user authorizes. Refuses to auto-flip test
    assertions or classify as flaky without re-run evidence. Read +
    Bash + Grep + Glob; no MCP tool access (asks main agent to invoke
    GitHub MCP).

(b) `.claude/agents/vercel-preview-auditor.md` (Tier 2 Lifecycle, sonnet)
    — Wraps Vercel MCP server chain: `list_deployments` →
    `get_deployment_build_logs` → `get_runtime_logs` →
    `web_fetch_vercel_url` 3-route UA probe. Codifies the CLAUDE.md
    §Commands "When Vercel MCP is loaded, list_deployments →
    get_runtime_logs is the cheap pre-Playwright pass" — which today
    depends on main-agent memory. Fires before Mark-Ready on any UI-
    touching PR. Refuses to invoke `deploy_to_vercel` or promote
    preview to production. Knows the QuantRank Vercel project name
    (quantrank), team scope (dackclups-projects), and preview URL
    pattern.

(c) `.claude/agents/literature-searcher.md` (Tier 3 Specialized, sonnet)
    — WebSearch + WebFetch wrapper for academic papers + SEC rule
    releases + EDGAR filings. Carries CLAUDE.md's 17-paper canonical
    anchor list in-prompt (Altman / Sloan / Beneish / Dechow / Mayew
    / BD / HLM / DT / Damodaran / Roychowdhury / Cohen / CMP / JMZ /
    Jagolinzer / Bushman-Smith / Aboody / Huber) and refuses to re-
    fetch those. For new papers: WebSearch → preferred
    author/SSRN/NBER free PDF → WebFetch → locate the section →
    return citation-ready excerpt + suggested docstring format.
    Offloads retrieval from `methodology-scientist` (opus) — judgment
    stays on opus, fetch stays on sonnet. Refuses to make a
    methodology verdict itself (that's methodology-scientist's slot
    exclusively); refuses copyright-violating mirrors (sci-hub /
    libgen) — preprint OR institutional-access workflow only.

Tier counts updated:
- Tier 1 Core: 5 (unchanged)
- Tier 2 Lifecycle: 4 → 5 (+vercel-preview-auditor)
- Tier 3 Specialized: 4 → 5 (+literature-searcher)
- Tier 4 Operations: 2 → 3 (+ci-triage-engineer)
- Total: 15 → 18

4-opus / 14-sonnet split preserved (was 4/11). All 3 new agents are
sonnet to drain the underutilized Sonnet-only pool per PR #219 +
PR #223 token-economy rebalance.

Doc lockstep:
- `.claude/agents/README.md` tier tables (T2 / T3 / T4 add a row each)
  + roster header 15 → 18
- `CLAUDE.md` §Layout `.claude/agents/` row 15 → 18
- `CLAUDE.md` §Auto-routing policy:
  - Delegation patterns table +3 rows (CI fail / Vercel preview /
    literature search)
  - Cue table +3 rows (CI webhook / pre-Mark-Ready UI / methodology
    cite outside anchor list)
  - Spawn discipline split count 4-vs-11 → 4-vs-14
  - "Pattern not in the table" walks 18 agents now (was 15)
- `AGENTS.md` §Project structure agents row 15 → 18; new §Phase +
  version state entry documenting cross-tool routing posture (still
  Claude-Code-only)

Doc-only PR. No compute / schema / scoring / valuation / frontend
code change. CI Python lint+test should pass on this branch with
zero regression (no Python code touched).

Stacked alongside PR #224 (Form-4 10b5-1 filter, on
`claude/magical-curie-saWfR`); both branched from same main SHA
`79bb5ae`. Land order does not matter — no scope overlap.

https://claude.ai/code/session_01SSg1tZypxdgthAYTnQyd1h

Co-authored-by: Claude <noreply@anthropic.com>
dackclup added a commit that referenced this pull request May 23, 2026
…iewer WARNs land (#227)

PR #224 quantrank-reviewer WARN-tier punch-list (3 items). Two land
here as test-discipline polish; one deferred for @network verification.

(a) tests/test_scoring/test_form4_signals.py — two PR-#222 Hypothesis
    property tests drop @settings(deadline=None) per CLAUDE.md §Gotchas
    "Don't use @settings(deadline=None) — a slow example is itself a
    signal":

    - test_cluster_monotonic_under_added_compensation_txns
    - test_cluster_fires_only_within_lookback

    Both verified sub-millisecond under default 200ms Hypothesis
    deadline (40-test suite passes 0.84s offline).
    HealthCheck.too_slow + HealthCheck.filter_too_much suppression
    retained — orthogonal to the deadline rule, separate concern.

(b) test_strict_superset_invariant_holds_under_10b5_1_filter — docstring
    + inline contrapositive comment rewritten to remove the confusing
    mixed "c_suite ⊆ cluster strict-superset" notation (set symbol +
    boolean direction collided). Replaced with the unambiguous
    implication form "c_suite firing implies cluster firing (PR #222
    strict-superset, when the $1M floor is met)" + the
    ¬cluster ⟹ ¬c_suite contrapositive framing. Inline comment line
    "c_suite requires a subset of the conditions cluster requires"
    (geometrically incorrect — c_suite has no $1M floor, so its
    fire-set is NOT a strict subset of cluster's fire-set in general)
    replaced with "Contrapositive check: c_suite firing implies
    cluster firing (PR #222 strict-superset)".

(c) DEFERRED — _FOOTNOTES_REQUIRED_ATTRS manifest extension
    ("get",) → ("get", "__contains__"). Sandbox doesn't have
    edgartools installed; can't verify Footnotes.__contains__ exists
    on the live class. Adding the attr blindly risks a silent break
    under pytest --run-network on a future CI cron run. Schedule for
    a follow-up PR that can run @network tests first.

Tests: 1168 → 1168 (3 in-place edits; no test added / removed).
ruff clean. No compute / schema / scoring / valuation / frontend /
Python production-code change. CLAUDE.md + AGENTS.md lockstep
satisfied via §Phase status in-flight notes.

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
…+ 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
…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
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
…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
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
…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
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 a16c8879015748 (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
… from PR #230 (+ dogfoods PR #237 INFLIGHT convention) (#238)

PR #230's edgar-debugger audit + Part 1 §"Footnote resolution"
docstring surfaced an architectural gap: filers who check
<aff10b5One>true at the document level but omit the per-transaction
footnote text slip past the existing footnote-text regex path and
INCORRECTLY enter the opportunistic-trades cohort that drives the
insider_sell_cluster + c_suite_unusual_sell annotates.

edgar-debugger session 5 design report (2026-05-24) confirmed:
- Access path: filing.xml() — already @lru_cache(maxsize=4), so
  calling it AFTER filing.obj() is a free cache hit (zero extra
  HTTP per filing; universe-wide added cost ~1.5s for lxml find)
- Element location: ownershipDocument/aff10b5One per SEC schema
  X0609 (Release 33-11138, effective 2023-04-01)
- Edgartools 5.31.5's Ownership.parse_xml walks a fixed set of
  child tags and never reads aff10b5One — the element is PRESENT
  in the BS4 tree but discarded after parse

Implementation (compute/scoring/form4_insider.py, ~30 LOC prod):

- _AFF10B5_REQUIRED_ATTRS = ("aff10b5One",) manifest tuple
  (drift-detector documenting the canonical SEC element name)
- _extract_aff10b5_one(xml_str) helper — lxml-based, handles
  1/true/0/false variants, returns None on absent / parse fail
  (graceful-degradation per existing pattern + methodology-
  scientist Mode B option (a) from PR #224)
- _combine_10b5_one_signals(doc_level, footnote_result) helper
  — OR-of-True truth table: True if either signal is True; None
  only when both are None; False otherwise
- _form4_to_transactions modified to call filing.xml() +
  _extract_aff10b5_one() ONCE per filing, then propagate via
  _combine_10b5_one_signals() to every transaction in that
  filing's rows
- Form4Transaction dataclass gains aff10b5_one_doc_level: bool |
  None = None field (backward-compat default for pre-PR cache
  rows); from_dict reads it via .get(...) with None fallback

Tests (tests/test_scoring/test_form4_insider.py, ~80 LOC, 10 cases
H1-H10) — test-engineer spawn delivered the spec:

- H1-H4: _extract_aff10b5_one truth + variants + malformed + absent
- H5: doc-level propagation to all transactions in a filing (mock
  filing with xml() method that returns synthetic XML)
- H6-H8: _combine_10b5_one_signals truth-table coverage
- H9: _AFF10B5_REQUIRED_ATTRS manifest drift-detector (mirrors the
  existing _FOOTNOTES_REQUIRED_ATTRS pattern at line 815)
- H10 (@network): live AAPL Form 4 extraction verifies access path
  end-to-end

Verification:
  $ pytest tests/test_scoring/test_form4_insider.py -v -m "not network"
  → 32 passed, 2 deselected (H10 + D4 @network)
  $ python3 -c "from compute.scoring.form4_insider import _extract_aff10b5_one, _combine_10b5_one_signals; ..."
  → 14 smoke assertions pass (all variants + truth-table)
  $ ruff check compute/scoring/form4_insider.py
  → All checks passed!
  $ python -m compute.output.schema_check
  → Schema snapshot in sync

Scope held to edgar-debugger estimate:
  - ~30 LOC prod ✓
  - ~80 LOC tests (actual: 214 lines including mock fixtures + docstrings) ✓
  - 0 new deps (lxml is already a transitive dep of edgartools) ✓
  - 0 schema changes (new field lives in Form-4 cache rows only;
    doesn't surface in StockDetail / Metadata in this PR) ✓

Defense layer flag count UNCHANGED at 32 (combined signal is
quality-improvement, not new flag). Downstream consumer
_is_opportunistic_sell in form4_signals.py reads the existing
combined is_rule_10b5_one field — no consumer changes needed.

DOGFOODING the PR #237 convention: this PR's in-flight entry lives
in PHASE_STATUS_INFLIGHT.md (NOT CLAUDE.md / AGENTS.md §Phase
status). First PR to test-drive the side-file pattern end-to-end.
Lockstep rule satisfied per the new §Conventions wording (an
in-flight entry in PHASE_STATUS_INFLIGHT.md OR a substance update
to CLAUDE.md/AGENTS.md sections — this PR uses the former).
Companion housekeeping move: PR #237's own in-flight entry moved
from "## In flight" to "## Merged" section within
PHASE_STATUS_INFLIGHT.md (still awaiting the housekeeping commit
to CLAUDE.md §Phase status — that's a separate workflow not in
scope here).

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 a16c8879015748 (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 a16c8879015748 (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>
dackclup added a commit that referenced this pull request May 26, 2026
…Craft frontend (#266)

Cuts the v1.3.0-phase4.5e release tag, closing the Phase 4.5e Form-4
insider-clustering ladder (PRs #167+#205+#222+#224+#238) and shipping
the LedgerCraft frontend reskin (A1-A3+B1-B4+animation PRs 1-3+#244
polish+dark-mode tooltip fixes through PR #263) since v1.2.0-phase4.5
(6d414a9, 2026-05-17).

Scope (3 files):
- pyproject.toml — version 0.3.0 → 1.3.0
- docs/release-notes/v1.3.0-phase4.5e.md (NEW) — release body grouped
  by Form-4 cluster / data-quality / defense layer / frontend /
  methodology + agent infra / CI hygiene; ~800 words
- PHASE_STATUS.md — Current state schema 0.10.4 → 0.10.5-phase4.5e,
  defense layer headline 32 → 33 declared flags, production-run
  pointer refreshed to 26423296287

Pre-flight ladder verified by release-captain (opus):
- ruff clean
- pytest 1216 passed (offline)
- schema_check in sync at 0.10.5-phase4.5e
- verify-production-output Section A-G + I-L PASS; Section H 1
  known FAIL (orphan BK.json legacy snapshot, pre-existing)
- frontend build verified via vercel-preview-auditor (sonnet) on
  main HEAD e6013ba — 506/506 routes compiled, types validated,
  runtime clean, 3-route UA probe PASS

Defense scorecard: 7 active vetoes unchanged
(altman_distress / sloan_accruals_top_decile / net_issuance_top_decile
/ non_reliance_filing / beneish_manipulation_veto /
dechow_manipulation_veto / data_quality_input_corruption).
Headline 32 → 33 declared boolean flags
(adds multi_class_aggregate_shares_suspected per PR #264; PR #265
DQIC rename is identifier-shape, not new flag).

Production output: metadata.json reports 0.10.4-phase4.5e from cron
#4 (2026-05-26T01:12); next weekday cron Wed 2026-05-27 22:00 UTC
re-renders at full 0.10.5-phase4.5e semantics. Tag is anchored to
code, not last committed snapshot per release-tag SKILL.md §Gotchas.

CVE baseline 25 → 15 open (0C/6H/7M/2L); all 15 are next@14.x SSR
advisories with zero exploitability on static-export.

Post-merge: tag command + GitHub Release creation require explicit
user authorization per CLAUDE.md §Executing actions with care.

https://claude.ai/code/session_01JwntEE4PNAXSMkZxRA9BB4

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