fix(xbrl): an amended annual report is still an annual report; cash f… #887
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
| # Regression Tests Workflow | |
| # Runs regression tests on-demand or on schedule to catch breaking changes | |
| name: Regression Tests | |
| on: | |
| # Manual trigger | |
| workflow_dispatch: | |
| # Run weekly on Sundays | |
| schedule: | |
| - cron: '0 8 * * 0' | |
| # ADVISORY pre-merge run. This lane MUST NEVER become a required check. | |
| # | |
| # The 647 network-marked regression tests gate nothing today: they run only | |
| # after merge, so a break is invisible on the pull request that caused it. | |
| # That is not hypothetical — three of four consecutive post-merge runs failed | |
| # the week of 2026-08-08 with 16 failures across three files, all network- | |
| # marked, one of them genuine content loss in `Filing.text()` that sat on main | |
| # for three commits (bead edgartools-07lk.24, finding 1). | |
| # | |
| # Advisory, for two independent reasons, and both have to hold: | |
| # 1. It is paths-filtered. A docs-only pull request produces no run at all, | |
| # and a required check that never reports blocks the pull request forever | |
| # (the deadlock documented at the top of python-hatch-workflow.yml). | |
| # 2. It is ~26 minutes against the live SEC, so it is far too slow and too | |
| # network-dependent to sit in front of a merge. | |
| # The required contexts stay exactly the two named in python-hatch-workflow.yml. | |
| # Making this one required is the fastest way to deadlock the repository. | |
| pull_request: | |
| branches: [ "main" ] | |
| paths: | |
| - 'tests/issues/regression/**' | |
| - 'edgar/**' | |
| - '.github/workflows/regression-tests.yml' | |
| - 'tests/conftest.py' | |
| - 'tests/_offline_filings.py' | |
| - 'tests/_offline_harness.py' | |
| - 'tests/_vcr_safety.py' | |
| - 'pyproject.toml' | |
| # Also run on main branch pushes that touch the library. | |
| # | |
| # This used to name three packages — edgar/xbrl, edgar/entity and the | |
| # regression tree itself — which left every other package uncovered. A change | |
| # under edgar/documents that added no regression file ran the suite in no job | |
| # at all, and that is where the section-extraction defect cluster lives | |
| # (bead edgartools-07lk.21). `edgar/**` closes the hole as a class instead of | |
| # one package at a time; docs- and script-only merges still skip it. | |
| # | |
| # The four test-harness files are here because they decide what this lane | |
| # RUNS, not just what it runs against. tests/conftest.py assigns every | |
| # regression test its fast/network marker and fails collection on an | |
| # unclassified file; _offline_filings.py backs the tests that resolve an | |
| # accession without the quarterly index; _offline_harness.py is the offline | |
| # measurement; _vcr_safety.py installs the safe YAML loader the cassette gate | |
| # exists to protect. A pull request can break this suite by touching only | |
| # those and never trigger the lane — PR #1013 was exactly that shape. | |
| # pyproject.toml carries the marker registry and pytest options | |
| # (xfail_strict among them), which change outcomes suite-wide. | |
| push: | |
| branches: [ "main" ] | |
| paths: | |
| - 'tests/issues/regression/**' | |
| - 'edgar/**' | |
| - '.github/workflows/regression-tests.yml' | |
| - 'tests/conftest.py' | |
| - 'tests/_offline_filings.py' | |
| - 'tests/_offline_harness.py' | |
| - 'tests/_vcr_safety.py' | |
| - 'pyproject.toml' | |
| # Cancel superseded regression runs on the same ref. | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: true | |
| jobs: | |
| # See python-hatch-workflow.yml: cassettes are loaded with PyYAML's unsafe | |
| # loader, so this has to pass before anything runs pytest. | |
| # | |
| # The name carries the "(regression)" suffix on purpose. `main` requires the | |
| # context "Cassette safety gate", produced by the identically-named job in | |
| # python-hatch-workflow.yml. Once this workflow runs on pull requests too, an | |
| # unsuffixed name here would put two check runs with that one required name on | |
| # every pull request touching edgar/**, and branch protection matches contexts | |
| # by name. Either this advisory job's red blocks a merge, or its green | |
| # satisfies the requirement the real gate was supposed to answer — and which | |
| # of the two you get depends on reporting order. Keep the names distinct. | |
| cassette-gate: | |
| name: Cassette safety gate (regression) | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| - name: Set up Python 3.13 | |
| uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 | |
| with: | |
| python-version: '3.13' | |
| - name: Install PyYAML | |
| run: python -m pip install --upgrade pip PyYAML | |
| - name: Scan cassettes for unsafe YAML tags | |
| run: python scripts/check_cassettes.py | |
| regression: | |
| needs: cassette-gate | |
| runs-on: ubuntu-latest | |
| env: | |
| EDGAR_LOCAL_DATA_DIR: ${{ github.workspace }}/.edgar-data | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| # Single version: regression tests verify data correctness against real | |
| # filings, not Python compatibility (the fast job covers 3.10 + 3.13). | |
| python-version: ["3.12"] | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| - name: Set up Python ${{ matrix.python-version }} | |
| uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 | |
| with: | |
| python-version: ${{ matrix.python-version }} | |
| cache: 'pip' | |
| # Persist the SEC HTTP cache — regression tests fetch real filings, and | |
| # /Archives/edgar/data is cached forever (see CACHE_RULES), so a warm cache | |
| # serves them from disk instead of re-downloading. | |
| # | |
| # Restore and save are split so pull requests can READ the cache without | |
| # WRITING one. This entry is 857 MB, and the repository was already at | |
| # 10.7 GB against a 10 GB ceiling when the pull-request trigger was added, | |
| # so it is evicting LRU today. A save on every pull-request run would push | |
| # main's warm entry out and leave the lane re-downloading a filing set it | |
| # already had — slower, and rude to the SEC. Pull-request runs restore from | |
| # whatever main last saved and throw their own copy away. | |
| - name: Restore SEC HTTP cache | |
| uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 | |
| with: | |
| path: ${{ github.workspace }}/.edgar-data/_tcache | |
| key: edgar-tcache-regression-${{ github.run_id }} | |
| restore-keys: | | |
| edgar-tcache-regression- | |
| - name: Install dependencies | |
| run: | | |
| python -m pip install --upgrade pip | |
| python -m pip install -e ".[ai]" | |
| python -m pip install pytest pytest-cov pytest-env pytest-xdist pytest-asyncio pytest-retry pytest-mock pytest-vcr "vcrpy<8.2" freezegun==1.5.1 filelock tqdm responses | |
| - name: Run regression tests | |
| run: | | |
| pytest --cov --cov-report=xml -m regression --enable-cache | |
| # `always()`: a run that failed still warmed the cache, and the next one | |
| # should not pay for those downloads again. | |
| - name: Save SEC HTTP cache | |
| if: always() && github.event_name != 'pull_request' | |
| uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 | |
| with: | |
| path: ${{ github.workspace }}/.edgar-data/_tcache | |
| key: edgar-tcache-regression-${{ github.run_id }} | |
| # Not on pull requests. Codecov posts its own commit statuses, and a partial | |
| # `regression`-flag upload from an advisory lane would put a red check on a | |
| # pull request that this workflow is explicitly not allowed to influence. | |
| - name: Upload coverage reports | |
| uses: codecov/codecov-action@e79a6962e0d4c0c17b229090214935d2e33f8354 # v6.0.1 | |
| if: matrix.python-version == '3.12' && github.event_name != 'pull_request' | |
| with: | |
| files: coverage.xml | |
| fail_ci_if_error: false | |
| flags: regression | |
| token: ${{ secrets.CODECOV_TOKEN }} | |
| # A red `main` has to arrive somewhere a person looks. | |
| # | |
| # This lane runs post-merge, so nothing blocks on it and its result lands in | |
| # the Actions tab and nowhere else. On 2026-08-08 it went red at 13:00 and | |
| # three more pull requests merged through it before anyone noticed — the | |
| # failures were visible the whole time and no one was looking (edgartools-hwdp). | |
| # | |
| # One issue, reopened and commented rather than duplicated, so a week of | |
| # nightly failures is one thread and not seven. It closes itself when the lane | |
| # goes green again, which makes the issue list the answer to "is main ok?". | |
| # `needs` covers both jobs on purpose. A failed cassette gate skips the | |
| # regression job rather than failing it, so keying only on that job's result | |
| # would stay silent for the one failure that stops the suite running at all. | |
| # Neither a cancelled run (concurrency supersedes one on every push) nor a | |
| # skipped one is treated as red. | |
| # | |
| # The `github.event_name != 'pull_request'` guard became load-bearing when the | |
| # advisory pre-merge trigger was added: "main is red" is a claim about main, | |
| # and a pull request that breaks a regression test must not file it. Its | |
| # `issues: write` would not survive a fork pull request anyway. | |
| report: | |
| name: Report red main | |
| needs: [cassette-gate, regression] | |
| if: always() && github.event_name != 'pull_request' | |
| runs-on: ubuntu-latest | |
| permissions: | |
| issues: write | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| REPO: ${{ github.repository }} | |
| RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} | |
| SHA: ${{ github.sha }} | |
| LABEL: red-main | |
| TITLE: "Regression Tests are failing on main" | |
| steps: | |
| - name: Ensure the tracking label exists | |
| run: | | |
| gh label create "$LABEL" --repo "$REPO" --color b60205 \ | |
| --description "Post-merge regression lane is red" 2>/dev/null || true | |
| - name: Open or update the tracking issue | |
| if: needs.regression.result == 'failure' || needs['cassette-gate'].result == 'failure' | |
| run: | | |
| number=$(gh issue list --repo "$REPO" --label "$LABEL" --state all \ | |
| --limit 1 --json number --jq '.[0].number // empty') | |
| body=$(printf '%s\n\n- commit: %s\n- run: %s\n' \ | |
| "The post-merge regression lane failed on \`main\`." "$SHA" "$RUN_URL") | |
| if [ -z "$number" ]; then | |
| gh issue create --repo "$REPO" --label "$LABEL" \ | |
| --title "$TITLE" --body "$body" | |
| else | |
| gh issue reopen --repo "$REPO" "$number" 2>/dev/null || true | |
| gh issue comment --repo "$REPO" "$number" --body "$body" | |
| fi | |
| - name: Close the tracking issue when main is green again | |
| if: needs.regression.result == 'success' && needs['cassette-gate'].result == 'success' | |
| run: | | |
| number=$(gh issue list --repo "$REPO" --label "$LABEL" --state open \ | |
| --limit 1 --json number --jq '.[0].number // empty') | |
| if [ -n "$number" ]; then | |
| gh issue close --repo "$REPO" "$number" \ | |
| --comment "Green again at $SHA — $RUN_URL" | |
| fi |