            valid=[];bad=[]
            for row in output['stores']:
                lng,lat=row.get('longitude'),row.get('latitude')
                if not isinstance(lng,(int,float)) or not isinstance(lat,(int,float)) or not(-180<=lng<=180 and -90<=lat<=90):bad.append({'id':row['id'],'reason':'missing-or-range'})
                else:valid.append({'type':'Feature','properties':{'id':row['id'],'name':row['name']},'geometry':{'type':'Point','coordinates':[lng,lat]}})
            geo={'type':'FeatureCollection','features':valid};jout('stores.geojson',geo);cout('coordinate-review.csv',bad)
            r=json.loads((folder/'stores.geojson').read_text());check('GeoJSON坐标顺序为经度、纬度',r['features'][0]['geometry']['coordinates']==[120.15,30.28]);check('倒置与缺坐标记录未进入地图',sorted(x['id'] for x in bad)==['S3','S4'])
            points=''.join(f'<circle cx="{80+(f["geometry"]["coordinates"][0]-110)*25}" cy="{500-(f["geometry"]["coordinates"][1]-20)*20}" r="8"/><text x="{90+(f["geometry"]["coordinates"][0]-110)*25}" y="{500-(f["geometry"]["coordinates"][1]-20)*20}">{html.escape(f["properties"]["name"])}</text>' for f in valid)
            (folder/'map.html').write_text('<!doctype html><meta charset="utf-8"><title>GeoJSON回读预览</title><h1>虚构门店经纬度定位（无底图）</h1><p>真实GeoJSON回读；缺坐标与越界项不绘制，不自动纠正。</p><svg viewBox="0 0 700 600" style="width:900px;fill:#563875;background:#f7f3ec">'+points+'</svg>');artifacts.append('map.html');result={'accepted':[f['properties']['id'] for f in valid],'review':bad,'geometry':geo}
