landvex: IOL v3 + Identify API + Neo4j-graf + byggpipeline
- FastAPI Identify API (port 8081) - Neo4j propertygraf (64 noder, 80 kanter) - Ingest-pipeline (Python) - Byggscript + integrationstester - Docker Compose setup
This commit is contained in:
@@ -0,0 +1,69 @@
|
||||
# AGENTARMÉ — implementeringsorder v1
|
||||
## Landvex produktionspaket · 58 agenter · 11 skvadroner · 4 faser
|
||||
|
||||
Detta dokument är arbetsordern. Paketets filer är enda sanningskällan. Ingenting implementeras som inte går att spåra hit.
|
||||
|
||||
⸻
|
||||
|
||||
## Hårda regler — gäller varje agent, utan undantag
|
||||
|
||||
1. **Enda sanningskälla:** `10-data/` + `lvx-objekt-schema.json`. Ingen agent uppfinner fakta, fält, klasser, standarder eller ID:n.
|
||||
2. **Aldrig mock i kundnära väg.** Saknad förmåga levereras som ärligt svar med konfidens + automatisk bounty (mönstret i `30-produkt/lvx-api-demosvar.json`, case 3) — aldrig som låtsad träff, aldrig som stubbe i miljö en kund kan nå.
|
||||
3. **PLATSHÅLLARE serialiseras aldrig** ut ur något publikt gränssnitt. Filtret sitter i serialiseringskoden och testas.
|
||||
4. **Källa på varje påstående.** Publika svar bär `verifieringsniva`, `konfidens` och `kallor[]` — obligatoriska fält enligt API-kontraktet.
|
||||
5. **Standarder refereras** med organ + beteckning + roll. Standardtext återges aldrig (juridikregeln).
|
||||
6. **ID:n mintas endast** via registertjänsten (`lvx-id-register.json`-logiken i `ingest.py`). ID:n återanvänds aldrig, av ingen.
|
||||
7. **Verifieringstrappan är enkelriktad:** obekräftad → källbelagd (kräver källor) → fältverifierad (endast fotoflödet) → tillverkarbekräftad (endast portalflödet). Ingen kod får sätta nivå utanför sitt flöde.
|
||||
8. **Grind före merge/deploy:** `00-styrning/verifiera.py` grönt + checksummor mot `MANIFEST.json`. Rött stoppar allt.
|
||||
9. **Ontologi-/schemaändring** endast via Ändringsrådet (skvadron A) med versionslyft och migrering.
|
||||
10. **Osäkerhet uttrycks, döljs aldrig.** Konfidens är produktyta, inte intern metadata.
|
||||
|
||||
⸻
|
||||
|
||||
## Skvadroner (58 agenter)
|
||||
|
||||
**A · Programkontor & arkitektur (3)** — äger detta dokument, MANIFEST, gränssnittskontrakt och Ändringsrådet. Godkänner fasgrindar. *Input:* allt. *Output:* beslut, versionslyft.
|
||||
|
||||
**B · Plattform & SRE (6)** — Fas 0: dev/stage/prod, IaC, hemligheter, observabilitet, larm, backup/DR. Driftsätter samtliga tjänster. *DoD:* verifiera.py körs grönt i CI på varje miljö; återställningstest genomfört.
|
||||
|
||||
**C · Datakärna & graf (6)** — laddar `lvx-klassposter-master-v3.json` + modellfilerna till dokument-+graflager; grafen härleds alltid ur posterna (aldrig handredigerad); ID-registertjänst med atomär mintning. *DoD:* laddad databas reproducerar exakt grafstatistiken i `lvx-graf-kanter-v3.json` (176 kanter · 80 LVX · 82 standard).
|
||||
|
||||
**D · Ingest-automation (7)** — produktifierar `20-automation/ingest.py` till tjänst: tre köer (schemalagd / kunddriven / fält), skördare med källvitlista och referatregel, extraktor-API bakom `lvx-extraktionsprompt.md`, granskningskö-UI för konflikter, körningsrapporter. *DoD:* våg 7-batchen i `intag/` replayad genom tjänsten ger identiskt resultat (4 nivålyft, 1 mint, 0 fel).
|
||||
|
||||
**E · Object API (7)** — implementerar `30-produkt/lvx-pilot-api-spec.md` exakt: 6 endpoints, svarskontraktet, PLATSHÅLLARE-filter, feedback→bounty, nycklar, rate limits och **usage-mätning per identify för debitering** (SaaS-kärnan). *DoD:* kontraktstester gröna; de tre golden-casen reproduceras via API:t mot riktig data.
|
||||
|
||||
**F · Vision & identify (6)** — utvärderings- och träningsflöde från bountydata; identify-modell bakom API:t. Tills modellen når beslutad tröskel svarar identify med klasskandidat + ärlig konfidens — regel 2 i praktiken. *DoD:* golden-case-regressionssvit; konfidenskalibrering rapporterad.
|
||||
|
||||
**G · QUIXZOOM fältflöde (6)** — bounty-motorn (`lvx-zoomer-bounties-vag1.json` + auto-förslagen): poäng, först-på-individ ×2, dubblettkontroll hash+geo, hårdkodade säkerhets- och integritetsregler (auto-blurr, inga registreringsskyltar), foto→OCR→**fältverifierad**-lyft, LVX-I-individregister. *DoD:* ett komplett e2e-lyft källbelagd→fältverifierad i stage med riktigt foto.
|
||||
|
||||
**H · Tillverkarportal (4)** — flödet som sätter **tillverkarbekräftad**: claims kopplade till modellposter, spårbara godkännanden, aldrig standardtext. *DoD:* en modellpost lyft e2e i stage.
|
||||
|
||||
**I · Säkerhet, juridik & compliance (4)** — källpolicy (referat, aldrig kopiering), licensregister per källa, GDPR för positionsdata, åtkomstmodell, pentest. *DoD:* godkänd granskning av serialisering + datalager.
|
||||
|
||||
**J · QA & verifiering (5)** — äger och utökar `verifiera.py`; kontraktstester mot API-spec; datagrind 0 valideringsfel; golden-case-replay i CI; last- och kaostester. *DoD:* grindarna blockerar på riktigt (bevisat med avsiktligt rött bygge).
|
||||
|
||||
**K · Kund, pilot & SaaS-paketering (4)** — Pilot/Pro/Enterprise enligt API-specens paketering, mätarbaserad debitering på identify, onboarding, pilotworkshop med `40-strategi/landvex-pilotpitch.pptx`. *DoD:* första designpartner tecknad till pilotstart.
|
||||
|
||||
⸻
|
||||
|
||||
## Faser & grindar
|
||||
|
||||
**Fas 0 — Grund (B, C, J):** miljöer uppe, data laddad, verifiera.py grönt i CI.
|
||||
→ *Grind 0:* checksummor + 0 valideringsfel + grafstatistik reproducerad.
|
||||
|
||||
**Fas 1 — Produkt (D, E, F, I):** ingest-tjänst och Object API live mot riktig data.
|
||||
→ *Grind 1:* golden cases via API:t; våg 7-replay identisk; pentest av API-ytan.
|
||||
|
||||
**Fas 2 — Flyhjul (G, F):** fältflödet live; feedback→bounty→foto→fältverifierad sluter loopen.
|
||||
→ *Grind 2:* första e2e-nivålyftet; kunddrivna kön mätbar.
|
||||
|
||||
**Fas 3 — Skala (H, K, D):** tillverkarportal, fler skördebatchar genom pipelinen, pilotkund onboardad.
|
||||
→ *Grind 3:* tillverkarbekräftad e2e; kostnad per verifierat faktum rapporteras sjunkande.
|
||||
|
||||
Beroenden: C före D/E · E före F/G-exponering · G före H:s fältdata · J tvärs igenom alla faser.
|
||||
|
||||
⸻
|
||||
|
||||
## Eskalering
|
||||
|
||||
Datakonflikt → granskningskö (D) → Ändringsrådet (A). Schemabehov → A med versionslyft. Säkerhets-/juridikfynd → I stoppar berörd leverans omedelbart. Oklarhet om fakta → svaret är alltid: gå till källfilen; finns det inte där, finns det inte.
|
||||
@@ -0,0 +1,210 @@
|
||||
{
|
||||
"paket": "landvex-produktionspaket",
|
||||
"version": "1.0",
|
||||
"genererad": "2026-07-04",
|
||||
"statistik": {
|
||||
"objektklasser": 37,
|
||||
"kallbelagda_klasser": 35,
|
||||
"produktmodeller": 27,
|
||||
"grafkanter": 176,
|
||||
"aktiva_bounties": 16,
|
||||
"bounty_forslag": 17,
|
||||
"valideringsfel": 0
|
||||
},
|
||||
"kanonisk_not": "10-data/ ersätter alla tidigare versioner (master v1/v2, delfiler). Arkiv kvar i arbetsytan utanför paketet.",
|
||||
"antal_filer": 32,
|
||||
"filer": [
|
||||
{
|
||||
"sokvag": "00-styrning/AGENTARME.md",
|
||||
"roll": "styrning",
|
||||
"bytes": 6037,
|
||||
"sha256": "dcacfff60fa3354729fe758b0baf2a0478faa011acd679686709de813dac85c1"
|
||||
},
|
||||
{
|
||||
"sokvag": "00-styrning/OVERSIKT.md",
|
||||
"roll": "styrning",
|
||||
"bytes": 2497,
|
||||
"sha256": "6005162be3fd4921255bf39eed7d6ec8f8ecbffa058f3e32d367410375fe0880"
|
||||
},
|
||||
{
|
||||
"sokvag": "00-styrning/verifiera.py",
|
||||
"roll": "styrning",
|
||||
"bytes": 5745,
|
||||
"sha256": "1723e94897d87f3a83a22995ae034aca88302b70611ad3b12dfb51888503ec64"
|
||||
},
|
||||
{
|
||||
"sokvag": "10-data/lvx-bounty-kandidater-vag7.json",
|
||||
"roll": "data (enda sanningskälla)",
|
||||
"bytes": 4798,
|
||||
"sha256": "d6f3e8a192c769a85fcc7c5e0043128838ccbb6074c38857943f8b558cd9d682"
|
||||
},
|
||||
{
|
||||
"sokvag": "10-data/lvx-exempel-brunnsbetackning.json",
|
||||
"roll": "data (enda sanningskälla)",
|
||||
"bytes": 4700,
|
||||
"sha256": "cc7f09f629512829330f91232523d4f5d33bef92983e4c2a24631064d703454b"
|
||||
},
|
||||
{
|
||||
"sokvag": "10-data/lvx-graf-kanter-v3.json",
|
||||
"roll": "data (enda sanningskälla)",
|
||||
"bytes": 40573,
|
||||
"sha256": "2a920c49767e812574a2d4d83cd6d04c2231d3005ff09ebb54c364a5dfe47527"
|
||||
},
|
||||
{
|
||||
"sokvag": "10-data/lvx-id-register.json",
|
||||
"roll": "data (enda sanningskälla)",
|
||||
"bytes": 593,
|
||||
"sha256": "939ce9b0305724e7cdf3fb00a44610f6e122489ccdec652a7188cc747f6ff54e"
|
||||
},
|
||||
{
|
||||
"sokvag": "10-data/lvx-klassposter-master-v3.json",
|
||||
"roll": "data (enda sanningskälla)",
|
||||
"bytes": 152207,
|
||||
"sha256": "b568ee83d4f6b941dcf07b0f5631cd38431cd727436687bb0f3e52cf9d5078f8"
|
||||
},
|
||||
{
|
||||
"sokvag": "10-data/lvx-objekt-schema.json",
|
||||
"roll": "data (enda sanningskälla)",
|
||||
"bytes": 8023,
|
||||
"sha256": "8abb092517947b5313c3a7fd23c835c9126b9054d688f50c1b5fa968a17f05f5"
|
||||
},
|
||||
{
|
||||
"sokvag": "10-data/lvx-produktmodeller-auto.json",
|
||||
"roll": "data (enda sanningskälla)",
|
||||
"bytes": 1592,
|
||||
"sha256": "59e1fdc6af7ec282664fadd7c9943bdd1742e6d97a14a5fe24fdb7fa8b4ccfd7"
|
||||
},
|
||||
{
|
||||
"sokvag": "10-data/lvx-produktmodeller-vag2.json",
|
||||
"roll": "data (enda sanningskälla)",
|
||||
"bytes": 24976,
|
||||
"sha256": "25d8618e172971982fab827ae45982c9e161f59050deb3d44e1c1813e2f7f2cb"
|
||||
},
|
||||
{
|
||||
"sokvag": "10-data/lvx-produktmodeller-vag3.json",
|
||||
"roll": "data (enda sanningskälla)",
|
||||
"bytes": 15428,
|
||||
"sha256": "490242c8f798c09932451226d1fcadb255bcc808c258d81e583346417d911c50"
|
||||
},
|
||||
{
|
||||
"sokvag": "10-data/lvx-taxonomi-tier1.json",
|
||||
"roll": "data (enda sanningskälla)",
|
||||
"bytes": 6316,
|
||||
"sha256": "7ef057a11d6d0cc5ba6da6fac40790f85f9a713716c7eb20b8c27993cd616464"
|
||||
},
|
||||
{
|
||||
"sokvag": "10-data/lvx-taxonomi-tier2.json",
|
||||
"roll": "data (enda sanningskälla)",
|
||||
"bytes": 4419,
|
||||
"sha256": "4701f03f5dbed92d48dae5d40a9d5ad5ed427340b9f9bdc6b04bcb7776787515"
|
||||
},
|
||||
{
|
||||
"sokvag": "10-data/lvx-zoomer-bounties-vag1.json",
|
||||
"roll": "data (enda sanningskälla)",
|
||||
"bytes": 5651,
|
||||
"sha256": "fc314b0641e92d3ee663a08a423826b04e53a9361d474f35520acafb7466081d"
|
||||
},
|
||||
{
|
||||
"sokvag": "20-automation/ingest.py",
|
||||
"roll": "automation",
|
||||
"bytes": 12071,
|
||||
"sha256": "93c41f1eebe2564454e1743d7791eb8ed3f02916bede017f648d556bfb729ea5"
|
||||
},
|
||||
{
|
||||
"sokvag": "20-automation/intag/kallpaket-vag7.json",
|
||||
"roll": "automation",
|
||||
"bytes": 4242,
|
||||
"sha256": "0df98d5bb0843f563ea39a8716e5ed9e450a2cee06689734afab39199df840db"
|
||||
},
|
||||
{
|
||||
"sokvag": "20-automation/intag/kandidatposter-vag7.json",
|
||||
"roll": "automation",
|
||||
"bytes": 7126,
|
||||
"sha256": "c1816b9077232ecf1752ffd659d0385eae6f0edac3f5bf90b4b91e1ab2f701d3"
|
||||
},
|
||||
{
|
||||
"sokvag": "20-automation/lvx-extraktionsprompt.md",
|
||||
"roll": "automation",
|
||||
"bytes": 2171,
|
||||
"sha256": "ae9bceb372fa64745bee75979ff7907643e50133bfae120f2f50321266255a30"
|
||||
},
|
||||
{
|
||||
"sokvag": "20-automation/lvx-ingest-spec.md",
|
||||
"roll": "automation",
|
||||
"bytes": 3879,
|
||||
"sha256": "073a67d8921fe005f89dd4a4a5647a0c4abfc920022e65612ffa2e63d4b20ce4"
|
||||
},
|
||||
{
|
||||
"sokvag": "30-produkt/lvx-api-demosvar.json",
|
||||
"roll": "produkt",
|
||||
"bytes": 4936,
|
||||
"sha256": "f9a7eb102084b1d0add635260d1fe623b7467c3b57fc50be1a762a56773348be"
|
||||
},
|
||||
{
|
||||
"sokvag": "30-produkt/lvx-pilot-api-spec.md",
|
||||
"roll": "produkt",
|
||||
"bytes": 3766,
|
||||
"sha256": "596325020e8f87009639491036c7d65f23a2eb321b2524f1d5f1e294927794d7"
|
||||
},
|
||||
{
|
||||
"sokvag": "30-produkt/lvx-zoomer-bountyprogram.md",
|
||||
"roll": "produkt",
|
||||
"bytes": 3887,
|
||||
"sha256": "05f70da6891b34901ede9b4f1249375b051f6927035cb46830005bdeb70a6c04"
|
||||
},
|
||||
{
|
||||
"sokvag": "40-strategi/iol-grundspecifikation.md",
|
||||
"roll": "strategi & rapporter",
|
||||
"bytes": 9721,
|
||||
"sha256": "4c7fbed1d30407467d17b2ff97ea14692028b5632e9d6a522381c82c1836f01a"
|
||||
},
|
||||
{
|
||||
"sokvag": "40-strategi/landvex-genome-strategi.md",
|
||||
"roll": "strategi & rapporter",
|
||||
"bytes": 8777,
|
||||
"sha256": "99b6a5030f70a6abf2c3f4a65d2869e4dfde598d135c83ecb54f01204726b28a"
|
||||
},
|
||||
{
|
||||
"sokvag": "40-strategi/landvex-pilotpitch.pptx",
|
||||
"roll": "strategi & rapporter",
|
||||
"bytes": 170939,
|
||||
"sha256": "bbcbec18e3701a73e27e987b873abac886fc70ef96bc93fc315cbe9518dd113a"
|
||||
},
|
||||
{
|
||||
"sokvag": "40-strategi/rapporter/lvx-leveransrapport-tier1.md",
|
||||
"roll": "strategi & rapporter",
|
||||
"bytes": 3613,
|
||||
"sha256": "a5b3d57dc00f3d56df91c27664f186a237eaa783344988db31504e7151983140"
|
||||
},
|
||||
{
|
||||
"sokvag": "40-strategi/rapporter/lvx-leveransrapport-vag2.md",
|
||||
"roll": "strategi & rapporter",
|
||||
"bytes": 3186,
|
||||
"sha256": "f63016785654b7bc0d9face400fc8ac4401aba52c45aea03cc25f61b1fb4c1d4"
|
||||
},
|
||||
{
|
||||
"sokvag": "40-strategi/rapporter/lvx-leveransrapport-vag3.md",
|
||||
"roll": "strategi & rapporter",
|
||||
"bytes": 2804,
|
||||
"sha256": "3276174b8f65b28a88a6bfd911d5773e1c4f5e1fa15ee002269c6e7b3f765058"
|
||||
},
|
||||
{
|
||||
"sokvag": "40-strategi/rapporter/lvx-leveransrapport-vag4.md",
|
||||
"roll": "strategi & rapporter",
|
||||
"bytes": 2698,
|
||||
"sha256": "19a62e5d4c7e9d8422ec46baae85576dec2b4d769a1cc227669ecc93b6ccefc4"
|
||||
},
|
||||
{
|
||||
"sokvag": "40-strategi/rapporter/lvx-leveransrapport-vag5.md",
|
||||
"roll": "strategi & rapporter",
|
||||
"bytes": 2745,
|
||||
"sha256": "8a019cc71ede7683026f60401c46f96bdde0117c025808225871c7c657a56652"
|
||||
},
|
||||
{
|
||||
"sokvag": "40-strategi/rapporter/lvx-leveransrapport-vag7.md",
|
||||
"roll": "strategi & rapporter",
|
||||
"bytes": 2887,
|
||||
"sha256": "a8e9f287988a44eca60c13c05015903e4918d04e4db5b36e6971977533ff9644"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
# LANDVEX PRODUKTIONSPAKET v1
|
||||
## Översikt · 2026-07-04
|
||||
|
||||
Allt material och all automation från utvecklingstråden, paketerat för implementering av agentarmén. Arbetsordern ligger i `00-styrning/AGENTARME.md`. Integriteten ligger i `00-styrning/MANIFEST.json` (SHA-256 per fil) och grinden i `00-styrning/verifiera.py`.
|
||||
|
||||
⸻
|
||||
|
||||
## Innehållskarta
|
||||
|
||||
| Mapp | Innehåll | Kanonisk status |
|
||||
|---|---|---|
|
||||
| `00-styrning/` | AGENTARMÉ (arbetsorder, 58 agenter), MANIFEST (checksummor), verifiera.py (grind), denna översikt | Styr allt |
|
||||
| `10-data/` | Biblioteket: master v3 (37 klasser, 35 källbelagda), graf v3 (176 kanter), schema, taxonomier, 27 modeller, ID-register, 16 bounties + 17 auto-förslag, exempelpost | **Enda sanningskälla.** Ersätter alla äldre versioner (v1/v2 och delfiler arkiveras utanför paketet) |
|
||||
| `20-automation/` | `ingest.py` (pipelinens kärna), extraktionsprompten (LLM-kontraktet), `intag/` med våg 7-batchen (källpaket + kandidatposter) för replay-test | Körbar, demonstrerad: 4 nivålyft · 1 ID mintat · 0 fel |
|
||||
| `30-produkt/` | Object API-spec v0 (6 endpoints, svarskontrakt, SaaS-paketering), tre golden demo-svar, Zoomer-bountyprogrammet | Implementeras exakt av skvadron E/G |
|
||||
| `40-strategi/` | Genome-strategin, IOL-grundspecifikationen, pilotpitch-deck (pptx), samtliga leveransrapporter | Kontext & säljmaterial |
|
||||
|
||||
⸻
|
||||
|
||||
## Snabbstart (varje agent, dag 1)
|
||||
|
||||
1. Läs `AGENTARME.md` — särskilt de tio hårda reglerna.
|
||||
2. Kör `python3 00-styrning/verifiera.py` från paketroten — ska sluta **GRÖNT**.
|
||||
3. Hitta din skvadron, dina inputfiler och din Definition of Done.
|
||||
4. Fakta finns bara i `10-data/`. Finns det inte där, finns det inte.
|
||||
|
||||
⸻
|
||||
|
||||
## Nyckeltal vid paketering
|
||||
|
||||
37 objektklasser (35 källbelagda, 2 ärligt obekräftade) · 27 produktmodeller (alla källbelagda) · 176 grafkanter (80 LVX-upplösta, 82 standardkanter) · 0 valideringsfel · ID-register: nästa P = 0000028 · Verifieringstrappa: obekräftad → källbelagd → fältverifierad → tillverkarbekräftad.
|
||||
|
||||
⸻
|
||||
|
||||
## Ärlighetsnot om "deployat"
|
||||
|
||||
Paketet är körbart och maskinverifierat i byggmiljön (verifiera.py grönt vid paketering). Driftsättning i er infrastruktur är per definition **Fas 0 i arbetsordern** — miljöer, dataladdning och CI-grindar sätts av skvadron B/C/J innan något exponeras. Att påstå något annat vore att bryta paketets egen regel 2. Inga mockar, inga låtsade tjänster: det som inte är driftsatt hos er kallas inte driftsatt.
|
||||
@@ -0,0 +1,111 @@
|
||||
#!/usr/bin/env python3
|
||||
# Landvex produktionspaket — verifieringsgrind
|
||||
# Körs från paketroten: python3 00-styrning/verifiera.py
|
||||
# GRÖNT krävs för merge/deploy. Kontrollerar:
|
||||
# 1) SHA-256 mot MANIFEST 2) schemaregler (0 fel) 3) grafen reproduceras exakt
|
||||
# 4) ID-registret konsistent 5) referensintegritet (bounties, demosvar, relationer)
|
||||
import json, re, hashlib, sys
|
||||
from pathlib import Path
|
||||
|
||||
ROT = Path(__file__).resolve().parent.parent
|
||||
PREDIKAT = {"monteras_pa","star_pa","ansluts_med","matas_fran","ingar_i","del_av",
|
||||
"ersatter","ersatts_av","kompatibel_med","forvaxlas_med","tillverkas_av","foljer_standard","variant_av"}
|
||||
VERIF = {"obekraftad","kallbelagd","faltverifierad","tillverkarbekraftad"}
|
||||
DOMANER = {"TRP","ELN","VAT","TEL","BYG","PRK","JVG","HMN","FLG"}
|
||||
ID_RE = re.compile(r"^LVX-(TRP|ELN|VAT|TEL|BYG|PRK|JVG|HMN|FLG)-[0-9]{4}$|^LVX-P-[0-9]{7}$")
|
||||
fel = []
|
||||
|
||||
def load(rel):
|
||||
with open(ROT/rel, encoding="utf-8") as f: return json.load(f)
|
||||
|
||||
# ---- 1. Checksummor ----
|
||||
man = load("00-styrning/MANIFEST.json")
|
||||
for post in man["filer"]:
|
||||
p = ROT/post["sokvag"]
|
||||
if not p.exists():
|
||||
fel.append(f"MANIFEST: saknad fil {post['sokvag']}"); continue
|
||||
h = hashlib.sha256(p.read_bytes()).hexdigest()
|
||||
if h != post["sha256"]:
|
||||
fel.append(f"MANIFEST: checksumma avviker {post['sokvag']}")
|
||||
|
||||
# ---- 2. Schemaregler ----
|
||||
poster = {p["lvx_id"]: p for p in load("10-data/lvx-klassposter-master-v3.json")["poster"]}
|
||||
modeller = {}
|
||||
for fn in ["lvx-produktmodeller-vag2.json","lvx-produktmodeller-vag3.json","lvx-produktmodeller-auto.json"]:
|
||||
for m in load(f"10-data/{fn}")["poster"]: modeller[m["lvx_id"]] = m
|
||||
|
||||
def kolla(rec, typ):
|
||||
rid = rec.get("lvx_id","?")
|
||||
if not ID_RE.match(rid): fel.append(f"{rid}: ID-format")
|
||||
if rec.get("doman") not in DOMANER: fel.append(f"{rid}: domän")
|
||||
if not (rec.get("namn",{}).get("sv") and rec["namn"].get("en")): fel.append(f"{rid}: namn")
|
||||
prov = rec.get("proveniens",{})
|
||||
if prov.get("verifieringsniva") not in VERIF: fel.append(f"{rid}: verifieringsnivå")
|
||||
for pr in rec.get("problem",[]):
|
||||
if not pr.get("kalla"): fel.append(f"{rid}: problem utan källa")
|
||||
for rel in rec.get("relationer",[]):
|
||||
if rel.get("predikat") not in PREDIKAT: fel.append(f"{rid}: predikat")
|
||||
if typ=="modell" and not rec.get("parent"): fel.append(f"{rid}: parent")
|
||||
if prov.get("verifieringsniva")=="kallbelagd" and not prov.get("kallor"): fel.append(f"{rid}: källbelagd utan källor")
|
||||
|
||||
for p in poster.values(): kolla(p,"klass")
|
||||
for m in modeller.values(): kolla(m,"modell")
|
||||
|
||||
# ---- 3. Grafen reproduceras ----
|
||||
kanter, olosta = [], set()
|
||||
def multi(m): return " / " in m and all(ID_RE.match(x.strip()) for x in m.split(" / "))
|
||||
def kant(s,pred,obj):
|
||||
typ = "lvx" if ID_RE.match(obj or "") else ("standard" if pred=="foljer_standard" else "text")
|
||||
if typ=="text": olosta.add(obj)
|
||||
kanter.append(typ)
|
||||
def emit(s,pred,m):
|
||||
for x in (m.split(" / ") if multi(m) else [m]): kant(s,pred,x.strip())
|
||||
for p in poster.values():
|
||||
for rel in p.get("relationer",[]): emit(p["lvx_id"],rel["predikat"],rel["mal"])
|
||||
for st in p.get("standarder",[]):
|
||||
if st.get("beteckning"): kant(p["lvx_id"],"foljer_standard",st["beteckning"])
|
||||
for m in modeller.values():
|
||||
kant(m["lvx_id"],"tillhor_klass",m["parent"])
|
||||
for e in m.get("historik",{}).get("ersatter",[]): kant(m["lvx_id"],"ersatter",e)
|
||||
for e in m.get("historik",{}).get("ersatts_av",[]): kant(m["lvx_id"],"ersatts_av",e)
|
||||
for rel in m.get("relationer",[]): emit(m["lvx_id"],rel["predikat"],rel["mal"])
|
||||
g = load("10-data/lvx-graf-kanter-v3.json")["statistik"]
|
||||
fakta = {"kanter_totalt": len(kanter), "upplosta_lvx_kanter": kanter.count("lvx"),
|
||||
"standardkanter": kanter.count("standard"), "textnoder_olosta": len(olosta)}
|
||||
for k,v in fakta.items():
|
||||
if g.get(k) != v: fel.append(f"GRAF: {k} förväntat {g.get(k)}, fick {v}")
|
||||
|
||||
# ---- 4. ID-registret ----
|
||||
reg = load("10-data/lvx-id-register.json")
|
||||
max_p = max(int(m[6:]) for m in modeller)
|
||||
if reg["P_nasta"] <= max_p: fel.append(f"REGISTER: P_nasta {reg['P_nasta']} ≤ högsta använda {max_p}")
|
||||
for rid in poster:
|
||||
dom, num = rid[4:7], rid[8:]
|
||||
tier, nn = num[1], int(num[2:])
|
||||
if reg["klass_nasta"].get(dom,{}).get(tier,0) <= nn:
|
||||
fel.append(f"REGISTER: {dom} tier {tier} nästa ≤ använda {nn}")
|
||||
|
||||
# ---- 5. Referensintegritet ----
|
||||
alla = set(poster) | set(modeller)
|
||||
for b in load("10-data/lvx-zoomer-bounties-vag1.json")["bounties"]:
|
||||
if b["mal_lvx"] not in alla: fel.append(f"BOUNTY {b['bounty_id']}: okänt mål {b['mal_lvx']}")
|
||||
for f_ in load("10-data/lvx-bounty-kandidater-vag7.json")["forslag"]:
|
||||
if f_["mal_lvx"] not in alla: fel.append(f"BOUNTYFÖRSLAG: okänt mål {f_['mal_lvx']}")
|
||||
demoraw = (ROT/"30-produkt/lvx-api-demosvar.json").read_text(encoding="utf-8")
|
||||
for rid in set(re.findall(r"LVX-(?:TRP|ELN|VAT|TEL|BYG|PRK|JVG|HMN|FLG)-[0-9]{4}|LVX-P-[0-9]{7}", demoraw)):
|
||||
if rid not in alla: fel.append(f"DEMOSVAR: okänt lvx_id {rid}")
|
||||
for p in poster.values():
|
||||
for rel in p.get("relationer",[]):
|
||||
m = rel["mal"]
|
||||
for x in (m.split(" / ") if multi(m) else [m]):
|
||||
if ID_RE.match(x.strip()) and x.strip() not in alla:
|
||||
fel.append(f"{p['lvx_id']}: relation mot okänt ID {x.strip()}")
|
||||
|
||||
# ---- Resultat ----
|
||||
print(f"Filer i manifest: {len(man['filer'])} | Klasser: {len(poster)} | Modeller: {len(modeller)}")
|
||||
print(f"Graf: {fakta['kanter_totalt']} kanter ({fakta['upplosta_lvx_kanter']} lvx / {fakta['standardkanter']} standard / {fakta['textnoder_olosta']} textnoder)")
|
||||
if fel:
|
||||
print(f"\nRÖTT — {len(fel)} fel:")
|
||||
for e in fel: print(" !!", e)
|
||||
sys.exit(1)
|
||||
print("\nGRÖNT — paketet verifierat. Grind passerad.")
|
||||
Reference in New Issue
Block a user