-
Notifications
You must be signed in to change notification settings - Fork 0
feat(related-persons): регистърни доказателства за връзките (#279) #2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 19 commits
a40df20
4fde377
ea00781
b364031
f8b198f
98ac4f5
800b2c1
5f64f5c
4226ae3
2a08744
796903a
d50a723
7a424bd
299400e
7f2e174
2327e62
76ed3b2
935ce1c
f4d73dc
2b06b76
95d8c51
0a61317
df76ae7
2d3c064
26333d8
436dbc2
024c26f
ae661ac
9555fbf
6265854
37e1258
17f9472
ddaaacb
8139adb
8db751d
3e047c6
4f4ff60
73e055d
8689212
bf4fa97
466d002
e5e8709
aee1427
36cdd9d
4d21ba0
37a3db8
8e6f1dc
ce5b2ad
e9c17b9
021da5a
b8b4332
2039b9f
da80eaf
013b177
1ab3dcd
753dd4d
daeb6e9
426e2ef
e481baa
ac15498
24c8001
23a1939
2813726
9999ad5
f8973a2
b17e70c
0a97059
0d8be1e
90c2f99
25d189e
227bb4c
215123d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,119 @@ | ||
| name: Related-persons — Trade Register cache refresh (monthly) | ||
|
|
||
| # Refreshes the Търговски регистър deed cache the publishing decision rests on (#279, ADR-0033). | ||
| # | ||
| # WHY THIS IS ITS OWN WORKFLOW, separate from the decision run: this is the only step in the project | ||
| # that touches a public register at volume, and it is the only one with a network budget. The decisions | ||
| # themselves are a pure function of declarations, cached deeds and contracts — they must be able to run | ||
| # daily without ever calling the register. Splitting them keeps that true by construction. | ||
| # | ||
| # It SHIPS NOTHING. It writes only the cache under scratch/tr/, which is git-ignored and never leaves | ||
| # the runner except as an artifact. A run that fails leaves the previous cache intact. | ||
| # | ||
| # It also PURGES: raw deeds past the 35-day retention (ADR-0033 decision 5) are deleted at the end of | ||
| # every run, including one a 429 cut short. Retention is not the same knob as freshness — --max-age-days | ||
| # decides what gets re-requested, --retention-days decides what stops being kept — and the default is | ||
| # the ADR's 35 days, so the rail holds even when the workflow passes neither. | ||
| # | ||
| # Pace and refusals live in scripts/tr/fetch-deeds.mjs and are not configurable here on purpose: | ||
| # 1 request / 3 s, sequential, a closed candidate set, and a 429 ends the run (exit 2) without marking | ||
| # anything. Spec §3.3 permits a bounded per-ЕИК lookup and forbids bulk scraping; the limiter is the | ||
| # operator's only way to state a rate preference, so we do not tune around it. | ||
|
|
||
| on: | ||
| schedule: | ||
| # 02:00 UTC on the 1st. Monthly, per #279 §9 — a deed changes rarely, and every extra pass is | ||
| # ~400 requests against somebody else's service for almost no new information. | ||
| - cron: '0 2 1 * *' | ||
| workflow_dispatch: | ||
| inputs: | ||
| max_age_days: | ||
| description: Re-fetch deeds older than this many days (default 30). Use a large value to force a full refresh. | ||
| type: string | ||
| default: '30' | ||
| limit: | ||
| description: Stop after this many lookups (blank = all pending). Bound a first run while watching for a 429. | ||
| type: string | ||
| default: '' | ||
|
|
||
| concurrency: | ||
| # Never two crawls at once: they would double the request rate against the register, which is the one | ||
| # thing the pacing exists to prevent. Queue rather than cancel — a cancelled crawl wastes its progress. | ||
| group: tr-cache-refresh | ||
| cancel-in-progress: false | ||
|
|
||
| permissions: | ||
| contents: read | ||
|
|
||
| jobs: | ||
| refresh: | ||
| runs-on: ubuntu-latest | ||
| # ~400 candidates at 1 per 3 s is ~20 minutes; the ceiling allows retries and a slower day without | ||
| # letting a hung run sit for the 6-hour default. | ||
| timeout-minutes: 60 | ||
| steps: | ||
| - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 | ||
| - uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9 | ||
| - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 | ||
| with: | ||
| node-version: 24 | ||
| cache: pnpm | ||
|
|
||
| # The cache is the whole point of the workflow: without restoring it, every run would re-fetch | ||
| # every deed and turn a 400-request refresh into a 400-request full crawl each month. | ||
| - name: Restore the deed cache | ||
| id: cache | ||
| uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.0.2 | ||
| with: | ||
| path: scratch/tr | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. B3 (privacy):
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Потвърдено, и то беше нарушение на собственото обещание на ADR-0033 решение 5. Поправено в ae661ac. Права сте и за причината: Не приех обаче „кеширай само индекса" — това чупи решаващия ход: Затова изборът е първата от вашите две алтернативи в друга форма: никаква граница. Обхождане и решение в едно задание, кешира се само Последица, която да е казана честно: решението не може да се преизчисли без актовете, а актовете не преживяват изпълнителя — значи всеки ход, който решава, трябва и да обхожда. Ежедневно решение би значело ~400 заявки дневно към чужд регистър. Затова графикът стана месечен, а не ежедневен, както го описват §9 и ADR-0033. Връщането към ежедневно иска обхождането да издава присъди по (връзка, ЕИК), за да пресичат границата само булеви стойности — това е промяна в дизайна и нарочно не е вкарана тук. |
||
| key: tr-deed-cache-${{ github.run_id }} | ||
| restore-keys: | | ||
| tr-deed-cache- | ||
|
|
||
| - run: pnpm install --frozen-lockfile | ||
|
|
||
| # The closed candidate set. It is produced by the decision run (load.mjs writes it), so a cache | ||
| # refresh can never invent an ЕИК to look up — it can only refresh what the corpus already | ||
| # resolved. Absent on a first run, in which case there is nothing to refresh yet. | ||
| - name: Check for a candidate list | ||
| id: candidates | ||
| run: | | ||
| if [ -s scratch/cacbg/staging/candidate-eiks.txt ]; then | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. B1 (the other half of the deadlock):
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Потвърдено — и двете половини на блокировката бяха точно както ги описвате. Поправено в ae661ac (+ 024c26f). Този работен поток е премахнат. Обхождането на регистъра вече тече вътре в заданието на решенията. Не може да се поправи с пренасяне на файлове между ходовете, защото решението се нуждае от суровите актове (
|
||
| echo "count=$(wc -l < scratch/cacbg/staging/candidate-eiks.txt)" >> "$GITHUB_OUTPUT" | ||
| echo "ok=true" >> "$GITHUB_OUTPUT" | ||
| else | ||
| echo "No candidate-eiks.txt — run the decision workflow first. Nothing to refresh." | ||
| echo "ok=false" >> "$GITHUB_OUTPUT" | ||
| fi | ||
|
|
||
| - name: Refresh deeds | ||
| if: steps.candidates.outputs.ok == 'true' | ||
| env: | ||
| MAX_AGE: ${{ inputs.max_age_days || '30' }} | ||
| LIMIT: ${{ inputs.limit }} | ||
| run: | | ||
| set -euo pipefail | ||
| ARGS=(--eiks-file scratch/cacbg/staging/candidate-eiks.txt --max-age-days "$MAX_AGE") | ||
| [ -n "$LIMIT" ] && ARGS+=(--limit "$LIMIT") | ||
| set +e | ||
| node scripts/tr/fetch-deeds.mjs "${ARGS[@]}" | ||
| code=$? | ||
| set -e | ||
| # Exit 2 is the rate limiter, and it is NOT a build failure: the run stopped politely, marked | ||
| # nothing, and the partial cache is valid and resumable. Failing the workflow here would train | ||
| # everyone to ignore a red X on the one signal that means "the register asked us to stop". | ||
| if [ "$code" -eq 2 ]; then | ||
| echo "::warning::The register rate-limited us; the run stopped and the cache is resumable." | ||
| exit 0 | ||
| fi | ||
| exit "$code" | ||
|
|
||
| - name: Publish the cache index as an artifact (no raw deeds — they carry third-party data) | ||
| if: always() && steps.candidates.outputs.ok == 'true' | ||
| uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 | ||
| with: | ||
| name: tr-cache-index | ||
| # The SQLite index only. scratch/tr/deeds/*.json holds owner and manager names and company | ||
| # addresses (ADR-0033 decision 5) and must not leave the runner. | ||
| path: scratch/tr/tr-cache.sqlite | ||
| retention-days: 7 | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
B1+B2: This cron can never go green: the hydrate step recreates the work DB with only the 4 EOP tables, and nothing in this workflow restores
scratch/tr→load.mjsdies atREFUSE TO LOAD: no Trade Register cacheon every daily run. At the same time the monotonicity pre-wipe export readsinterest_linksfrom that same recreated DB → "no such table" → snapshot[]every run, i.e. the gate from2327e62protects nothing in CI. Both resolve in one place: exportinterest_links+interest_link_evidencetoo in the hydrate step (it already runswrangler d1 exportfor 4 tables), and restore the TR index from cache/artifact/R2.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Потвърдено и по двете точки, и наистина се решават на едно място — макар и не съвсем на онова, което предлагате. Поправено в ae661ac (+ 024c26f).
B1. Работният поток за обхождане е премахнат; обхождането тече вътре в това задание. Възстановяване на
scratch/trот кеш/артефакт не приех — това е самото нарушение от B3, а суровите актове са и точно данните, коитоevidenceVerdictизисква (сравнява името на декларатора със самия текст на регистъра). Затова: без граница между двете.--emit-candidates(024c26f) зарежда списъка от нула на изпълнител без кеш, обхождането го използва,load.mjsчетеscratch/trот същия изпълнител, а суровите актове се трият изрично.B2. Точно така — стъпката за захранване правеше
rm -f backfill.sqliteи изнасяше само четирите EOP таблици, така че предварителният износ четеше „no such table", снимката беше[]при всеки ход, и гейтът от 2327e62 не пазеше нищо. Сега се изнасят иinterest_links+interest_link_evidence— не за резолвера (load.mjsги пресъздава), а именно като вход на гейта. Изнасят се отделно и липсата им се търпи: при първи ход 0003/0006 още не са приложени.Едно нещо, което открих при поправката и което си струва да се отбележи, защото щеше да върне B2 през задния вход: зареждащият ход не бива да пипа истинската работна база. Стигането до списъка с кандидати минава през пресъздаване на таблиците, което изхвърля
interest_links, а ходът никога не публикува — значи щеше да остави таблицата празна. Следващият истински ход щеше да прочете тази празна таблица като предишно публикувано състояние, да запише празна снимка, и гейтът, чиято единствена работа е да забележи изчезнало публикувано твърдение, щеше да минава безусловно завинаги. Затова--emit-candidatesработи върху захвърляемо копие. Проверено с мутация: насочен към истинската база, проваля и двата теста.Последица за графика, казана честно: решението не може да се преизчисли без актовете, актовете не преживяват изпълнителя, значи всеки ход, който решава, трябва и да обхожда — а ежедневно решение би значело ~400 заявки дневно към чужд регистър. Графикът стана месечен, не ежедневен, както го описват §9 и ADR-0033. Отбелязано е в коментар в самия работен поток. Връщането към ежедневно иска обхождането да издава присъди по (връзка, ЕИК), за да пресичат границата само булеви стойности — промяна в дизайна, нарочно невключена тук.