From 0cda2dbc27733503ed29e1a70e0ff8c57725b158 Mon Sep 17 00:00:00 2001 From: Financialstream Date: Thu, 6 Aug 2026 23:03:09 -0700 Subject: [PATCH 1/6] Run final minor maintenance patch --- .github/workflows/final-minor-maintenance.yml | 179 ++++++++++++++++++ 1 file changed, 179 insertions(+) create mode 100644 .github/workflows/final-minor-maintenance.yml diff --git a/.github/workflows/final-minor-maintenance.yml b/.github/workflows/final-minor-maintenance.yml new file mode 100644 index 0000000..605daf4 --- /dev/null +++ b/.github/workflows/final-minor-maintenance.yml @@ -0,0 +1,179 @@ +name: Final minor maintenance patch + +on: + push: + branches: + - agent/final-financial-stream-minor-maintenance + +permissions: + contents: write + +jobs: + patch: + if: github.actor != 'github-actions[bot]' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Apply exact maintenance patch and source QA + shell: python + run: | + from pathlib import Path + import re + + expected_menu_files = { + 'ru/index.html', + 'ru/blog/index.html', + 'ru/blog/buhgalteriya-ssha-malogo-biznesa.html', + 'ru/contact/index.html', + 'ru/start-here/index.html', + 'ru/services/tax-returns.html', + 'ru/services/company-formation.html', + 'ru/services/payroll-li-quarterly.html', + 'ru/services/financial-consulting.html', + 'ru/services/quickbooks-bookkeeping.html', + 'ru/services/sales-tax-dor-reporting.html', + } + + changed_menu_files = set() + menu_tag = re.compile(r']*\bdata-menu-toggle\b[^>]*>') + + for path in sorted(Path('ru').rglob('*.html')): + raw = path.read_bytes() + text = raw.decode('utf-8') + original = text + tags = list(menu_tag.finditer(text)) + if not tags: + continue + + out = [] + cursor = 0 + changed = False + for match in tags: + tag = match.group(0) + replacement = tag + if 'aria-label=' not in tag: + if ' data-menu-toggle' not in tag: + raise SystemExit(f'Unexpected menu toggle markup in {path}: {tag}') + replacement = tag.replace( + ' data-menu-toggle', + ' aria-label="Открыть меню" data-menu-toggle', + 1, + ) + changed = True + out.append(text[cursor:match.start()]) + out.append(replacement) + cursor = match.end() + out.append(text[cursor:]) + text = ''.join(out) + + if changed: + changed_menu_files.add(path.as_posix()) + path.write_bytes(text.encode('utf-8')) + + if changed_menu_files != expected_menu_files: + raise SystemExit( + 'Unexpected RU menu patch set. ' + f'Expected {sorted(expected_menu_files)}, got {sorted(changed_menu_files)}' + ) + + metadata = { + 'blog/us-bookkeeping-small-business.html': ( + '', + '', + ), + 'ru/blog/buhgalteriya-ssha-malogo-biznesa.html': ( + '', + '', + ), + } + + for file_name, (twitter_image, twitter_alt) in metadata.items(): + path = Path(file_name) + text = path.read_bytes().decode('utf-8') + if text.count(twitter_alt) == 0: + if text.count(twitter_image) != 1: + raise SystemExit(f'Expected exactly one twitter:image in {file_name}') + newline = '\r\n' if '\r\n' in text else '\n' + text = text.replace(twitter_image, twitter_image + newline + twitter_alt, 1) + path.write_bytes(text.encode('utf-8')) + if path.read_bytes().decode('utf-8').count(twitter_alt) != 1: + raise SystemExit(f'Expected exactly one twitter:image:alt in {file_name}') + + # Accessibility source QA across every RU HTML page using the mobile toggle. + for path in sorted(Path('ru').rglob('*.html')): + text = path.read_bytes().decode('utf-8') + for match in menu_tag.finditer(text): + tag = match.group(0) + if 'aria-label="Открыть меню"' not in tag: + raise SystemExit(f'Missing canonical aria-label in {path}: {tag}') + if 'aria-expanded="false"' not in tag: + raise SystemExit(f'aria-expanded damaged in {path}: {tag}') + controls = re.search(r'aria-controls="([^"]+)"', tag) + if not controls: + raise SystemExit(f'aria-controls missing in {path}: {tag}') + if f'id="{controls.group(1)}"' not in text: + raise SystemExit(f'aria-controls target missing in {path}: {controls.group(1)}') + + # Flagship metadata QA: preserve canonical/hreflang/OG/Twitter image and schema blocks. + checks = { + 'blog/us-bookkeeping-small-business.html': [ + '', + '', + '', + '', + '"@type":"Article"', + '"@type":"BreadcrumbList"', + '"@type":"FAQPage"', + ], + 'ru/blog/buhgalteriya-ssha-malogo-biznesa.html': [ + '', + '', + '', + '', + '"@type":"Article"', + '"@type":"BreadcrumbList"', + '"@type":"FAQPage"', + ], + } + for file_name, required in checks.items(): + text = Path(file_name).read_bytes().decode('utf-8') + for item in required: + if text.count(item) != 1: + raise SystemExit(f'Flagship QA failed in {file_name}: {item}') + if text.count(' Date: Thu, 6 Aug 2026 23:05:06 -0700 Subject: [PATCH 2/6] Run final minor maintenance patch on PR --- .github/workflows/final-minor-maintenance.yml | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/.github/workflows/final-minor-maintenance.yml b/.github/workflows/final-minor-maintenance.yml index 605daf4..831fa2f 100644 --- a/.github/workflows/final-minor-maintenance.yml +++ b/.github/workflows/final-minor-maintenance.yml @@ -1,9 +1,9 @@ name: Final minor maintenance patch on: - push: + pull_request: branches: - - agent/final-financial-stream-minor-maintenance + - main permissions: contents: write @@ -15,6 +15,7 @@ jobs: steps: - uses: actions/checkout@v4 with: + ref: ${{ github.head_ref }} fetch-depth: 0 - name: Apply exact maintenance patch and source QA @@ -22,6 +23,7 @@ jobs: run: | from pathlib import Path import re + import subprocess expected_menu_files = { 'ru/index.html', @@ -41,13 +43,10 @@ jobs: menu_tag = re.compile(r']*\bdata-menu-toggle\b[^>]*>') for path in sorted(Path('ru').rglob('*.html')): - raw = path.read_bytes() - text = raw.decode('utf-8') - original = text + text = path.read_bytes().decode('utf-8') tags = list(menu_tag.finditer(text)) if not tags: continue - out = [] cursor = 0 changed = False @@ -68,7 +67,6 @@ jobs: cursor = match.end() out.append(text[cursor:]) text = ''.join(out) - if changed: changed_menu_files.add(path.as_posix()) path.write_bytes(text.encode('utf-8')) @@ -102,7 +100,6 @@ jobs: if path.read_bytes().decode('utf-8').count(twitter_alt) != 1: raise SystemExit(f'Expected exactly one twitter:image:alt in {file_name}') - # Accessibility source QA across every RU HTML page using the mobile toggle. for path in sorted(Path('ru').rglob('*.html')): text = path.read_bytes().decode('utf-8') for match in menu_tag.finditer(text): @@ -117,7 +114,6 @@ jobs: if f'id="{controls.group(1)}"' not in text: raise SystemExit(f'aria-controls target missing in {path}: {controls.group(1)}') - # Flagship metadata QA: preserve canonical/hreflang/OG/Twitter image and schema blocks. checks = { 'blog/us-bookkeeping-small-business.html': [ '', @@ -147,11 +143,11 @@ jobs: raise SystemExit(f'Expected exactly one twitter:image:alt in {file_name}') allowed = expected_menu_files | {'blog/us-bookkeeping-small-business.html'} - import subprocess changed = set(subprocess.check_output(['git', 'diff', '--name-only'], text=True).splitlines()) if changed != allowed: raise SystemExit(f'Unexpected changed files: {sorted(changed)}; expected {sorted(allowed)}') + subprocess.run(['git', 'diff', '--check'], check=True) print('Source QA passed. Changed files:') for item in sorted(changed): print(item) @@ -176,4 +172,4 @@ jobs: blog/us-bookkeeping-small-business.html git diff --cached --check git commit -m "Fix minor accessibility and Twitter image metadata" - git push origin HEAD:agent/final-financial-stream-minor-maintenance + git push origin HEAD:${{ github.head_ref }} From f84bd1f51130d31d61b5b640f7af60f2b3b21c3a Mon Sep 17 00:00:00 2001 From: Financialstream Date: Thu, 6 Aug 2026 23:06:54 -0700 Subject: [PATCH 3/6] Expand final accessibility patch to all confirmed RU pages --- .github/workflows/final-minor-maintenance.yml | 30 +++++++++++-------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/.github/workflows/final-minor-maintenance.yml b/.github/workflows/final-minor-maintenance.yml index 831fa2f..ccb4c42 100644 --- a/.github/workflows/final-minor-maintenance.yml +++ b/.github/workflows/final-minor-maintenance.yml @@ -28,7 +28,21 @@ jobs: expected_menu_files = { 'ru/index.html', 'ru/blog/index.html', + 'ru/blog/buhgalter-seattle-quickbooks.html', 'ru/blog/buhgalteriya-ssha-malogo-biznesa.html', + 'ru/blog/irs-2026-inflation-adjustments.html', + 'ru/blog/nalogovaya-deklaraciya-seattle.html', + 'ru/blog/nalogovaya-deklaraciya-ssha-cheklist-dokumentov-2026.html', + 'ru/blog/nalogovaya-deklaraciya-usa-udalenno-cheklist.html', + 'ru/blog/payroll-2026.html', + 'ru/blog/podgotovka-nalogovoy-deklaracii-ssha.html', + 'ru/blog/quickbooks-healthy-books.html', + 'ru/blog/quickbooks-uchet-ssha-ezhemesyachnyy-cheklist.html', + 'ru/blog/russkogovoryashchiy-bukhgalter-v-ssha-seattle-washington.html', + 'ru/blog/sales-tax-2026.html', + 'ru/blog/seattle-area-accountant-bookkeeper.html', + 'ru/blog/seattle-bo-tax-changes-2026.html', + 'ru/blog/washington-bookkeeping-quickbooks.html', 'ru/contact/index.html', 'ru/start-here/index.html', 'ru/services/tax-returns.html', @@ -100,6 +114,7 @@ jobs: if path.read_bytes().decode('utf-8').count(twitter_alt) != 1: raise SystemExit(f'Expected exactly one twitter:image:alt in {file_name}') + # Accessibility source QA across every RU HTML page using the mobile toggle. for path in sorted(Path('ru').rglob('*.html')): text = path.read_bytes().decode('utf-8') for match in menu_tag.finditer(text): @@ -114,6 +129,7 @@ jobs: if f'id="{controls.group(1)}"' not in text: raise SystemExit(f'aria-controls target missing in {path}: {controls.group(1)}') + # Flagship metadata QA: preserve canonical/hreflang/OG/Twitter image and schema blocks. checks = { 'blog/us-bookkeeping-small-business.html': [ '', @@ -157,19 +173,7 @@ jobs: run: | git config user.name "github-actions[bot]" git config user.email "41898282+github-actions[bot]@users.noreply.github.com" - git add \ - ru/index.html \ - ru/blog/index.html \ - ru/blog/buhgalteriya-ssha-malogo-biznesa.html \ - ru/contact/index.html \ - ru/start-here/index.html \ - ru/services/tax-returns.html \ - ru/services/company-formation.html \ - ru/services/payroll-li-quarterly.html \ - ru/services/financial-consulting.html \ - ru/services/quickbooks-bookkeeping.html \ - ru/services/sales-tax-dor-reporting.html \ - blog/us-bookkeeping-small-business.html + git diff --name-only -z | xargs -0 git add -- git diff --cached --check git commit -m "Fix minor accessibility and Twitter image metadata" git push origin HEAD:${{ github.head_ref }} From 2814206894438d94efe19a08ce3ddfb93deeb395 Mon Sep 17 00:00:00 2001 From: Financialstream Date: Thu, 6 Aug 2026 23:08:13 -0700 Subject: [PATCH 4/6] Allow already-correct RU menu labels in source QA --- .github/workflows/final-minor-maintenance.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/final-minor-maintenance.yml b/.github/workflows/final-minor-maintenance.yml index ccb4c42..c0977d9 100644 --- a/.github/workflows/final-minor-maintenance.yml +++ b/.github/workflows/final-minor-maintenance.yml @@ -119,8 +119,9 @@ jobs: text = path.read_bytes().decode('utf-8') for match in menu_tag.finditer(text): tag = match.group(0) - if 'aria-label="Открыть меню"' not in tag: - raise SystemExit(f'Missing canonical aria-label in {path}: {tag}') + label = re.search(r'aria-label="([^"]+)"', tag) + if not label or not label.group(1).strip(): + raise SystemExit(f'Missing accessible name in {path}: {tag}') if 'aria-expanded="false"' not in tag: raise SystemExit(f'aria-expanded damaged in {path}: {tag}') controls = re.search(r'aria-controls="([^"]+)"', tag) From ce0adf93d3ef4deca76af63a96fb5edd2e944068 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 7 Aug 2026 06:08:27 +0000 Subject: [PATCH 5/6] Fix minor accessibility and Twitter image metadata --- blog/us-bookkeeping-small-business.html | 1 + ru/blog/buhgalter-seattle-quickbooks.html | 2 +- ru/blog/buhgalteriya-ssha-malogo-biznesa.html | 3 ++- ru/blog/index.html | 2 +- ru/blog/irs-2026-inflation-adjustments.html | 2 +- ru/blog/nalogovaya-deklaraciya-seattle.html | 2 +- .../nalogovaya-deklaraciya-ssha-cheklist-dokumentov-2026.html | 2 +- ru/blog/nalogovaya-deklaraciya-usa-udalenno-cheklist.html | 2 +- ru/blog/payroll-2026.html | 2 +- ru/blog/podgotovka-nalogovoy-deklaracii-ssha.html | 2 +- ru/blog/quickbooks-healthy-books.html | 2 +- ru/blog/quickbooks-uchet-ssha-ezhemesyachnyy-cheklist.html | 2 +- ...sskogovoryashchiy-bukhgalter-v-ssha-seattle-washington.html | 2 +- ru/blog/sales-tax-2026.html | 2 +- ru/blog/seattle-area-accountant-bookkeeper.html | 2 +- ru/blog/seattle-bo-tax-changes-2026.html | 2 +- ru/blog/washington-bookkeeping-quickbooks.html | 2 +- ru/contact/index.html | 2 +- ru/index.html | 2 +- ru/services/company-formation.html | 2 +- ru/services/financial-consulting.html | 2 +- ru/services/payroll-li-quarterly.html | 2 +- ru/services/quickbooks-bookkeeping.html | 2 +- ru/services/sales-tax-dor-reporting.html | 2 +- ru/services/tax-returns.html | 2 +- ru/start-here/index.html | 2 +- 26 files changed, 27 insertions(+), 25 deletions(-) diff --git a/blog/us-bookkeeping-small-business.html b/blog/us-bookkeeping-small-business.html index dbd1a70..283c30a 100644 --- a/blog/us-bookkeeping-small-business.html +++ b/blog/us-bookkeeping-small-business.html @@ -22,6 +22,7 @@ + diff --git a/ru/blog/buhgalter-seattle-quickbooks.html b/ru/blog/buhgalter-seattle-quickbooks.html index c75f9c1..791f0fd 100644 --- a/ru/blog/buhgalter-seattle-quickbooks.html +++ b/ru/blog/buhgalter-seattle-quickbooks.html @@ -81,7 +81,7 @@ - +
diff --git a/ru/blog/buhgalteriya-ssha-malogo-biznesa.html b/ru/blog/buhgalteriya-ssha-malogo-biznesa.html index 7c1129b..0031343 100644 --- a/ru/blog/buhgalteriya-ssha-malogo-biznesa.html +++ b/ru/blog/buhgalteriya-ssha-malogo-biznesa.html @@ -22,6 +22,7 @@ + @@ -58,7 +59,7 @@ EN
Оставить запрос - diff --git a/ru/blog/index.html b/ru/blog/index.html index bc36113..c1aa1e0 100644 --- a/ru/blog/index.html +++ b/ru/blog/index.html @@ -53,7 +53,7 @@ EN Оставить запрос - diff --git a/ru/blog/irs-2026-inflation-adjustments.html b/ru/blog/irs-2026-inflation-adjustments.html index d593195..bbb2b16 100644 --- a/ru/blog/irs-2026-inflation-adjustments.html +++ b/ru/blog/irs-2026-inflation-adjustments.html @@ -81,7 +81,7 @@ - +
diff --git a/ru/blog/nalogovaya-deklaraciya-seattle.html b/ru/blog/nalogovaya-deklaraciya-seattle.html index b35ac2d..53ef3c6 100644 --- a/ru/blog/nalogovaya-deklaraciya-seattle.html +++ b/ru/blog/nalogovaya-deklaraciya-seattle.html @@ -81,7 +81,7 @@ - +
diff --git a/ru/blog/nalogovaya-deklaraciya-ssha-cheklist-dokumentov-2026.html b/ru/blog/nalogovaya-deklaraciya-ssha-cheklist-dokumentov-2026.html index d49ccc7..bc1df4a 100644 --- a/ru/blog/nalogovaya-deklaraciya-ssha-cheklist-dokumentov-2026.html +++ b/ru/blog/nalogovaya-deklaraciya-ssha-cheklist-dokumentov-2026.html @@ -81,7 +81,7 @@ - +
diff --git a/ru/blog/nalogovaya-deklaraciya-usa-udalenno-cheklist.html b/ru/blog/nalogovaya-deklaraciya-usa-udalenno-cheklist.html index 2d1644e..b3bb370 100644 --- a/ru/blog/nalogovaya-deklaraciya-usa-udalenno-cheklist.html +++ b/ru/blog/nalogovaya-deklaraciya-usa-udalenno-cheklist.html @@ -87,7 +87,7 @@ - +
diff --git a/ru/blog/payroll-2026.html b/ru/blog/payroll-2026.html index 08f7059..df75678 100644 --- a/ru/blog/payroll-2026.html +++ b/ru/blog/payroll-2026.html @@ -81,7 +81,7 @@ - +
diff --git a/ru/blog/podgotovka-nalogovoy-deklaracii-ssha.html b/ru/blog/podgotovka-nalogovoy-deklaracii-ssha.html index b477ad7..a5d27da 100644 --- a/ru/blog/podgotovka-nalogovoy-deklaracii-ssha.html +++ b/ru/blog/podgotovka-nalogovoy-deklaracii-ssha.html @@ -87,7 +87,7 @@ - +
diff --git a/ru/blog/quickbooks-healthy-books.html b/ru/blog/quickbooks-healthy-books.html index 939bac6..bd0fd98 100644 --- a/ru/blog/quickbooks-healthy-books.html +++ b/ru/blog/quickbooks-healthy-books.html @@ -87,7 +87,7 @@ - +
diff --git a/ru/blog/quickbooks-uchet-ssha-ezhemesyachnyy-cheklist.html b/ru/blog/quickbooks-uchet-ssha-ezhemesyachnyy-cheklist.html index e1ddc8c..8e367f5 100644 --- a/ru/blog/quickbooks-uchet-ssha-ezhemesyachnyy-cheklist.html +++ b/ru/blog/quickbooks-uchet-ssha-ezhemesyachnyy-cheklist.html @@ -81,7 +81,7 @@ - +
diff --git a/ru/blog/russkogovoryashchiy-bukhgalter-v-ssha-seattle-washington.html b/ru/blog/russkogovoryashchiy-bukhgalter-v-ssha-seattle-washington.html index 1fcecfe..b949c8e 100644 --- a/ru/blog/russkogovoryashchiy-bukhgalter-v-ssha-seattle-washington.html +++ b/ru/blog/russkogovoryashchiy-bukhgalter-v-ssha-seattle-washington.html @@ -87,7 +87,7 @@ - +
diff --git a/ru/blog/sales-tax-2026.html b/ru/blog/sales-tax-2026.html index fc8d670..811e040 100644 --- a/ru/blog/sales-tax-2026.html +++ b/ru/blog/sales-tax-2026.html @@ -81,7 +81,7 @@ - +
diff --git a/ru/blog/seattle-area-accountant-bookkeeper.html b/ru/blog/seattle-area-accountant-bookkeeper.html index 8d3384e..b116777 100644 --- a/ru/blog/seattle-area-accountant-bookkeeper.html +++ b/ru/blog/seattle-area-accountant-bookkeeper.html @@ -87,7 +87,7 @@ - +
diff --git a/ru/blog/seattle-bo-tax-changes-2026.html b/ru/blog/seattle-bo-tax-changes-2026.html index 0ae46ee..0c6905e 100644 --- a/ru/blog/seattle-bo-tax-changes-2026.html +++ b/ru/blog/seattle-bo-tax-changes-2026.html @@ -42,7 +42,7 @@ - +
diff --git a/ru/index.html b/ru/index.html index e936339..1a7c6ea 100644 --- a/ru/index.html +++ b/ru/index.html @@ -84,7 +84,7 @@
Оставить запрос - +
diff --git a/ru/services/company-formation.html b/ru/services/company-formation.html index 6dc4b75..62a3abf 100644 --- a/ru/services/company-formation.html +++ b/ru/services/company-formation.html @@ -52,7 +52,7 @@ EN
Оставить запрос -
diff --git a/ru/services/financial-consulting.html b/ru/services/financial-consulting.html index 833b558..d2c09d6 100644 --- a/ru/services/financial-consulting.html +++ b/ru/services/financial-consulting.html @@ -52,7 +52,7 @@ EN
Оставить запрос -
diff --git a/ru/services/payroll-li-quarterly.html b/ru/services/payroll-li-quarterly.html index 3618912..b081f37 100644 --- a/ru/services/payroll-li-quarterly.html +++ b/ru/services/payroll-li-quarterly.html @@ -52,7 +52,7 @@ EN
Оставить запрос -
diff --git a/ru/services/quickbooks-bookkeeping.html b/ru/services/quickbooks-bookkeeping.html index df9e9f9..4741e17 100644 --- a/ru/services/quickbooks-bookkeeping.html +++ b/ru/services/quickbooks-bookkeeping.html @@ -52,7 +52,7 @@ EN
Оставить запрос -
diff --git a/ru/services/sales-tax-dor-reporting.html b/ru/services/sales-tax-dor-reporting.html index cfaad84..bdc5b93 100644 --- a/ru/services/sales-tax-dor-reporting.html +++ b/ru/services/sales-tax-dor-reporting.html @@ -52,7 +52,7 @@ EN
Оставить запрос -
diff --git a/ru/services/tax-returns.html b/ru/services/tax-returns.html index 6af5a09..46d8a97 100644 --- a/ru/services/tax-returns.html +++ b/ru/services/tax-returns.html @@ -52,7 +52,7 @@ EN
Оставить запрос - diff --git a/ru/start-here/index.html b/ru/start-here/index.html index 0490720..0dbf590 100644 --- a/ru/start-here/index.html +++ b/ru/start-here/index.html @@ -52,7 +52,7 @@ EN Оставить запрос - From 97f25b8747cfc1f992de3a87855ad05ca2117cac Mon Sep 17 00:00:00 2001 From: Financialstream Date: Thu, 6 Aug 2026 23:11:17 -0700 Subject: [PATCH 6/6] Remove temporary maintenance workflow --- .github/workflows/final-minor-maintenance.yml | 180 ------------------ 1 file changed, 180 deletions(-) delete mode 100644 .github/workflows/final-minor-maintenance.yml diff --git a/.github/workflows/final-minor-maintenance.yml b/.github/workflows/final-minor-maintenance.yml deleted file mode 100644 index c0977d9..0000000 --- a/.github/workflows/final-minor-maintenance.yml +++ /dev/null @@ -1,180 +0,0 @@ -name: Final minor maintenance patch - -on: - pull_request: - branches: - - main - -permissions: - contents: write - -jobs: - patch: - if: github.actor != 'github-actions[bot]' - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - with: - ref: ${{ github.head_ref }} - fetch-depth: 0 - - - name: Apply exact maintenance patch and source QA - shell: python - run: | - from pathlib import Path - import re - import subprocess - - expected_menu_files = { - 'ru/index.html', - 'ru/blog/index.html', - 'ru/blog/buhgalter-seattle-quickbooks.html', - 'ru/blog/buhgalteriya-ssha-malogo-biznesa.html', - 'ru/blog/irs-2026-inflation-adjustments.html', - 'ru/blog/nalogovaya-deklaraciya-seattle.html', - 'ru/blog/nalogovaya-deklaraciya-ssha-cheklist-dokumentov-2026.html', - 'ru/blog/nalogovaya-deklaraciya-usa-udalenno-cheklist.html', - 'ru/blog/payroll-2026.html', - 'ru/blog/podgotovka-nalogovoy-deklaracii-ssha.html', - 'ru/blog/quickbooks-healthy-books.html', - 'ru/blog/quickbooks-uchet-ssha-ezhemesyachnyy-cheklist.html', - 'ru/blog/russkogovoryashchiy-bukhgalter-v-ssha-seattle-washington.html', - 'ru/blog/sales-tax-2026.html', - 'ru/blog/seattle-area-accountant-bookkeeper.html', - 'ru/blog/seattle-bo-tax-changes-2026.html', - 'ru/blog/washington-bookkeeping-quickbooks.html', - 'ru/contact/index.html', - 'ru/start-here/index.html', - 'ru/services/tax-returns.html', - 'ru/services/company-formation.html', - 'ru/services/payroll-li-quarterly.html', - 'ru/services/financial-consulting.html', - 'ru/services/quickbooks-bookkeeping.html', - 'ru/services/sales-tax-dor-reporting.html', - } - - changed_menu_files = set() - menu_tag = re.compile(r']*\bdata-menu-toggle\b[^>]*>') - - for path in sorted(Path('ru').rglob('*.html')): - text = path.read_bytes().decode('utf-8') - tags = list(menu_tag.finditer(text)) - if not tags: - continue - out = [] - cursor = 0 - changed = False - for match in tags: - tag = match.group(0) - replacement = tag - if 'aria-label=' not in tag: - if ' data-menu-toggle' not in tag: - raise SystemExit(f'Unexpected menu toggle markup in {path}: {tag}') - replacement = tag.replace( - ' data-menu-toggle', - ' aria-label="Открыть меню" data-menu-toggle', - 1, - ) - changed = True - out.append(text[cursor:match.start()]) - out.append(replacement) - cursor = match.end() - out.append(text[cursor:]) - text = ''.join(out) - if changed: - changed_menu_files.add(path.as_posix()) - path.write_bytes(text.encode('utf-8')) - - if changed_menu_files != expected_menu_files: - raise SystemExit( - 'Unexpected RU menu patch set. ' - f'Expected {sorted(expected_menu_files)}, got {sorted(changed_menu_files)}' - ) - - metadata = { - 'blog/us-bookkeeping-small-business.html': ( - '', - '', - ), - 'ru/blog/buhgalteriya-ssha-malogo-biznesa.html': ( - '', - '', - ), - } - - for file_name, (twitter_image, twitter_alt) in metadata.items(): - path = Path(file_name) - text = path.read_bytes().decode('utf-8') - if text.count(twitter_alt) == 0: - if text.count(twitter_image) != 1: - raise SystemExit(f'Expected exactly one twitter:image in {file_name}') - newline = '\r\n' if '\r\n' in text else '\n' - text = text.replace(twitter_image, twitter_image + newline + twitter_alt, 1) - path.write_bytes(text.encode('utf-8')) - if path.read_bytes().decode('utf-8').count(twitter_alt) != 1: - raise SystemExit(f'Expected exactly one twitter:image:alt in {file_name}') - - # Accessibility source QA across every RU HTML page using the mobile toggle. - for path in sorted(Path('ru').rglob('*.html')): - text = path.read_bytes().decode('utf-8') - for match in menu_tag.finditer(text): - tag = match.group(0) - label = re.search(r'aria-label="([^"]+)"', tag) - if not label or not label.group(1).strip(): - raise SystemExit(f'Missing accessible name in {path}: {tag}') - if 'aria-expanded="false"' not in tag: - raise SystemExit(f'aria-expanded damaged in {path}: {tag}') - controls = re.search(r'aria-controls="([^"]+)"', tag) - if not controls: - raise SystemExit(f'aria-controls missing in {path}: {tag}') - if f'id="{controls.group(1)}"' not in text: - raise SystemExit(f'aria-controls target missing in {path}: {controls.group(1)}') - - # Flagship metadata QA: preserve canonical/hreflang/OG/Twitter image and schema blocks. - checks = { - 'blog/us-bookkeeping-small-business.html': [ - '', - '', - '', - '', - '"@type":"Article"', - '"@type":"BreadcrumbList"', - '"@type":"FAQPage"', - ], - 'ru/blog/buhgalteriya-ssha-malogo-biznesa.html': [ - '', - '', - '', - '', - '"@type":"Article"', - '"@type":"BreadcrumbList"', - '"@type":"FAQPage"', - ], - } - for file_name, required in checks.items(): - text = Path(file_name).read_bytes().decode('utf-8') - for item in required: - if text.count(item) != 1: - raise SystemExit(f'Flagship QA failed in {file_name}: {item}') - if text.count('