import json, sys

IN = '/var/www/html/peta/storage/app/propertylab-catalogue-match/review/v_hr_02_in.jsonl'
OUT = '/var/www/html/peta/storage/app/propertylab-catalogue-match/review/v_hr_02_out.jsonl'

S, P, D, U = 'SAME', 'PART_OF', 'DIFFERENT', 'UNSURE'
# case: (verdict, cand, conf, relation, web, reason)
V = {
'v0002': (S, 0, 62, None, True, "Estate name exact; R flats vs C houses; nearby TM Residency is by Tunas Muda co-op, not proven same."),
'v0006': (P, 0, 90, 'R_in_C', False, "R is Section 7 flats; C k0 is the whole Seksyen 7 Bandar Baru Bangi locality."),
'v0010': (S, 1, 92, None, True, "Exact name 32 m, Sungai Ara; web RM356-522 psf, RM490-636k matches R; C price wrong."),
'v0014': (S, 0, 92, None, True, "Exact name 16 m; web 2025 median RM659 psf, RM610k matches R; C price stale."),
'v0018': (S, 0, 80, None, False, "Same estate name, Penang, C includes flats; psf agrees; mixed-estate price gap."),
'v0022': (P, 0, 92, 'C_in_R', False, "R covers all Vista Komanwel blocks; C lists A, B, C separately; prices match."),
'v0026': (S, 0, 90, None, False, "Same name 30 m, Mukim Batu fits Dutamas; psf plausible, median price looks like bulk sales."),
'v0030': (S, 0, 86, None, False, "Exact name, Kuchai Lama; R is phase 1, not Kuchai Sentral 2; no C price."),
'v0034': (S, 0, 93, None, True, "Exact name 112 m; web median RM250k, RM290 psf equals R; C price data wrong."),
'v0038': (S, 0, 86, None, False, "Ruby condo in Muar town 12 m; second Ruby Apartment 1.5 km away; no price check."),
'v0042': (U, 0, 50, None, False, "'Bandar' vs 'Taman' Machang Bubok unclear; road-level point 2.7 km; flat category, house-level prices."),
'v0046': (S, 1, 95, None, False, "Zen Residence Puchong 40 m; psf and price within 3%."),
'v0050': (S, 2, 92, None, False, "Daisy apartment in Taman Subang Perdana; psf and price within 3%; R point misplaced."),
'v0054': (S, 1, 90, None, False, "Sri Putramas I, Dutamas, Mukim Batu; psf and price identical; II/III are separate projects."),
'v0058': (P, 0, 88, 'R_in_C', False, "R is Section 15 flats inside C's Seksyen 15 Bandar Baru Bangi locality row."),
'v0062': (S, 1, 80, None, True, "Web: Pangsapuri Seri Jati is in Puchong Hartamas; k1 same name, Puchong; R price above listings."),
'v0066': (S, 0, 86, None, False, "Skyridge Apartment 78 m, mukim 18 Tanjung Tokong; Skyridge Garden is a newer pricier condo."),
'v0070': (S, 0, 86, None, False, "Flat Stulang Laut 34 m, JB; psf 0.81, price 0.91."),
'v0074': (S, 0, 80, None, True, "Web: estate has several apartment blocks plus terraces; R apartments, C row terrace-only."),
'v0078': (S, 1, 68, None, True, "Township name exact; R units larger than web low-cost flats, likely shop apartments; C terrace-typed."),
'v0082': (S, 2, 92, None, False, "SkyVille 8 @ Benteng, Old Klang Road 66 m; psf within 9%."),
'v0086': (S, 0, 82, None, False, "Exact estate name, Penang, includes flats; psf 0.85; R geocoded from name."),
'v0090': (P, 0, 75, 'R_in_C', False, "R is a township-level serviced apartment scheme; C k0 is the 16 Sierra township row."),
'v0094': (S, 1, 95, None, False, "The Latitude, Tanjung Tokong 76 m; psf and price within 2%."),
'v0098': (S, 0, 75, None, True, "Same Jinjang locality, same price, 2.4 km; web lists Sri Jinjang Court separately, alias unconfirmed."),
'v0102': (S, 0, 94, None, False, "Exact name, Klang; psf and price identical."),
'v0106': (S, 0, 88, None, False, "Exact name, KL, 133 m; duplicate k1 price agrees; R district blank."),
'v0110': (S, 0, 90, None, False, "Centre Court flat, KL; psf and price within 1%."),
'v0114': (S, 0, 92, None, False, "Exact distinctive name; Mukim Petaling matches Kuchai Lama; affordable price consistent."),
'v0118': (D, None, 75, None, True, "R is MKH TR Residence phase 1; k0 is TR2 phase 2; 1 Razak Mansion is Residensi Bandar Tun Razak."),
'v0122': (S, 0, 90, None, False, "Alias Permai Seri matches; psf within 4%; Ampang, Selangor."),
'v0126': (S, 0, 93, None, False, "Exact PPR name, Setapak 147 m; price identical; C psf erroneous."),
'v0130': (P, 0, 78, 'R_in_C', False, "R is Section 2 flats of Taman Pinggiran Putra; C k0 is the estate row."),
'v0134': (S, 0, 72, None, False, "Exact estate name 45 m, Bayan Lepas; R apartments vs C terraces; price gap."),
'v0138': (S, 0, 92, None, True, "Exact name 15 m, Ampang Hilir; web 3,000-5,500 sq ft units fit R; Katana Residences separate."),
'v0142': (S, 2, 92, None, True, "Web: Kayangan Puri Mutiara is Pearlvue Heights (1993, Tanjung Tokong); k2 psf and price within 4%."),
'v0146': (S, 0, 93, None, True, "Exact name 10 m; web RM900-1,480 psf matches R's 1,155; C price stale."),
'v0150': (S, 1, 94, None, False, "Exact name incl. Meda Park alias, Kepong; psf and price identical."),
'v0154': (S, 0, 94, None, True, "Web: SkyLuxe On The Park is Residensi SkyLuxe; recent RM870-990 psf matches R."),
'v0158': (S, 0, 90, None, False, "Residensi Star is Star Residences, KLCC; psf within 2%."),
'v0162': (S, 0, 86, None, False, "Exact Section 7 name, Seri Kembangan 542 m; no price."),
'v0166': (S, 0, 86, None, False, "Only KL Pangsapuri Mawar is Sentul; psf and price within 7%."),
'v0170': (P, 1, 75, 'C_in_R', False, "R lacks a number; C splits Sri Mekar 1 and 2 at same site; k1 price matches."),
'v0174': (S, 0, 93, None, False, "99 Residence @ KL North; psf within 3%, price 10%."),
'v0178': (S, 1, 95, None, False, "Exact name, JB 46 m; psf and price within 5%."),
'v0182': (S, 1, 92, None, False, "Vistaria, Sungai Ara 73 m; psf and price within 5%."),
'v0186': (P, 0, 88, 'R_in_C', True, "Web: Rafflesia condo, Sentul, median RM428 psf matches R; R is its phase 3; C point 34 m."),
'v0190': (S, 0, 90, None, False, "Villa Pantai condominium; psf within 7%, price within 3%; R point offset."),
'v0194': (S, 0, 78, None, True, "Exact name, Permas Jaya; web RM275-658 psf only loosely covers R's 552; C psf 391."),
'v0198': (U, 0, 50, None, False, "Name exact but state blank; R price fits houses, not Batu Pahat flats."),
'v0202': (S, 0, 95, None, False, "MasReca 19 Cyberjaya; psf and price identical."),
'v0206': (S, 1, 75, None, False, "Exact estate name, Rawang; no price; nearby Rawang Mutiara condo psf close, price not."),
'v0210': (S, 0, 86, None, False, "Same estate, Ampang, includes condos; psf within 5%, price 15%."),
'v0214': (S, 0, 70, None, False, "Estate name exact, includes flats; state blank; psf mismatch."),
'v0218': (P, 0, 86, 'C_in_R', False, "R 'Le Chateau' prices sit between Le Chateau 1 and 2; R pools both."),
'v0222': (S, 0, 86, None, False, "Wangsa Heights, Ulu Kelang; psf 0.85, price 0.80."),
'v0226': (S, 0, 78, None, False, "Exact estate name 13 m; R low-cost units vs C terrace row."),
'v0230': (S, 0, 82, None, False, "Taman Batu Uban apartment; psf and price within 2%; C row labelled phase 2."),
'v0234': (S, 2, 90, None, False, "Putra One Residence, Bukit Rahman Putra fits mukim Sungai Buloh; psf and price within 11%."),
'v0238': (S, 1, 92, None, False, "Sentrio Suites alias Sentrio Pandan 5 m; psf 1.13."),
'v0242': (S, 0, 75, None, False, "Exact estate name, Klang, includes flats; psf 0.92; R point 16 km off."),
'v0246': (S, 1, 88, None, False, "Exact distinctive name, Bandar Puteri Puchong; C k0 price data implausible."),
'v0250': (S, 0, 92, None, False, "Exact name 39 m, KL Town Centre; Pavilion Residences separate."),
'v0254': (S, 0, 93, None, False, "Alias Menara Warisan exact; psf and price within 6%."),
'v0258': (P, 1, 65, 'C_in_R', False, "R pools IOI Resort City condos (psf 648); Puteri Palma is one older condo within."),
'v0262': (S, 0, 90, None, False, "Exact name 16 m, Georgetown mukim; Halaman Pulau Tikus is another building."),
'v0266': (S, 0, 93, None, False, "Alam Prima, Shah Alam; psf and price identical."),
'v0270': (S, 2, 68, None, False, "Taman Masai estate; R flats vs C houses; Residensi Masai is different."),
'v0274': (S, 1, 92, None, False, "Sri Pelangi condo, Setapak 72 m; psf and price within 4%."),
'v0278': (S, 2, 72, None, False, "Taman Mas Sepang estate includes flats; Pangsapuri Jati price equal; R point misplaced."),
'v0282': (U, None, 55, None, False, "State blank; many Saujana estates; Pontian row is houses; apartment rows unconfirmed."),
'v0286': (S, 0, 93, None, False, "Exact name, KL Town Centre; psf 1.09, price 1.07."),
'v0290': (S, 0, 78, None, False, "Exact estate name, Klang; no price; R district blank."),
'v0294': (S, 0, 92, None, False, "Exact flat row 27 m, Skudai/Pulai; estate psf agrees."),
'v0298': (S, 1, 94, None, False, "Da Men USJ; psf 1.08, price identical."),
'v0302': (S, 0, 92, None, False, "Exact name 5 m; duplicate Hock Ann Mansion 9 m has identical price."),
'v0306': (S, 0, 88, None, False, "Exact name, KL; no price; R district blank."),
'v0310': (P, 2, 55, 'R_in_C', False, "28 Boulevard lies in Taman Pandan Perdana locality row; no candidate is the building; R price odd."),
'v0314': (S, 0, 78, None, False, "The Maple Residences at R's point; mukim label conflicts; no C psf."),
'v0318': (S, 3, 78, None, False, "Suria Apartment PJ price identical; generic name, R district blank."),
'v0322': (P, 3, 90, 'C_in_R', False, "R covers both Danau Impian and Danau Idaman; prices match each."),
'v0326': (S, 0, 86, None, False, "Sri Desa condo, Kuchai Lama, only KL match; psf and price agree."),
'v0330': (S, 1, 95, None, False, "The Westside One, Desa ParkCity 20 m; psf and price within 4%."),
'v0334': (U, 3, 50, None, False, "State blank; Klang Taman Meru rows are houses; R's flat prices unexplained."),
'v0338': (S, 1, 94, None, False, "The H2O, Jelutong 53 m; psf 1.07, price 1.12."),
'v0342': (S, 0, 95, None, False, "Alias Residensi Laman Permai exact; psf identical."),
'v0346': (S, 0, 88, None, False, "Garden Plaza Cyberjaya; same unit size; psf 1.31 reflects launch prices."),
'v0350': (S, 3, 93, None, False, "Midas Perling 51 m, mukim Pulai; psf identical; Seri Alam one is elsewhere."),
'v0354': (S, 0, 90, None, False, "Teluk Indah flat, Seberang Perai Tengah; psf and price identical."),
'v0358': (S, 1, 95, None, False, "Impian Senibong; psf and price within 3%."),
'v0362': (S, 3, 82, None, True, "Web: Marina Residence, Bayu Puteri, 1,219/1,487 sq ft near RM500 psf fits R; Bayu Marina smaller."),
'v0366': (S, 0, 92, None, False, "Taman Plentong Utama flat; psf and price identical."),
'v0370': (S, 1, 92, None, False, "Taman Tegoh, Georgetown 44 m; psf and price within 2%."),
'v0374': (S, 0, 85, None, False, "Catalogue alias Segemal Indah for Springs Ville, Butterworth; no price; state blank."),
'v0378': (S, 0, 94, None, False, "1 Sentul; psf and price within 4%."),
'v0382': (S, 0, 93, None, False, "Bukit OUG condominiums; psf and price within 8%."),
'v0386': (S, 0, 82, None, True, "Web: Seri Molek Perdana sells RM258-300k, matching R; 'Seri' dropped; Segamat namesake is houses."),
'v0390': (S, 0, 62, None, False, "Kampung Sungai Danga row; R flats within it; Seri Danga prices differ."),
'v0394': (S, 0, 90, None, False, "Exact name 55 m, Elmina; fixed affordable price explains psf 0.77."),
'v0398': (S, 0, 90, None, False, "Exact name, Georgetown 191 m; Wisma Kebun Nyor is a different building."),
'v0402': (S, 0, 86, None, False, "Exact name 28 m, Setapak; no price; Villa Harmonis is a separate building."),
'v0406': (S, 0, 75, None, False, "Taman Naga Emas estate includes flats; R low-cost flats; 2018 apartment row unlikely."),
'v0410': (S, 0, 88, None, False, "Exact distinctive name 25 m, Bukit Jelutong; no price; R district blank."),
'v0414': (S, 0, 88, None, False, "Exact distinctive name 8 m, Batu Ferringhi; no price; R district blank."),
'v0418': (S, 0, 82, None, False, "Pangsapuri Taman Bukit Rawang Putra; psf 1.31 borderline; R district blank."),
'v0422': (P, 3, 90, 'C_in_R', False, "R covers Winner Court; C splits blocks A and B, both priced like R."),
'v0426': (S, 0, 90, None, False, "Kang Har Tong apartment 10 m, Jelutong; R is the flat part."),
'v0430': (S, 0, 93, None, False, "Wangsa Maju Section 10 flats; psf within 3%."),
'v0434': (S, 1, 93, None, False, "Alias Bayu Tiara, Bayan Lepas; psf and price identical."),
'v0438': (S, 0, 90, None, True, "Web: Desa Pelangi, Jalan Logan, 1,130 sq ft near RM663 psf matches R; Kajang namesake cheaper."),
'v0442': (S, 0, 82, None, False, "PPR Wahyu, Batu; psf and price identical; R names phase 1 only."),
'v0446': (U, 3, 50, None, True, "Web: Plot C is the low-medium cost block; no candidate names it; k3 price similar."),
'v0450': (S, 0, 84, None, False, "Exact name 16 m, Bangsar; similar unit size; psf 1.40 conflict; R district blank."),
'v0454': (S, 0, 75, None, False, "Taman Mawar, Bandar Kinrara, includes flats; R flats priced below estate."),
'v0458': (S, 2, 80, None, False, "Pangsapuri Cheras Utama psf within 2%; R state blank."),
'v0462': (S, 0, 90, None, False, "Pangsapuri Persiaran Tanjung, JB; psf and price within 2%."),
'v0466': (S, 0, 90, None, True, "Web: Aria KLCC is also called Pangsapuri Aria, RM1,211-2,330 psf; k0 freehold 2019 matches."),
'v0470': (S, 0, 88, None, False, "Exact estate name; psf and price within 5%; R point offset."),
'v0474': (S, 0, 90, None, False, "Exact name, Selayang; no price."),
'v0478': (S, 1, 90, None, False, "Flat Serantau Baru; psf and price within 11%; exact-name duplicate k0."),
'v0482': (S, 2, 68, None, False, "Taman Desa Skudai estate; R condo psf above both apartment rows."),
'v0486': (S, 0, 60, None, False, "Ampang Jaya locality row with flats; R flat prices unusually high."),
'v0490': (U, 0, 50, None, False, "R is a road-level serviced apartment bucket; C road row is an unnamed 2010 condo."),
'v0494': (S, 1, 93, None, False, "Exact name, Batu Caves; psf and price identical; R point misplaced."),
'v0498': (S, 1, 90, None, False, "Exact name, Cheras; psf identical, price within 5%."),
'v0502': (S, 0, 90, None, False, "Exact name 31 m, George Town."),
'v0506': (S, 2, 90, None, False, "R names Bandar Kinrara; k2 is Seri Kasturi Kinrara, prices within 5%; k0/k1 are Setia Alam."),
'v0510': (S, 2, 90, None, True, "Web: Block B is the condominium, 1,302-1,345 sq ft, RM599+ psf, matching R."),
'v0514': (S, 0, 90, None, False, "Alias Residensi 1 Utara exact; Lakepark Residence, KL North."),
'v0518': (S, 0, 84, None, False, "Golf View 104 m, mukim 13; duplicate k1 price agrees."),
'v0522': (S, 0, 80, None, False, "Ara Greens Apartment, Penang; psf 1.21; R state blank."),
'v0526': (S, 1, 80, None, False, "Bandar Selesa Jaya flat; psf identical; R township-level name."),
'v0530': (S, 0, 80, None, False, "Exact estate name, Bukit Mertajam, includes flats."),
'v0534': (S, 1, 93, None, False, "Prima Bayu, Klang 61 m; psf within 6%."),
'v0538': (S, 1, 88, None, False, "Ayuman Suites, Gombak 51 m; same unit size; psf 0.80."),
'v0542': (S, 0, 85, None, False, "MH Platinum Residences phase 1; psf 1.18; MH Platinum 2 is a cheaper Residensi."),
'v0546': (P, 0, 70, 'R_in_C', False, "R pools condos of Bandar Sungai Long; C k0 is the locality row."),
'v0550': (S, 1, 94, None, False, "8 Gurney 10 m; psf identical."),
'v0554': (S, 0, 88, None, False, "Flat Taman Bukit Angkasa 68 m; estate price agrees; R district blank."),
'v0558': (S, 0, 86, None, False, "Exact name, Cyberjaya; same unit size; psf 0.73 likely affordable launch price."),
'v0562': (S, 2, 90, None, False, "Vogue Suites One @ KL Eco City; k2 price within 8%."),
'v0566': (P, 2, 60, 'R_in_C', False, "28 Boulevard lies in Taman Pandan Perdana locality row; no candidate is the building itself."),
'v0570': (S, 0, 88, None, False, "Exact distinctive name 4 m, Ayer Itam; no price; R district blank."),
'v0574': (S, 1, 75, None, False, "Alias Symphony Court, Ampang; C price 2.2x R's."),
'v0578': (S, 0, 88, None, False, "Exact distinctive name, Selayang; no price; R district blank."),
'v0582': (S, 0, 93, None, False, "Kiara 163, Mont Kiara; psf within 1%."),
'v0586': (S, 2, 95, None, False, "Dorsett Residence Bukit Bintang; psf and price identical."),
'v0590': (S, 0, 80, None, False, "Bayan Baru flats row matches R's township flat scheme; no price."),
'v0594': (S, 1, 92, None, False, "Ayuman Suites, Gombak 51 m; psf within 9%."),
'v0598': (S, 1, 95, None, False, "Dynasty Garden, Kuchai Lama 20 m; psf and price within 2%."),
'v0602': (S, 0, 80, None, False, "Exact name, Bukit Jambul; psf 1.26, price 1.39."),
'v0606': (S, 0, 90, None, False, "Desa Petaling flat; psf identical."),
'v0610': (S, 0, 88, None, False, "Exact name 38 m, Brickfields; no price; R district blank."),
'v0614': (S, 0, 88, None, False, "Exact name 6 m; Plaza Rah is a different building; R district blank."),
'v0618': (S, 0, 90, None, False, "Taman Sri Sungai Chua, Kajang; psf and price identical."),
'v0622': (S, 1, 95, None, False, "Madge Residences 8 m; psf and price identical."),
'v0626': (P, 0, 90, 'R_in_C', False, "R is Section 2 flats; C k0 is the whole Seksyen 2 Bandar Baru Bangi locality."),
'v0630': (S, 0, 93, None, False, "Cheras Court (Hulu Langat); psf and price within 6%."),
'v0634': (S, 2, 80, None, False, "Pelangi Condominium, Sentul; price identical; R state blank."),
'v0638': (U, 0, 50, None, False, "State blank; common street name; psf 1.53 vs Pudu flat."),
'v0642': (S, 2, 82, None, True, "R in WP KL; web puts Pangsapuri Cemara at Bandar Sri Permaisuri; k2 price matches; k0 Selangor namesake."),
'v0646': (S, 0, 80, None, False, "Taman Pantai Dalam flats in estate row; R district blank."),
'v0650': (S, 0, 93, None, False, "Akasia, Bandar Botanic; psf and price within 5%."),
'v0654': (S, 0, 93, None, False, "Jaya One Residences; psf identical."),
'v0658': (S, 1, 94, None, False, "Astaka Heights 14 m; psf and price within 1%."),
'v0662': (S, 1, 85, None, False, "Taman Impian Indah flat, Bukit Mertajam; no price; R district blank."),
'v0666': (S, 3, 90, None, False, "Forest Green, Bandar Sungai Long; psf and price within 3%."),
'v0670': (S, 0, 70, None, False, "Taman Prima Pelangi estate; R condo vs C terrace row."),
'v0674': (S, 2, 90, None, False, "Kinrara Industrial Park condo; psf within 7%."),
'v0678': (S, 0, 88, None, False, "Taman Sri Sentosa, Old Klang Road; low-cost flats; psf and price within 10%."),
'v0682': (S, 0, 82, None, False, "Exact estate name, Gelugor, flats plus low-cost houses; C psf implausible."),
'v0686': (S, 0, 93, None, False, "Astana Putra, Bukit Rahman Putra; psf and price within 3%."),
'v0690': (S, 0, 82, None, False, "Exact estate name, George Town, includes flats; no price."),
'v0694': (S, 0, 82, None, False, "Taman Cemerlang apartment, Jelutong 66 m; duplicate k1 psf 1.35."),
'v0698': (S, 0, 82, None, False, "Taman Serdang Perdana estate includes flats; psf 1.22."),
'v0702': (U, None, 55, None, False, "State blank; many Taman Kasturi; candidates' prices inconsistent."),
'v0706': (S, 0, 84, None, False, "Vista Harmoni Residences Sentul (2025) fits R's new-condo price; Cheras Vista Harmoni cheaper."),
'v0710': (U, 3, 55, None, True, "Web: R is Taman Juru Indah flats, 603 sq ft; which candidate row holds them unclear."),
'v0714': (S, 1, 95, None, False, "Putra Marine 133 m; psf and price within 4%."),
'v0718': (S, 0, 80, None, False, "Sri Bukit Persekutuan; R condo part of mixed luxury estate."),
'v0722': (S, 0, 92, None, True, "Web: Spectrum Residence is Pangsapuri Alam Permai, 1,200-1,400 sq ft; duplicate k1 price identical."),
'v0726': (S, 0, 82, None, False, "Taman Miharja estate includes flats; price within 1%."),
'v0730': (S, 1, 94, None, False, "Mutiara Villa, Gurney 25 m; psf and price within 4%."),
'v0734': (S, 0, 80, None, False, "Exact estate name 84 m, Hulu Langat, includes flats."),
'v0738': (S, 0, 92, None, False, "Exact distinctive name, Bandar Sunway."),
'v0742': (P, 0, 82, 'R_in_C', False, "Residensi Estetika is inside Mines Resort City; C k0 is the township row."),
'v0746': (S, 1, 88, None, False, "Straits View, Permas Jaya 113 m; R's large units explain price gap."),
'v0750': (S, 0, 80, None, False, "Taman Bukit Pandan estate includes condos; no price."),
'v0754': (S, 0, 92, None, False, "Exact distinctive name 23 m, Jelutong."),
'v0758': (S, 2, 90, None, False, "V-Residensi 5, Selayang; psf 1.16, price 1.08."),
'v0762': (S, 0, 93, None, True, "Web: Subang Central 1 is Pusat Bandar Subang Utama; 2021-22 median RM365k, RM313 psf equals R."),
'v0766': (S, 1, 82, None, False, "Taman Nibong Tebal Jaya flat; exact name; no price."),
'v0770': (S, 3, 92, None, False, "Alias Grandeur Tower; psf and price within 3%."),
'v0774': (S, 1, 80, None, False, "Taman Tenaga, Kajang matches mukim; R point misplaced near KL namesake."),
}

cases = [json.loads(l) for l in open(IN)]
errors = []
out = []
seen = set()
for c in cases:
    cid = c['case']
    if cid not in V:
        errors.append(f'{cid}: missing decision'); continue
    verdict, cand, conf, rel, web, reason = V[cid]
    ks = {x['k'] for x in c['catalogue_candidates']}
    if verdict not in (S, P, D, U): errors.append(f'{cid}: bad verdict')
    if cand is not None and cand not in ks: errors.append(f'{cid}: cand {cand} not in {ks}')
    if verdict in (S, P) and cand is None: errors.append(f'{cid}: {verdict} needs cand')
    if verdict == D and cand is not None: errors.append(f'{cid}: DIFFERENT must have cand null')
    if verdict == P and rel not in ('R_in_C', 'C_in_R'): errors.append(f'{cid}: PART_OF needs relation')
    if verdict != P and rel is not None: errors.append(f'{cid}: relation only for PART_OF')
    if not (0 <= conf <= 100): errors.append(f'{cid}: bad conf')
    nw = len(reason.split())
    if nw > 20: errors.append(f'{cid}: reason {nw} words')
    seen.add(cid)
    out.append({"case": cid, "verdict": verdict, "cand": cand, "confidence_pct": conf,
                "relation": rel, "reason": reason, "web": web})
extra = set(V) - seen
if extra: errors.append(f'decisions for unknown cases: {sorted(extra)}')
if len(out) != len(cases): errors.append(f'count {len(out)} != {len(cases)}')
if errors:
    print('ERRORS:'); print('\n'.join(errors)); sys.exit(1)
if '--write' in sys.argv:
    with open(OUT, 'w') as f:
        for o in out:
            f.write(json.dumps(o, ensure_ascii=False) + '\n')
    print('written', len(out))
from collections import Counter
print('verdicts', dict(Counter(o['verdict'] for o in out)))
print('conf>=90', sum(o['confidence_pct'] >= 90 for o in out))
print('web cases', sum(o['web'] for o in out))
print('PART_OF relations', dict(Counter(o['relation'] for o in out if o['verdict'] == 'PART_OF')))
