From 305dcccab0e6d5dae3378010a064656724c9bc91 Mon Sep 17 00:00:00 2001 From: godavid Date: Mon, 9 Nov 2026 09:27:38 +0100 Subject: [PATCH] =?UTF-8?q?fix(pipeline):=20anom=C3=A1li=C3=A1s=20jogszab?= =?UTF-8?q?=C3=A1ly=20m=C3=A1r=20commitolt=20mai=20=C3=A1llapota=20is=20az?= =?UTF-8?q?=20indexbe=20=C3=A9s=20a=20HEAD-helyre=C3=A1ll=C3=ADt=C3=A1sba?= =?UTF-8?q?=20ker=C3=BCl=20(#38)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A bekerült események szűrése eddig a slug szintű anomália-halmazból számolt, így az anomália előtti, már commitolt mai állapot kimaradt az indexből, a felvételi naplóból és a HEAD-helyreállításból. Az anomália mostantól a napját is rögzíti; a bekerülést a bekerultAllapot(), a helyreállítás szükségességét a helyreallitasKell() dönti el (tesztelve). Co-Authored-By: Claude Opus 5.5 --- AGENTS.md | 2 +- docs/uzemeltetes.md | 7 +++-- packages/pipeline/src/delta.ts | 44 +++++++++++++++------------ packages/pipeline/src/health.ts | 24 +++++++++++++++ packages/pipeline/test/health.test.ts | 34 +++++++++++++++++++++ 5 files changed, 88 insertions(+), 23 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index d9b4308..b231ab2 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -102,7 +102,7 @@ együttes megadására hibát ad. | Horgony: a `szakaszokraBont()` horgonya bit szerint egyezik az `mdRender()`-ével (`-2`, `-3` utótagokkal) | `packages/szoveg/src/szakaszok.ts`, `apps/web/lib/md.ts` | `packages/szoveg/test/szakaszok.test.ts` | | XSS: minden szövegcsomópont `esc()`-en megy át; a horgony-id `[a-z0-9-]`; csak a rögzített tag-készlet kerül ki | `apps/web/lib/md.ts` | `packages/szoveg/test/md-xss.test.ts` | | Keresési kiemelés STX/ETX, nem HTML | `packages/pipeline/supabase/02-`, `04-`, `05-*.sql`; `apps/web/app/kereses/page.tsx` | `kapuk.yml` `kiemeles` job (grep az SQL-ekre és a `dangerouslySetInnerHTML`-re) | -| Rossz adat nem kerül az adat-repóba: szerkezeti hibánál a delta semmit nem commitol, terjedelem-anomáliánál az érintett jogszabályt kihagyja | `packages/pipeline/src/delta.ts`, `health.ts` | `packages/pipeline/test/health.test.ts` (terjedelem-őr, szétcsúszás-őr, parser-őrfeltételek; a delta commit-elhagyó ága maga teszteletlen) | +| Rossz adat nem kerül az adat-repóba: szerkezeti hibánál a delta semmit nem commitol, terjedelem-anomáliánál a jogszabály anomáliás és aznapi későbbi állapotait kihagyja, a korábbiak bekerülnek az indexbe | `packages/pipeline/src/delta.ts`, `health.ts` | `packages/pipeline/test/health.test.ts` (terjedelem-őr, szétcsúszás-őr, parser-őrfeltételek, `bekerultAllapot()`, `helyreallitasKell()`; a delta commit-elhagyó ága maga teszteletlen) | | Az index-szinkron hibája nem buktatja a deltát | `delta.ts` `keresoIndexSzinkron()` | csak a kód | | A diff szerver oldalon renderel | `apps/web/components/DiffNezet.tsx` | csak a kódkomment | | A `napi-delta.yml` két példánya byte-azonos | `packages/pipeline/adatrepo/napi-delta.yml` ↔ `godavid/magyar-jog` | `kapuk.yml` `napi-delta-masolat` job (heti `cmp` az élő példánnyal; PR-en nem fut) | diff --git a/docs/uzemeltetes.md b/docs/uzemeltetes.md index 50cc4c6..c0ef5ae 100644 --- a/docs/uzemeltetes.md +++ b/docs/uzemeltetes.md @@ -196,9 +196,10 @@ Tünet: piros napi delta futás + `parser-riasztas` issue. Szerkezeti hibánál (cím-, hatálydátum-eltérés, ismeretlen osztály, splicing) a delta SEMMIT nem commitol — rossz adat nem kerülhet a repóba. A terjedelem-anomália ezzel szemben -JOGSZABÁLYONKÉNTI: az érintett jogszabályt (és aznapi további állapotait) kihagyja, -a többit normálisan commitolja, a végén külön issue-val riaszt, és a kihagyott -állapotokat a következő futások újra megpróbálják. (2026-08-26-tól kilenc napig +JOGSZABÁLYONKÉNTI: az anomáliás állapotot (és a jogszabály aznapi további állapotait) +kihagyja, a korábbi mai állapotait és a többi jogszabályt normálisan commitolja és +indexeli, a végén külön issue-val riaszt, és a kihagyott állapotokat a következő +futások újra megpróbálják. (2026-08-26-tól kilenc napig egyetlen módosító törvény duzzadása miatt 37 más törvény állapota sem került be — ez volt a tanulság.) diff --git a/packages/pipeline/src/delta.ts b/packages/pipeline/src/delta.ts index 9b80002..ce14759 100644 --- a/packages/pipeline/src/delta.ts +++ b/packages/pipeline/src/delta.ts @@ -30,6 +30,8 @@ import { markdownGeneralas } from "./normalize.js"; import { megjelolesIllesztes, parsolSnapshot } from "./parse.js"; import { naploIras, type FelvetelTetel } from "./felvetel.js"; import { + bekerultAllapot, + helyreallitasKell, modositoBlokkValtozas, modositoTorveny, regebbiAllapot, @@ -102,14 +104,21 @@ async function retegTerkepMentes(terkep: RetegTerkep): Promise { } /** - * Terjedelem-anomáliára futott jogszabályok (slug → a hiba üzenete). Ezeket a - * futás kihagyja — a mai további állapotaikkal együtt, hogy a HEAD ne egy - * későbbi állapotot mutasson, miközben egy korábbi hiányzik —, a többi - * jogszabály viszont normálisan bekerül. A végén riasztunk. Egy jogszabály - * tartalmi anomáliája nem állíthatja le az egész napi frissítést: 2026-08-26-tól - * kilenc napig egyetlen módosító törvény miatt 37 új időállapot nem került be. + * Terjedelem-anomáliára futott jogszabályok (slug → az anomáliás állapot napja + * és a hiba üzenete). Az anomáliás állapotot a futás kihagyja — a jogszabály + * mai további állapotaival együtt, hogy a HEAD ne egy későbbi állapotot + * mutasson, miközben egy korábbi hiányzik. Az anomália előtti mai állapotai + * viszont már commitolódtak, ezek bekerülnek (bekerultAllapot()), ahogy a többi + * jogszabály is. A végén riasztunk. Egy jogszabály tartalmi anomáliája nem + * állíthatja le az egész napi frissítést: 2026-08-26-tól kilenc napig egyetlen + * módosító törvény miatt 37 új időállapot nem került be. */ -const anomaliak = new Map(); +const anomaliak = new Map(); + +/** A mai esemény ténylegesen bekerült-e (commitolódott-e) az adat-repóba. */ +function bekerultEsemeny(e: UjEsemeny): boolean { + return bekerultAllapot(e.allapot.hatalyba, anomaliak.get(e.js.slug)?.datum); +} async function fut(): Promise { if (!existsSync(join(ADAT_REPO_DIR, "index", "allapotok.json"))) { @@ -257,7 +266,7 @@ async function fut(): Promise { }); } catch (e) { if (!(e instanceof TerjedelemAnomalia)) throw e; - anomaliak.set(js.slug, e.message); + anomaliak.set(js.slug, { datum: allapot.hatalyba, uzenet: e.message }); console.error(`[kihagyva] ${e.message}`); continue; } @@ -279,9 +288,7 @@ async function fut(): Promise { ); } if (uzenetSorok.length === 0) continue; // a nap minden tétele kimaradt - const nevek = napiak - .filter((n) => !anomaliak.has(n.js.slug)) - .map((n) => n.js.rovidites ?? n.js.megjeloles); + const nevek = napiak.filter(bekerultEsemeny).map((n) => n.js.rovidites ?? n.js.megjeloles); const cim = nevek.length === 1 ? `${nevek[0]} — időállapot ${datum}` @@ -289,19 +296,18 @@ async function fut(): Promise { await commit(`${cim}\n\nForrás (njt):\n${uzenetSorok.map((s) => `- ${s}`).join("\n")}`, datum); console.log(`Commit: ${datum} — ${nevek.join(", ")}`); } - const bekerult = esemenyek.filter((e) => !anomaliak.has(e.js.slug)); + const bekerult = esemenyek.filter(bekerultEsemeny); // 2b. HEAD-helyreállítás: ha egy jogszabálynak ma csak a legutóbb ismert // állapotánál régebbi állapota került be, a HEAD szövege a régi szöveg lett. // A legutóbbi állapot szövegét a saját commitjából visszaírjuk és a saját // dátumával újra commitoljuk, így a HEAD soha nem áll vissza régebbi - // szövegre. (Ez a git-ág teszteletlen: a regebbiAllapot() döntése tesztelt.) + // szövegre. (Ez a git-ág teszteletlen: a regebbiAllapot() és a + // helyreallitasKell() döntése tesztelt.) for (const { js, utolso } of helyreallitasok.values()) { - if (anomaliak.has(js.slug)) continue; // ma semmi nem került be belőle - const maiLegujabb = bekerult - .filter((e) => e.js.slug === js.slug) - .reduce((m, e) => (e.allapot.hatalyba > m ? e.allapot.hatalyba : m), ""); - if (!maiLegujabb || maiLegujabb >= utolso.datum) continue; // a HEAD már a legújabbat mutatja + // anomáliás jogszabálynál is: az anomália előtti mai állapota már commitolódott + const maiDatumok = bekerult.filter((e) => e.js.slug === js.slug).map((e) => e.allapot.hatalyba); + if (!helyreallitasKell(maiDatumok, utolso.datum)) continue; if (!utolso.sha) { throw new Error(`HEAD-helyreállítás sikertelen: ${js.slug} ${utolso.datum} állapotához az indexben nincs sha`); } @@ -394,7 +400,7 @@ async function fut(): Promise { await keresoIndexSzinkron(valtozottSlugok, listaIndex, retegTerkep); if (anomaliak.size > 0) { - const sorok = [...anomaliak.values()].map((s) => `- ${s}`).join("\n"); + const sorok = [...anomaliak.values()].map((a) => `- ${a.uzenet}`).join("\n"); console.error(`${anomaliak.size} jogszabály terjedelem-anomália miatt kimaradt:\n${sorok}`); await riaszt( "Terjedelem-anomália — kézi ellenőrzés kell", diff --git a/packages/pipeline/src/health.ts b/packages/pipeline/src/health.ts index ff408c8..a7c7e23 100644 --- a/packages/pipeline/src/health.ts +++ b/packages/pipeline/src/health.ts @@ -124,6 +124,30 @@ export function regebbiAllapot( return utolso && utolso.datum > ujDatum ? utolso : undefined; } +/** + * Bekerült-e a jogszabály egy mai állapota, ha a jogszabály `anomaliaDatum` + * napú állapota terjedelem-anomáliára futott (undefined: nem futott). + * + * A napi delta dátum szerint növekvő sorrendben dolgozik, és az első anomália + * után a jogszabály további mai állapotait kihagyja; az anomália ELŐTTI mai + * állapotai viszont már commitolódtak. Ezeknek az indexbe, a felvételi naplóba + * és a HEAD-helyreállításba is be kell kerülniük (#38) — a jogszabály egészét + * kidobni ezért hiba. + */ +export function bekerultAllapot(datum: string, anomaliaDatum: string | undefined): boolean { + return anomaliaDatum === undefined || datum < anomaliaDatum; +} + +/** + * Kell-e HEAD-helyreállítás: a jogszabály ma bekerült állapotai közül a + * legújabb is régebbi a legutóbb ismert állapotánál, így a HEAD szövege a régi + * szöveg lett. Ha ma semmi nem került be belőle, nincs mit helyreállítani. + */ +export function helyreallitasKell(maiDatumok: string[], utolsoDatum: string): boolean { + if (maiDatumok.length === 0) return false; + return maiDatumok.reduce((m, d) => (d > m ? d : m)) < utolsoDatum; +} + /** A terjedelem-őr hibája — a hívó ezt jogszabályonként kezeli, nem futás-szinten. */ export class TerjedelemAnomalia extends Error {} diff --git a/packages/pipeline/test/health.test.ts b/packages/pipeline/test/health.test.ts index 97c1c5f..fe39ee1 100644 --- a/packages/pipeline/test/health.test.ts +++ b/packages/pipeline/test/health.test.ts @@ -1,5 +1,7 @@ import { describe, expect, it } from "vitest"; import { + bekerultAllapot, + helyreallitasKell, modositoBlokkValtozas, modositoTorveny, regebbiAllapot, @@ -178,3 +180,35 @@ describe("regebbiAllapot (régebbi-állapot-őr, HEAD-helyreállítás)", () => expect(regebbiAllapot([], "1930-11-08")).toBeUndefined(); }); }); + +describe("anomáliás jogszabály mai bekerülése (#38)", () => { + // egy jogszabály két mai állapota: d1 átment és commitolódott, d2 anomáliás + const d1 = "2026-01-01"; + const d2 = "2026-03-01"; + const maiak = [d1, d2]; + const bekerultek = (anomaliaDatum: string | undefined) => + maiak.filter((d) => bekerultAllapot(d, anomaliaDatum)); + + it("az anomália előtti, már commitolt állapot bekerül, az anomáliás nem", () => { + expect(bekerultek(d2)).toEqual([d1]); + }); + it("ha az első mai állapot anomáliás, semmi nem kerül be", () => { + expect(bekerultek(d1)).toEqual([]); + }); + it("anomália nélkül minden mai állapot bekerül", () => { + expect(bekerultek(undefined)).toEqual([d1, d2]); + }); + it("HEAD-helyreállítás kell, ha a bekerült d1 régebbi a legutóbb ismertnél", () => { + expect(helyreallitasKell(bekerultek(d2), "2026-02-01")).toBe(true); + }); + it("nincs helyreállítás, ha a bekerült d1 nem régebbi a legutóbb ismertnél", () => { + expect(helyreallitasKell(bekerultek(d2), d1)).toBe(false); + expect(helyreallitasKell(bekerultek(d2), "2025-12-01")).toBe(false); + }); + it("nincs helyreállítás, ha ma semmi nem került be", () => { + expect(helyreallitasKell(bekerultek(d1), "2026-02-01")).toBe(false); + }); + it("több bekerült állapotnál a legújabb dönt", () => { + expect(helyreallitasKell(["2026-01-01", "2026-04-01"], "2026-02-01")).toBe(false); + }); +});