Cai
2026-08-24 156ea25b402479f0abc54c558bbf87f9eaaa0422
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
import fs from 'node:fs';
import path from 'node:path';
import crypto from 'node:crypto';
 
const ROOT = process.cwd();
const CASE_ROOT = path.join(ROOT, 'ana-data', 'cases', '机器人案例', 'ANA-ROBOT-INDUSTRY-001');
const AS_OF = '2026-08-06';
const BOUNDARY = 'OPEN_RETAINED_GAP_PARTIALLY_NARROWED';
 
const PATHS = {
  v3: path.join(CASE_ROOT, 'outputs', '数据表', 'robot_company_information_v03_20260805.csv'),
  gapRegister: path.join(CASE_ROOT, 'evidence', 'next_robot_075_high_value_gap_register_20260805.csv'),
  targetPriority: path.join(CASE_ROOT, 'evidence', 'next_robot_076_p0_target_priority_20260806.csv'),
  sourceBacktrace: path.join(CASE_ROOT, 'evidence', 'next_robot_076_primary_source_backtrace_20260806.csv'),
  gapEffect: path.join(CASE_ROOT, 'evidence', 'next_robot_076_gap_effect_register_20260806.csv'),
  v4: path.join(CASE_ROOT, 'outputs', '数据表', 'robot_company_information_v04_20260806.csv'),
  summary: path.join(CASE_ROOT, 'outputs', '核心文档', '机器人企业高价值缺口集中回源_第二轮_20260806.md'),
  validation: path.join(CASE_ROOT, 'manifest', 'next_robot_076_high_value_gap_content_sprint_validation_20260806.csv')
};
 
function readText(filePath) {
  return fs.readFileSync(filePath, 'utf8').replace(/^\uFEFF/, '');
}
 
function parseCsv(text) {
  const records = [];
  let record = [];
  let value = '';
  let quoted = false;
  for (let index = 0; index < text.length; index += 1) {
    const char = text[index];
    if (quoted) {
      if (char === '"') {
        if (text[index + 1] === '"') {
          value += '"';
          index += 1;
        } else quoted = false;
      } else value += char;
    } else if (char === '"') quoted = true;
    else if (char === ',') {
      record.push(value);
      value = '';
    } else if (char === '\n') {
      record.push(value.replace(/\r$/, ''));
      records.push(record);
      record = [];
      value = '';
    } else value += char;
  }
  if (value.length || record.length) {
    record.push(value.replace(/\r$/, ''));
    records.push(record);
  }
  if (!records.length) return [];
  const columns = records[0];
  return records.slice(1).filter((row) => row.some(Boolean)).map((row) =>
    Object.fromEntries(columns.map((column, index) => [column, row[index] || '']))
  );
}
 
function readCsv(filePath) {
  return parseCsv(readText(filePath));
}
 
function csvCell(value) {
  return `"${String(value ?? '').replace(/"/g, '""')}"`;
}
 
function writeCsv(filePath, rows, columns) {
  fs.mkdirSync(path.dirname(filePath), { recursive: true });
  const lines = [columns.map(csvCell).join(',')];
  for (const row of rows) lines.push(columns.map((column) => csvCell(row[column])).join(','));
  fs.writeFileSync(filePath, `${lines.join('\n')}\n`, 'utf8');
}
 
function writeText(filePath, text) {
  fs.mkdirSync(path.dirname(filePath), { recursive: true });
  fs.writeFileSync(filePath, text.replace(/\r\n/g, '\n').replace(/\r/g, '\n'), 'utf8');
}
 
function shaFile(filePath) {
  return crypto.createHash('sha256').update(fs.readFileSync(filePath)).digest('hex');
}
 
function normalize(value) {
  return String(value || '').replace(/\s+/g, ' ').trim();
}
 
function unique(values) {
  return [...new Set(values.filter(Boolean))];
}
 
function md(value) {
  return normalize(value).replace(/\|/g, '/');
}
 
const companies = readCsv(PATHS.v3);
const gaps = readCsv(PATHS.gapRegister);
const companyByName = new Map(companies.map((row) => [row.canonical_name, row]));
const companyById = new Map(companies.map((row) => [row.company_id, row]));
const v3IdentityBefore = { bytes: fs.statSync(PATHS.v3).size, sha256: shaFile(PATHS.v3) };
 
function companyId(name) {
  const row = companyByName.get(name);
  if (!row) throw new Error(`UNRESOLVED_COMPANY:${name}`);
  return row.company_id;
}
 
const targetNames = [
  '广和通', '速腾聚创', '1X Technologies', 'Agility Robotics', 'Apptronik',
  'Omron', 'Boston Dynamics', 'Tesla Optimus', 'Figure AI'
];
 
const sourceBacktrace = [
  {
    source_update_id: 'N076-SRC-001', canonical_name: '1X Technologies', target_field: 'customer_and_commercial_relationship', gap_types_addressed: 'ORDER|ASP',
    source_class: 'COMPANY_OFFICIAL_ORDER_PAGE', publisher: '1X Technologies', source_title: 'Order NEO', publication_date: 'CURRENT_AS_OF_2026-08-06', canonical_url: 'https://www.1x.tech/order', source_locator: 'Order page: ownership/subscription/deposit/delivery terms',
    content_update_text: 'NEO 官方销售页列示买断价2万美元、订阅价499美元/月、可退订金200美元,并称美国首批交付从2026年开始。',
    limitation: '预订不等于公司已接受的最终订单;页面未披露有效预订量、已交付量、退款率或收入确认。'
  },
  {
    source_update_id: 'N076-SRC-002', canonical_name: '1X Technologies', target_field: 'capacity_delivery_and_procurement', gap_types_addressed: 'MASS_PRODUCTION|CAPACITY|ORDER',
    source_class: 'COMPANY_OFFICIAL_FACTORY_UPDATE', publisher: '1X Technologies', source_title: 'NEO Factory update', publication_date: '2026-04-30', canonical_url: 'https://www.1x.tech/ja_jp/discover/neo-factory', source_locator: 'Factory update: Hayward footprint, workforce, capacity and bookings',
    content_update_text: '公司称Hayward工厂面积5.8万平方英尺、员工超过200人并已开始全面生产;Hayward与San Carlos合计年产能最高1万台,首年1万台产能在5天内被预订,目标到2027年底超过10万台/年。',
    limitation: '首年产能被预订不等于1万份最终订单或已交付;10万台/年为规划能力,不是当前产量。'
  },
  {
    source_update_id: 'N076-SRC-003', canonical_name: 'Agility Robotics', target_field: 'customer_and_commercial_relationship', gap_types_addressed: 'CUSTOMER|ORDER|MASS_PRODUCTION',
    source_class: 'COMPANY_OFFICIAL_COMMERCIAL_AGREEMENT', publisher: 'Agility Robotics', source_title: 'GXO signs industry-first multi-year agreement with Agility Robotics', publication_date: '2024-06-27', canonical_url: 'https://www.agilityrobotics.com/content/gxo-signs-industry-first-multi-year-agreement-with-agility-robotics', source_locator: 'Release: multi-year RaaS agreement and live SPANX operation',
    content_update_text: 'Agility与GXO签署多年RaaS协议,Digit在GXO为SPANX运营的仓库中进入实际物流流程并与Arc系统协同,公司称该部署开始产生服务收入。',
    limitation: '协议未披露机器人台数、合同总额、单台月费、毛利或续约条件;供应商的“产生收入”表述不替代财务报表。'
  },
  {
    source_update_id: 'N076-SRC-004', canonical_name: 'Agility Robotics', target_field: 'capacity_delivery_and_procurement', gap_types_addressed: 'MASS_PRODUCTION|CAPACITY',
    source_class: 'COMPANY_OFFICIAL_FACTORY_UPDATE', publisher: 'Agility Robotics', source_title: 'Announcing RoboFab', publication_date: '2023-09-20', canonical_url: 'https://www.agilityrobotics.com/videos/announcing-robofab-worlds-first-factory-for-humanoid-robots', source_locator: 'RoboFab announcement: footprint and designed output',
    content_update_text: 'RoboFab占地约7万平方英尺,公司披露首年计划生产数百台Digit,工厂具备年产超过1万台的设计能力。',
    limitation: '设计能力和首年计划不等于实际产量、交付量或产能利用率。'
  },
  {
    source_update_id: 'N076-SRC-005', canonical_name: 'Apptronik', target_field: 'customer_and_commercial_relationship', gap_types_addressed: 'CUSTOMER|ORDER|MASS_PRODUCTION',
    source_class: 'COMPANY_OFFICIAL_COMMERCIAL_AGREEMENT', publisher: 'Apptronik', source_title: 'Apptronik and Mercedes-Benz enter commercial agreement', publication_date: '2024-03-15', canonical_url: 'https://apptronik.com/news-collection/apptronik-and-mercedes-benz-enter-commercial-agreement', source_locator: 'Release: commercial agreement and manufacturing/logistics pilot',
    content_update_text: 'Apptronik与Mercedes-Benz达成商业协议,并将Apollo用于制造和物流场景试点;公司将其称为首个公开商业部署协议。',
    limitation: '未披露部署台数、采购义务、合同金额、任务良率或由试点转量产的条件。'
  },
  {
    source_update_id: 'N076-SRC-006', canonical_name: 'Apptronik', target_field: 'capacity_delivery_and_procurement', gap_types_addressed: 'MASS_PRODUCTION|CAPACITY',
    source_class: 'COMPANY_OFFICIAL_MANUFACTURING_PARTNERSHIP', publisher: 'Apptronik', source_title: 'Apptronik and Jabil collaborate to scale production', publication_date: '2025-02-25', canonical_url: 'https://apptronik.com/news-collection/apptronik-and-jabil-collaborate-to-scale-production', source_locator: 'Release: global manufacturing partner and factory pilot',
    content_update_text: 'Jabil成为Apptronik的全球制造合作伙伴,将生产Apollo,并在自身工厂试用Apollo执行制造任务。',
    limitation: '合作公告未给出产能、已生产台数、交付节奏、良率或采购金额。'
  },
  {
    source_update_id: 'N076-SRC-007', canonical_name: 'Apptronik', target_field: 'revenue_and_operational_metric', gap_types_addressed: 'REVENUE_PROFIT',
    source_class: 'COMPANY_OFFICIAL_FINANCING_UPDATE', publisher: 'Apptronik', source_title: 'Apptronik closes over $935 million Series A', publication_date: '2026-02-11', canonical_url: 'https://apptronik.com/news-collection/apptronik-closes-over-935-million-series-a', source_locator: 'Release: financing totals and stated use',
    content_update_text: 'Apptronik披露Series A累计融资超过9.35亿美元、公司累计融资接近10亿美元,资金用于扩大生产和商业部署。',
    limitation: '融资金额不是营业收入、订单、利润或客户付款,不能用于推断商业化兑现。'
  },
  {
    source_update_id: 'N076-SRC-008', canonical_name: 'Figure AI', target_field: 'customer_and_commercial_relationship', gap_types_addressed: 'CUSTOMER|MASS_PRODUCTION',
    source_class: 'COMPANY_OFFICIAL_DEPLOYMENT_REPORT', publisher: 'Figure AI', source_title: 'Production at BMW', publication_date: '2025-11-19', canonical_url: 'https://www.figure.ai/news/production-at-bmw', source_locator: 'BMW deployment metrics: shifts, parts, hours and vehicles',
    content_update_text: 'Figure披露Figure 02在BMW产线历时11个月部署,累计运行超过1250小时、处理超过9万件零件,并参与超过3万辆BMW X3的生产流程。',
    limitation: '供应商披露的运营数据需继续以BMW客户侧材料交叉验证;未披露合同金额、部署台数、收入或供应链。'
  },
  {
    source_update_id: 'N076-SRC-009', canonical_name: 'Figure AI', target_field: 'capacity_delivery_and_procurement', gap_types_addressed: 'MASS_PRODUCTION|CAPACITY',
    source_class: 'COMPANY_OFFICIAL_PRODUCTION_UPDATE', publisher: 'Figure AI', source_title: 'Ramping Figure 03 production', publication_date: '2026-04-29', canonical_url: 'https://www.figure.ai/news/ramping-figure-03-production', source_locator: 'Production update: units, rate, first-pass yield, batteries and actuators',
    content_update_text: '公司披露已生产超过350台Figure 03,生产节拍由每天1台提升到每小时1台,整机一次通过率超过80%,电池线一次通过率99.3%,累计生产超过500块电池和9000个执行器。',
    limitation: '生产节拍不等于持续满负荷年化产量;机器人分配到研发、数据和商业开发,不能全部视为客户交付。'
  },
  {
    source_update_id: 'N076-SRC-010', canonical_name: 'Figure AI', target_field: 'capacity_delivery_and_procurement', gap_types_addressed: 'CAPACITY',
    source_class: 'COMPANY_OFFICIAL_PRODUCT_AND_FACTORY_UPDATE', publisher: 'Figure AI', source_title: 'Introducing Figure 03', publication_date: '2025-10-09', canonical_url: 'https://www.figure.ai/news/introducing-figure-03?id=Figure03', source_locator: 'BotQ capacity and four-year production goal',
    content_update_text: 'Figure称BotQ首条产线设计年产能最高1.2万台,并提出未来四年累计生产10万台机器人的目标。',
    limitation: '均为设计能力或目标,不是已实现产量、订单或交付。'
  },
  {
    source_update_id: 'N076-SRC-011', canonical_name: 'Boston Dynamics', target_field: 'customer_and_commercial_relationship', gap_types_addressed: 'CUSTOMER|ORDER|MASS_PRODUCTION',
    source_class: 'COMPANY_OFFICIAL_DEPLOYMENT_ANNOUNCEMENT', publisher: 'Boston Dynamics', source_title: 'Boston Dynamics unveils new Atlas robot to revolutionize industry', publication_date: '2026-01-05', canonical_url: 'https://bostondynamics.com/blog/boston-dynamics-unveils-new-atlas-robot-to-revolutionize-industry/', source_locator: 'Announcement: committed 2026 deployments and named fleets',
    content_update_text: 'Boston Dynamics称2026年Atlas部署名额已全部落实,首批机器人将部署到Hyundai RMAC和Google DeepMind,并计划在2027年增加客户。',
    limitation: '“部署名额已落实”未给出台数、合同金额、验收状态或已交付数量。'
  },
  {
    source_update_id: 'N076-SRC-012', canonical_name: 'Boston Dynamics', target_field: 'capacity_delivery_and_procurement', gap_types_addressed: 'MASS_PRODUCTION|CAPACITY',
    source_class: 'COMPANY_OFFICIAL_PRODUCTION_ANNOUNCEMENT', publisher: 'Boston Dynamics', source_title: 'Boston Dynamics unveils new Atlas robot to revolutionize industry', publication_date: '2026-01-05', canonical_url: 'https://bostondynamics.com/blog/boston-dynamics-unveils-new-atlas-robot-to-revolutionize-industry/', source_locator: 'Announcement: production start and Hyundai factory plan',
    content_update_text: '公司宣布立即启动新Atlas生产;同时披露Hyundai筹备的新机器人设施规划年产3万台,并提出未来在集团设施内部署数万台机器人的计划。',
    limitation: '3万台/年和数万台部署属于Hyundai规划,不是Boston Dynamics当前产量或已经签收的Atlas订单。'
  },
  {
    source_update_id: 'N076-SRC-013', canonical_name: 'Tesla Optimus', target_field: 'capacity_delivery_and_procurement', gap_types_addressed: 'MASS_PRODUCTION|CUSTOMER',
    source_class: 'REGULATOR_FILING', publisher: 'Tesla, Inc. / SEC', source_title: 'Q1 2026 Update', publication_date: '2026-04-22', canonical_url: 'https://ir.tesla.com/_flysystem/s3/sec/000162828026026551/tsla-20260422-gen.pdf', source_locator: 'Q1 2026 update: Optimus production lines',
    content_update_text: 'Tesla在Q1 2026更新中称首代Optimus生产线正在安装,目的是为后续规模生产做准备。',
    limitation: '“生产线安装中”说明仍处建设/爬坡准备阶段,不等于已经量产;该文件未披露外部客户、订单、交付量或机器人收入。'
  },
  {
    source_update_id: 'N076-SRC-014', canonical_name: '速腾聚创', target_field: 'revenue_and_operational_metric', gap_types_addressed: 'REVENUE_PROFIT|MASS_PRODUCTION',
    source_class: 'HKEX_RESULTS_ANNOUNCEMENT', publisher: 'RoboSense / HKEX', source_title: 'Fourth quarter and full year 2025 results', publication_date: '2026-03-25', canonical_url: 'https://www1.hkexnews.hk/listedco/listconews/sehk/2026/0325/2026032501103_c.pdf', source_locator: 'Q4 2025 robotics revenue, volume, margin and profit tables',
    content_update_text: '公司披露2025年第四季度总收入7.507亿元、毛利率28.5%、经营利润1.301亿元、净利润1.037亿元;其中机器人业务收入3.467亿元,机器人及其他激光雷达销量22.12万台。',
    limitation: '季度机器人业务口径包含多类机器人场景,不等于人形机器人专属收入、ASP或利润;全年与单季不可混用。'
  },
  {
    source_update_id: 'N076-SRC-015', canonical_name: '速腾聚创', target_field: 'customer_and_commercial_relationship', gap_types_addressed: 'CUSTOMER|MASS_PRODUCTION',
    source_class: 'COMPANY_OFFICIAL_BUSINESS_UPDATE', publisher: 'RoboSense', source_title: '2025 annual results business update', publication_date: '2026-03-25', canonical_url: 'https://www.robosense.ai/en/news-show-1976', source_locator: 'Robotics customer and shipment summary',
    content_update_text: '公司称2025年机器人激光雷达出货约30.3万台,并已服务超过3400家机器人行业客户,第四季度机器人业务收入约3.47亿元。',
    limitation: '客户数量和市场排名为公司披露口径;未拆分人形机器人客户装机、单客户收入、ASP、主供份额或合同。'
  },
  {
    source_update_id: 'N076-SRC-016', canonical_name: '广和通', target_field: 'customer_and_commercial_relationship', gap_types_addressed: 'CUSTOMER|ORDER|MASS_PRODUCTION',
    source_class: 'REGULATOR_ANNUAL_REPORT', publisher: '广和通 / 巨潮资讯', source_title: '2024年年度报告', publication_date: '2025-04-19', canonical_url: 'https://static.cninfo.com.cn/finalpage/2025-04-19/1223152460.PDF', source_locator: 'Annual report: robot product line milestones',
    content_update_text: '广和通年报披露机器人产品线成立于2023年,2024年12月已交付首批客户订单;具身智能方案于2024年3月向一家全球知名具身机器人公司送样。',
    limitation: '客户未具名,未披露订单数量、金额、收入确认、复购或Fibot单独商业化数据。'
  },
  {
    source_update_id: 'N076-SRC-017', canonical_name: '广和通', target_field: 'capacity_delivery_and_procurement', gap_types_addressed: 'MASS_PRODUCTION|CAPACITY',
    source_class: 'COMPANY_OFFICIAL_PRODUCT_UPDATE', publisher: '广和通', source_title: '无边界AI割草机解决方案实现规模商用量产', publication_date: '2025-03-06', canonical_url: 'https://www.fibocom.com/newscenter/info_itemid_8604.html', source_locator: 'Release: mass commercial production for European market',
    content_update_text: '广和通披露面向永强集团子公司的无边界AI割草机方案已实现面向欧洲市场的规模商用量产,方案集成主控板、算法板和驱动板。',
    limitation: '这是割草机器人方案,不等于人形机器人或Fibot量产;未披露产能、出货量、ASP、毛利或客户订单金额。'
  },
  {
    source_update_id: 'N076-SRC-018', canonical_name: 'Omron', target_field: 'revenue_and_operational_metric', gap_types_addressed: 'REVENUE_PROFIT|CUSTOMER',
    source_class: 'COMPANY_INTEGRATED_REPORT', publisher: 'OMRON Corporation', source_title: 'Integrated Report 2025 - Industrial Automation Business', publication_date: '2025-09-01', canonical_url: 'https://www.omron.com/global/en/integrated_report/strategybusiness/iab/', source_locator: 'IAB performance and Output & Robotics product mix',
    content_update_text: 'Omron披露FY2024工业自动化业务净销售额3608亿日元、营业利润363亿日元、营业利润率10.1%,创新自动化客户4290家;Output & Robotics占该业务产品销售结构13%。',
    limitation: '工业自动化业务和Output & Robotics均为宽口径,后者还包含安全设备;不能据此拆出AMR/协作机器人收入、销量、客户或ASP。'
  }
].map((row) => ({
  ...row,
  company_id: companyId(row.canonical_name),
  source_fact_status: 'PRIMARY_SOURCE_CONTENT_CAPTURED_PENDING_VERIFICATION_NO_UPGRADE',
  gap_effect: BOUNDARY,
  claim_strength_after: 'PENDING_VERIFICATION_MAX_NO_UPGRADE',
  formal_pool_effect: 'NO_AUTOMATIC_FORMAL_POOL_CHANGE',
  evidence_strength_effect: 'NO_UPGRADE',
  captured_at: `${AS_OF}T15:30:00+08:00`
}));
 
const updatesByCompany = new Map();
for (const update of sourceBacktrace) {
  if (!updatesByCompany.has(update.company_id)) updatesByCompany.set(update.company_id, []);
  updatesByCompany.get(update.company_id).push(update);
}
 
const targetPriority = targetNames.map((name, index) => {
  const company = companyByName.get(name);
  const companyGaps = gaps.filter((row) => row.company_id === company.company_id && row.priority === 'P0');
  const updates = updatesByCompany.get(company.company_id) || [];
  return {
    priority_order: String(index + 1),
    company_id: company.company_id,
    canonical_name: name,
    original_p0_gap_count: String(companyGaps.length),
    original_p0_gap_types: companyGaps.map((row) => row.gap_type).sort().join('|'),
    official_source_update_count: String(updates.length),
    addressed_gap_types: unique(updates.flatMap((row) => row.gap_types_addressed.split('|'))).sort().join('|'),
    selection_reason: companyGaps.length >= 4 ? 'HIGH_YIELD_MULTI_GAP_PRIMARY_SOURCE_AVAILABLE' : 'HIGH_VALUE_CURRENT_PRODUCTION_OR_CUSTOMER_BOUNDARY',
    execution_mode: 'CONTENT_STRENGTHENING_NO_MICRO_REVIEW',
    boundary: 'PENDING_VERIFICATION_MAX_NO_UPGRADE|NO_AUTOMATIC_FORMAL_POOL_CHANGE|NO_UPGRADE'
  };
});
 
const gapEffect = [];
for (const target of targetPriority) {
  const companyUpdates = updatesByCompany.get(target.company_id) || [];
  const companyGaps = gaps.filter((row) => row.company_id === target.company_id && row.priority === 'P0');
  for (const gap of companyGaps) {
    const matched = companyUpdates.filter((row) => row.gap_types_addressed.split('|').includes(gap.gap_type));
    gapEffect.push({
      gap_effect_id: `N076-GFX-${String(gapEffect.length + 1).padStart(3, '0')}`,
      source_gap_id: gap.gap_id,
      company_id: gap.company_id,
      canonical_name: gap.canonical_name,
      gap_type: gap.gap_type,
      source_update_ids: matched.map((row) => row.source_update_id).join('|'),
      evidence_added_count: String(matched.length),
      status_after: matched.length ? 'OPEN_RETAINED_GAP_PARTIALLY_NARROWED' : 'OPEN_RETAINED_GAP_NOT_ADDRESSED_THIS_SPRINT',
      closure_effect: 'NO_GAP_CLOSURE',
      claim_strength_after: 'PENDING_VERIFICATION_MAX_NO_UPGRADE',
      formal_pool_effect: 'NO_AUTOMATIC_FORMAL_POOL_CHANGE',
      evidence_strength_effect: 'NO_UPGRADE'
    });
  }
}
 
const v4 = companies.map((row) => {
  const next = { ...row };
  const updates = updatesByCompany.get(row.company_id) || [];
  for (const field of ['product_and_technical_capability', 'customer_and_commercial_relationship', 'revenue_and_operational_metric', 'capacity_delivery_and_procurement', 'market_position_and_chain_role', 'high_value_gap_and_risk']) {
    const additions = updates.filter((update) => update.target_field === field);
    if (!additions.length) continue;
    const appended = additions.map((update) => `〔${update.source_update_id}〕${update.content_update_text}(边界:${update.limitation})`).join(';');
    next[field] = [normalize(row[field]), `【N076主源回补】${appended}`].filter(Boolean).join(';');
  }
  const target = targetPriority.find((item) => item.company_id === row.company_id);
  next.n076_target_status = target ? 'P0_HIGH_YIELD_TARGET_COMPLETED' : 'NOT_IN_N076_TARGET_BATCH';
  next.n076_original_p0_gap_count = target?.original_p0_gap_count || '0';
  next.n076_addressed_gap_types = target?.addressed_gap_types || '';
  next.n076_primary_source_update_ids = updates.map((update) => update.source_update_id).join('|');
  next.n076_content_update_status = updates.length ? 'PRIMARY_SOURCE_CONTENT_ENRICHED_GAPS_REMAIN_OPEN_NO_UPGRADE' : 'NO_N076_PRIMARY_SOURCE_UPDATE';
  next.content_update_batch = updates.length ? unique(String(row.content_update_batch || '').split('|').concat('NEXT-ROBOT-076')).join('|') : row.content_update_batch;
  next.as_of = AS_OF;
  return next;
});
 
const targetColumns = ['priority_order','company_id','canonical_name','original_p0_gap_count','original_p0_gap_types','official_source_update_count','addressed_gap_types','selection_reason','execution_mode','boundary'];
const sourceColumns = ['source_update_id','company_id','canonical_name','target_field','gap_types_addressed','source_class','publisher','source_title','publication_date','canonical_url','source_locator','content_update_text','limitation','source_fact_status','gap_effect','claim_strength_after','formal_pool_effect','evidence_strength_effect','captured_at'];
const gapEffectColumns = ['gap_effect_id','source_gap_id','company_id','canonical_name','gap_type','source_update_ids','evidence_added_count','status_after','closure_effect','claim_strength_after','formal_pool_effect','evidence_strength_effect'];
const v4Columns = unique(Object.keys(companies[0]).concat(['n076_target_status','n076_original_p0_gap_count','n076_addressed_gap_types','n076_primary_source_update_ids','n076_content_update_status']));
 
writeCsv(PATHS.targetPriority, targetPriority, targetColumns);
writeCsv(PATHS.sourceBacktrace, sourceBacktrace, sourceColumns);
writeCsv(PATHS.gapEffect, gapEffect, gapEffectColumns);
writeCsv(PATHS.v4, v4, v4Columns);
 
const affectedCompanies = new Set(sourceBacktrace.map((row) => row.company_id));
const addressedGapTypes = unique(sourceBacktrace.flatMap((row) => row.gap_types_addressed.split('|'))).sort();
const partiallyNarrowed = gapEffect.filter((row) => row.status_after === 'OPEN_RETAINED_GAP_PARTIALLY_NARROWED').length;
const remainingUnaddressed = gapEffect.length - partiallyNarrowed;
const summary = [
  '# 机器人企业高价值缺口集中回源(第二轮)',
  '',
  `> 更新日期:${AS_OF}`,
  '> 执行方式:内容补充优先;按高收益P0企业批量回源,不新增逐企业审核链。',
  '> 证据边界:所有新增信息均为待验证内容增强,不自动入正式池、不提升证据强度、不宣称缺口关闭。',
  '',
  '## 1. 本轮产出',
  '',
  `- 处理企业:${affectedCompanies.size}家。`,
  `- 新增官方/监管主源字段更新:${sourceBacktrace.length}条。`,
  `- 覆盖原始P0公司×缺口:${gapEffect.length}项,其中${partiallyNarrowed}项获得直接增量证据,${remainingUnaddressed}项继续保留。`,
  `- 涉及缺口类型:${addressedGapTypes.join('、')}。`,
  '',
  '## 2. 企业与增量事实',
  '',
  '| 企业 | 原P0缺口 | 新增主源数 | 本轮补充重点 |',
  '|---|---|---:|---|',
  ...targetPriority.map((row) => `| ${md(row.canonical_name)} | ${md(row.original_p0_gap_types)} | ${row.official_source_update_count} | ${md(row.addressed_gap_types)} |`),
  '',
  '## 3. 主源事实与边界',
  '',
  '| 企业 | 字段 | 新增事实 | 保留边界 |',
  '|---|---|---|---|',
  ...sourceBacktrace.map((row) => `| ${md(row.canonical_name)} | ${row.target_field} | ${md(row.content_update_text)} | ${md(row.limitation)} |`),
  '',
  '## 4. 实用结论',
  '',
  '- 1X、Agility、Apptronik、Figure和Boston Dynamics的客户、协议、试点、生产和设计产能已按“已发生/计划/未披露”分层。',
  '- Tesla Optimus当前可确认的是生产线安装和量产准备,不能写成已规模量产或已有外部客户。',
  '- 速腾聚创和广和通补入了可量化的机器人业务/交付线索,但人形专属收入、ASP、单客户合同和利润仍未被证明。',
  '- Omron补入宽口径IAB经营数据及Output & Robotics占比;继续禁止将该宽口径直接写成机器人单体收入。',
  '- 下一批继续按同样方法处理剩余P0公司,优先选择一份主源可同时补客户、订单、量产或财务多个字段的企业。',
  '',
  '## 5. 数据入口',
  '',
  '- 目标优先级:`evidence/next_robot_076_p0_target_priority_20260806.csv`',
  '- 主源回溯:`evidence/next_robot_076_primary_source_backtrace_20260806.csv`',
  '- 缺口效果表:`evidence/next_robot_076_gap_effect_register_20260806.csv`',
  '- 307家公司统一信息表v04:`outputs/数据表/robot_company_information_v04_20260806.csv`'
];
writeText(PATHS.summary, `${summary.join('\n')}\n`);
 
const validation = [];
function check(id, description, expected, actual) {
  validation.push({ check_id: id, check_description: description, expected: String(expected), actual: String(actual), status: String(expected) === String(actual) ? 'PASS' : 'FAIL' });
}
const immutable = ['company_id','canonical_name','aliases','company_type','region','listed_status','primary_track','detail_track','source_profile_status','formal_output_status','evidence_ceiling','formal_pool_effect'];
let immutableMismatch = 0;
let unauthorizedBaseFieldMismatch = 0;
for (const row of v4) {
  const before = companyById.get(row.company_id);
  for (const column of immutable) if ((before?.[column] || '') !== (row[column] || '')) immutableMismatch += 1;
  const allowedChanged = new Set(['as_of']);
  if (targetNames.includes(row.canonical_name)) {
    for (const column of ['product_and_technical_capability','customer_and_commercial_relationship','revenue_and_operational_metric','capacity_delivery_and_procurement','market_position_and_chain_role','high_value_gap_and_risk','content_update_batch']) allowedChanged.add(column);
  }
  for (const column of Object.keys(companies[0])) {
    if (!allowedChanged.has(column) && (before?.[column] || '') !== (row[column] || '')) unauthorizedBaseFieldMismatch += 1;
  }
}
const v3IdentityAfter = { bytes: fs.statSync(PATHS.v3).size, sha256: shaFile(PATHS.v3) };
const sourceText = readText(PATHS.sourceBacktrace);
 
check('N076-VAL-001', 'company v03 row count', 307, companies.length);
check('N076-VAL-002', 'company v03 id uniqueness', 307, new Set(companies.map((row) => row.company_id)).size);
check('N076-VAL-003', 'P0 target company count', 9, targetPriority.length);
check('N076-VAL-004', 'P0 target id uniqueness', 9, new Set(targetPriority.map((row) => row.company_id)).size);
check('N076-VAL-005', 'primary source update count', 18, sourceBacktrace.length);
check('N076-VAL-006', 'source update id uniqueness', 18, new Set(sourceBacktrace.map((row) => row.source_update_id)).size);
check('N076-VAL-007', 'source company ids unresolved', 0, sourceBacktrace.filter((row) => !companyById.has(row.company_id)).length);
check('N076-VAL-008', 'source rows missing URL/locator/content/boundary', 0, sourceBacktrace.filter((row) => !/^https:\/\//.test(row.canonical_url) || !row.source_locator || !row.content_update_text || !row.limitation).length);
check('N076-VAL-009', 'source rows outside target set', 0, sourceBacktrace.filter((row) => !targetNames.includes(row.canonical_name)).length);
check('N076-VAL-010', 'P0 gap effect rows', 32, gapEffect.length);
check('N076-VAL-011', 'P0 gap effect id uniqueness', 32, new Set(gapEffect.map((row) => row.gap_effect_id)).size);
check('N076-VAL-012', 'P0 gaps with direct increment', 24, partiallyNarrowed);
check('N076-VAL-013', 'P0 gaps retained unaddressed', 8, remainingUnaddressed);
check('N076-VAL-014', 'company v04 row count', 307, v4.length);
check('N076-VAL-015', 'company v04 id uniqueness', 307, new Set(v4.map((row) => row.company_id)).size);
check('N076-VAL-016', 'immutable company field mismatch', 0, immutableMismatch);
check('N076-VAL-017', 'v04 enriched company count', 9, v4.filter((row) => row.n076_content_update_status === 'PRIMARY_SOURCE_CONTENT_ENRICHED_GAPS_REMAIN_OPEN_NO_UPGRADE').length);
check('N076-VAL-018', 'source update ids missing from v04', 0, sourceBacktrace.filter((update) => !v4.find((row) => row.company_id === update.company_id)?.n076_primary_source_update_ids.includes(update.source_update_id)).length);
check('N076-VAL-019', 'formal pool boundary drift', 0, v4.filter((row) => row.formal_pool_effect !== 'NO_AUTOMATIC_FORMAL_POOL_CHANGE').length);
check('N076-VAL-020', 'source backtrace forbidden upgrade literal count', 0, (sourceText.match(/FORMAL_POOL_APPROVED|EVIDENCE_UPGRADED|GAP_CLOSED/g) || []).length);
check('N076-VAL-021', 'v03 bytes unchanged', v3IdentityBefore.bytes, v3IdentityAfter.bytes);
check('N076-VAL-022', 'v03 sha256 unchanged', v3IdentityBefore.sha256, v3IdentityAfter.sha256);
check('N076-VAL-023', 'target original P0 gaps nonzero', 0, targetPriority.filter((row) => Number(row.original_p0_gap_count) < 1).length);
check('N076-VAL-024', 'gap closure count', 0, gapEffect.filter((row) => row.closure_effect !== 'NO_GAP_CLOSURE').length);
check('N076-VAL-025', 'evidence strength upgrade count', 0, sourceBacktrace.filter((row) => row.evidence_strength_effect !== 'NO_UPGRADE').length);
check('N076-VAL-026', 'automatic formal-pool change count', 0, sourceBacktrace.filter((row) => row.formal_pool_effect !== 'NO_AUTOMATIC_FORMAL_POOL_CHANGE').length);
check('N076-VAL-027', 'unauthorized inherited base-field mismatch', 0, unauthorizedBaseFieldMismatch);
check('N076-VAL-028', 'source update tags missing from v04 content fields', 0, sourceBacktrace.filter((update) => !Object.values(v4.find((row) => row.company_id === update.company_id) || {}).some((value) => String(value).includes(`〔${update.source_update_id}〕`))).length);
 
writeCsv(PATHS.validation, validation, ['check_id','check_description','expected','actual','status']);
const failed = validation.filter((row) => row.status === 'FAIL');
console.log(JSON.stringify({
  status: failed.length ? 'FAIL' : 'PASS',
  targetCompanies: targetPriority.length,
  primarySourceUpdates: sourceBacktrace.length,
  p0GapEffects: gapEffect.length,
  partiallyNarrowed,
  remainingUnaddressed,
  v4Companies: v4.length,
  validationPass: validation.filter((row) => row.status === 'PASS').length,
  validationFail: failed.length,
  outputs: Object.values(PATHS).filter((value) => ![PATHS.v3, PATHS.gapRegister].includes(value))
}, null, 2));
if (failed.length) process.exitCode = 1;