@thinkingai/ae-cli 6.1.24 → 6.1.25

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (135) hide show
  1. package/README.md +7 -1
  2. package/README.zh.md +7 -1
  3. package/dist/{auth-XZNXFRJS.js → auth-LHM7NQPR.js} +167 -7
  4. package/dist/{capability-FYTU3X6L.js → capability-SJI5KOCK.js} +11 -6
  5. package/dist/{chunk-CCS66K4K.js → chunk-2FJM4HRV.js} +3 -3
  6. package/dist/{chunk-57RRFUEZ.js → chunk-5XRQ3CZR.js} +4 -4
  7. package/dist/{chunk-EQ255HKO.js → chunk-7WZACOAI.js} +2 -2
  8. package/dist/{chunk-DX5CDO34.js → chunk-BW7JUDEI.js} +84 -19
  9. package/dist/{chunk-7XGFSUOM.js → chunk-DU72X2IO.js} +271 -76
  10. package/dist/{chunk-V2NUHPXF.js → chunk-GK7WDY7E.js} +1 -1
  11. package/dist/{chunk-7FTTULED.js → chunk-IG3DYEIR.js} +4 -1
  12. package/dist/{chunk-HBGADCKA.js → chunk-KJDOTPYU.js} +3 -3
  13. package/dist/{chunk-4NRCTUZJ.js → chunk-KS4DE3MX.js} +4 -4
  14. package/dist/{chunk-JOCDD4ON.js → chunk-N4BLPJC7.js} +1 -1
  15. package/dist/{chunk-HFGZGYCG.js → chunk-QYXDJOLA.js} +252 -6
  16. package/dist/{chunk-DVMLWQD4.js → chunk-SO5LKIWP.js} +2 -2
  17. package/dist/{chunk-VYXXOKYS.js → chunk-XKIW3QMF.js} +7 -4
  18. package/dist/{chunk-MAPZ7VB6.js → chunk-YVGXGTD6.js} +13 -0
  19. package/dist/{community-report-client-CU22W53K.js → community-report-client-6VJDDMJJ.js} +1 -1
  20. package/dist/{config-XO3LI3SA.js → config-XQWOPDHN.js} +25 -6
  21. package/dist/{context-IFJI3LIT.js → context-M3HDGEIR.js} +4 -4
  22. package/dist/{data-integration-HNGUKFIC.js → data-integration-XBTCGXQT.js} +6 -6
  23. package/dist/index.js +78 -41
  24. package/dist/{local-data-upload-client-GTRIT64F.js → local-data-upload-client-3P7NIVQ4.js} +1 -1
  25. package/dist/{memory-QGPSNCQS.js → memory-6APEB2JB.js} +4 -4
  26. package/dist/{metadata-DI5BMYFU.js → metadata-JRYKNUS5.js} +7 -7
  27. package/dist/{model-RNPQXIBI.js → model-ZS7HVGKN.js} +4 -4
  28. package/dist/{personal-semantic-preference-5C3WUXFI.js → personal-semantic-preference-IDE2GE5T.js} +8 -6
  29. package/dist/project-semantic-36KOCYN7.js +356 -0
  30. package/dist/{sync-O45NXEQY.js → sync-EQLSTUS7.js} +7 -7
  31. package/dist/{te-agent-K5MSPNHJ.js → te-agent-Z3HCTKR7.js} +348 -55
  32. package/dist/{te-analysis-AISWCOOZ.js → te-analysis-5ZZ6YHVH.js} +940 -144
  33. package/dist/{te-community-4OYU3BDA.js → te-community-Z7JACGF3.js} +3 -3
  34. package/dist/{te-dataops-NFIPB6FX.js → te-dataops-F7GQOIS6.js} +644 -276
  35. package/dist/{te-engage-BAM3GBFX.js → te-engage-RNRMYDO3.js} +6 -6
  36. package/dist/{te-experiment-GUTZXZ5Z.js → te-experiment-BTAC2UNK.js} +6 -6
  37. package/dist/{te-kb-3TS73NO6.js → te-kb-QJLJEX5K.js} +323 -181
  38. package/dist/{te-system-STMHLS5J.js → te-system-IXC43QHZ.js} +6 -6
  39. package/dist/{te-team-3I5AOX3N.js → te-team-BIZIKIHZ.js} +6 -6
  40. package/dist/{update-RZXLPCRH.js → update-W2SGQNKT.js} +4 -4
  41. package/package.json +11 -4
  42. package/skills/ae-agent/SKILL.md +9 -4
  43. package/skills/ae-agent/references/notification.md +40 -0
  44. package/skills/ae-analysis/SKILL.md +43 -20
  45. package/skills/ae-analysis/references/adhoc_export.md +1 -1
  46. package/skills/ae-analysis/references/adhoc_run.md +1 -1
  47. package/skills/ae-analysis/references/agent_review_submit_to_page.md +2 -0
  48. package/skills/ae-analysis/references/ai_models/event.md +6 -1
  49. package/skills/ae-analysis/references/ai_models/funnel.md +7 -1
  50. package/skills/ae-analysis/references/ai_models/heat_map.md +23 -0
  51. package/skills/ae-analysis/references/ai_models/interval.md +24 -0
  52. package/skills/ae-analysis/references/ai_models/path.md +20 -0
  53. package/skills/ae-analysis/references/ai_models/prop_analysis.md +2 -0
  54. package/skills/ae-analysis/references/ai_models/rank_list.md +30 -0
  55. package/skills/ae-analysis/references/ai_models/retention.md +45 -1
  56. package/skills/ae-analysis/references/ai_models/revenue.md +20 -2
  57. package/skills/ae-analysis/references/ai_models/session.md +76 -0
  58. package/skills/ae-analysis/references/ai_models/tag.md +2 -0
  59. package/skills/ae-analysis/references/ai_models.md +73 -3
  60. package/skills/ae-analysis/references/analysis_data_retrieval.md +4 -0
  61. package/skills/ae-analysis/references/analysis_drilldown_contract.md +4 -1
  62. package/skills/ae-analysis/references/asset_batch_info_export.md +10 -0
  63. package/skills/ae-analysis/references/asset_batch_sql_export.md +13 -1
  64. package/skills/ae-analysis/references/asset_export.md +12 -1
  65. package/skills/ae-analysis/references/asset_list.md +1 -0
  66. package/skills/ae-analysis/references/asset_search.md +11 -5
  67. package/skills/ae-analysis/references/bi_panel_list.md +1 -1
  68. package/skills/ae-analysis/references/catalog_list.md +1 -1
  69. package/skills/ae-analysis/references/collaboration.md +48 -0
  70. package/skills/ae-analysis/references/command_index.md +19 -17
  71. package/skills/ae-analysis/references/dashboard_list.md +4 -4
  72. package/skills/ae-analysis/references/dashboard_report_data_export.md +2 -0
  73. package/skills/ae-analysis/references/drilldown_session_details_run.md +41 -0
  74. package/skills/ae-analysis/references/event_export.md +3 -1
  75. package/skills/ae-analysis/references/governance_recommendation_auto_review.md +77 -0
  76. package/skills/ae-analysis/references/governance_recommendation_export.md +11 -0
  77. package/skills/ae-analysis/references/metadata_resolution.md +3 -3
  78. package/skills/ae-analysis/references/metric_export.md +3 -1
  79. package/skills/ae-analysis/references/metric_list.md +1 -1
  80. package/skills/ae-analysis/references/operation_record_export.md +10 -0
  81. package/skills/ae-analysis/references/personal_semantic_preference_add.md +2 -2
  82. package/skills/ae-analysis/references/personal_semantic_preference_get.md +4 -4
  83. package/skills/ae-analysis/references/personal_semantic_preference_list.md +4 -4
  84. package/skills/ae-analysis/references/project_semantic_knowledge_wiki.md +29 -11
  85. package/skills/ae-analysis/references/project_semantic_knowledge_wiki_plan_schema.md +59 -0
  86. package/skills/ae-analysis/references/property_export.md +3 -1
  87. package/skills/ae-analysis/references/report_create.md +5 -1
  88. package/skills/ae-analysis/references/report_data_export.md +6 -0
  89. package/skills/ae-analysis/references/report_get.md +2 -0
  90. package/skills/ae-analysis/references/report_list.md +5 -5
  91. package/skills/ae-analysis/references/report_update.md +7 -1
  92. package/skills/ae-analysis/references/sql_table_columns.md +4 -4
  93. package/skills/ae-analysis/references/sql_table_list.md +5 -5
  94. package/skills/ae-analysis/references/user_cluster_models.md +8 -0
  95. package/skills/ae-analysis/references/user_tag_create.md +7 -1
  96. package/skills/ae-analysis/references/user_tag_get.md +1 -1
  97. package/skills/ae-analysis/references/user_tag_models.md +18 -2
  98. package/skills/ae-analysis/references/user_tag_refresh.md +1 -1
  99. package/skills/ae-analysis/references/user_tag_update.md +2 -2
  100. package/skills/ae-analysis/scripts/project-semantic-knowledge-wiki/build-project-semantic-wiki.mjs +337 -12
  101. package/skills/ae-analysis/scripts/project-semantic-knowledge-wiki/default-compile-rules.md +7 -2
  102. package/skills/ae-analysis/scripts/project-semantic-knowledge-wiki/generate-build-ir.mjs +284 -24
  103. package/skills/ae-analysis/scripts/project-semantic-knowledge-wiki/package-wiki-source-zip.mjs +185 -22
  104. package/skills/ae-analysis/scripts/project-semantic-knowledge-wiki/precompiled-source.mjs +46 -2
  105. package/skills/ae-capability/SKILL.md +63 -1
  106. package/skills/ae-capability/references/collaboration.md +48 -0
  107. package/skills/ae-community/SKILL.md +5 -1
  108. package/skills/ae-community/references/collaboration.md +48 -0
  109. package/skills/ae-data-integration/SKILL.md +4 -0
  110. package/skills/ae-data-integration/references/collaboration.md +48 -0
  111. package/skills/ae-dataops/SKILL.md +54 -74
  112. package/skills/ae-dataops/references/collaboration.md +48 -0
  113. package/skills/ae-dataops/references/dataops-backfill.md +23 -18
  114. package/skills/ae-dataops/references/dataops-flow-create.md +74 -23
  115. package/skills/ae-dataops/references/dataops-flow-monitor.md +45 -13
  116. package/skills/ae-dataops/references/dataops-integration.md +57 -30
  117. package/skills/ae-dataops/references/dataops-query.md +21 -4
  118. package/skills/ae-dataops/references/dataops-table.md +143 -11
  119. package/skills/ae-engage/SKILL.md +21 -6
  120. package/skills/ae-engage/references/channel-mgmt.md +3 -3
  121. package/skills/ae-engage/references/collaboration.md +48 -0
  122. package/skills/ae-engage/references/scene-config-channel.md +4 -4
  123. package/skills/ae-generate-tracking-plan/SKILL.md +4 -0
  124. package/skills/ae-generate-tracking-plan/references/collaboration.md +48 -0
  125. package/skills/ae-kb/SKILL.md +9 -1
  126. package/skills/ae-kb/references/collaboration.md +48 -0
  127. package/skills/ae-kb/references/schema-import.md +30 -0
  128. package/skills/ae-kb-discovery/SKILL.md +7 -2
  129. package/skills/ae-kb-discovery/references/collaboration.md +48 -0
  130. package/skills/ae-metadata/SKILL.md +20 -12
  131. package/skills/ae-metadata/references/collaboration.md +48 -0
  132. package/dist/project-semantic-3LF6Q6JZ.js +0 -1119
  133. package/skills/ae-project-semantic/SKILL.md +0 -193
  134. package/skills/ae-project-semantic/references/query-routing-v5.md +0 -165
  135. package/skills/ae-project-semantic/references/recommendation-quality.md +0 -68
@@ -11,6 +11,7 @@ const outputRoot = path.resolve(requiredArg(args, 'output'));
11
11
  const force = Boolean(args.force);
12
12
  const allowTruncated = Boolean(args['allow-truncated']);
13
13
  const allowAllVisible = Boolean(args['allow-all-visible']);
14
+ const allowSemanticPlanSnapshotDrift = Boolean(args['allow-semantic-plan-snapshot-drift']);
14
15
 
15
16
  assertSafePaths(packageRoot, outputRoot);
16
17
 
@@ -74,6 +75,7 @@ const domainPlan = readObject(domainPlanPath, 'business domain plan');
74
75
  const classifications = validateAndEnrichDomainPlan(domainPlan);
75
76
  const domains = classifications.domains;
76
77
  const appendices = classifications.appendices;
78
+ const projectBusinessModel = classifications.project_business_model;
77
79
  const domainByDashboardId = new Map();
78
80
  for (const domain of domains) {
79
81
  for (const id of domain.dashboard_ids) domainByDashboardId.set(id, domain);
@@ -132,6 +134,7 @@ const corpus = {
132
134
  truncated: Boolean(descriptor.truncated ?? manifest.truncated),
133
135
  business_domain_source: 'agent-authored semantic plan validated against every dashboard in the asset package',
134
136
  business_domain_plan_hash: domainPlanHash,
137
+ project_business_model: projectBusinessModel,
135
138
  counts: {
136
139
  business_domains: domains.length,
137
140
  technical_appendices: appendices.length,
@@ -244,7 +247,7 @@ function validateAndEnrichDomainPlan(plan) {
244
247
  if (!['1.0', '2.0'].includes(text(plan.schema_version))) {
245
248
  fail('Business domain plan schema 1.0 or project semantic Wiki plan schema 2.0 is required.');
246
249
  }
247
- if (text(plan.source_snapshot_hash) !== snapshotHash) {
250
+ if (text(plan.source_snapshot_hash) !== snapshotHash && !allowSemanticPlanSnapshotDrift) {
248
251
  fail(`Business domain plan snapshot mismatch: expected ${snapshotHash}.`);
249
252
  }
250
253
  if (text(plan.generation_method) !== 'agent_semantic_synthesis') {
@@ -296,6 +299,10 @@ function validateAndEnrichDomainPlan(plan) {
296
299
  const dashboardRows = dashboardIds.map((dashboardId) => dashboardById.get(dashboardId));
297
300
  const sourceContainerRefs = uniqueObjects(dashboardRows.map((row) => object(row.source_container_ref)), 'container_key');
298
301
  const reportRefs = uniqueObjects(dashboardRows.flatMap((row) => array(row.report_refs)), 'report_id');
302
+ const reportIds = new Set(reportRefs.map((ref) => text(ref.report_id)).filter(Boolean));
303
+ const metricIds = new Set(reports
304
+ .filter((report) => reportIds.has(text(report.resource_key)))
305
+ .flatMap((report) => array(report.metric_occurrences).map((item) => text(item.metric_key)).filter(Boolean)));
299
306
  return {
300
307
  domain_id: id,
301
308
  title,
@@ -303,6 +310,8 @@ function validateAndEnrichDomainPlan(plan) {
303
310
  primary_questions: questions,
304
311
  primary_metrics: primaryMetrics,
305
312
  drilldown_dimensions: drilldownDimensions,
313
+ domain_judgment_model: normalizeDomainJudgmentModel(domain.domain_judgment_model, id,
314
+ new Set(dashboardIds), reportIds, metricIds),
306
315
  merge_rationale: rationale,
307
316
  dashboard_ids: dashboardIds,
308
317
  dashboard_refs: dashboardRows.map((row) => ({
@@ -356,17 +365,160 @@ function validateAndEnrichDomainPlan(plan) {
356
365
  report_refs: uniqueObjects(dashboardRows.flatMap((row) => array(row.report_refs)), 'report_id'),
357
366
  };
358
367
  });
368
+ const projectBusinessModel = normalizeProjectBusinessModel(plan.project_business_model, domainIds);
359
369
  validateSqlSemanticPlan(plan);
360
370
  const missing = dashboards.map((row) => text(row.resource_key)).filter((id) => !assigned.has(id));
361
371
  if (missing.length) {
362
372
  fail(`Business domain plan does not classify ${missing.length} dashboard(s): ${missing.slice(0, 20).join(', ')}${missing.length > 20 ? ', ...' : ''}.`);
363
373
  }
364
374
  return {
375
+ project_business_model: projectBusinessModel,
365
376
  domains: enrichedDomains.sort((a, b) => a.domain_id.localeCompare(b.domain_id, 'en')),
366
377
  appendices: enrichedAppendices.sort((a, b) => a.appendix_id.localeCompare(b.appendix_id, 'en')),
367
378
  };
368
379
  }
369
380
 
381
+ function normalizeProjectBusinessModel(value, domainIds) {
382
+ const model = object(value);
383
+ const businessPositioning = text(model.business_positioning);
384
+ const audienceRoles = array(model.audience_roles).map(text).filter(Boolean);
385
+ const businessObjects = array(model.business_objects).map((item, index) => {
386
+ const row = object(item);
387
+ const objectId = text(row.object_id);
388
+ const name = text(row.name);
389
+ const meaning = text(row.meaning);
390
+ if (!/^[a-z0-9][a-z0-9_-]{1,79}$/.test(objectId)) fail(`project_business_model.business_objects[${index}] has invalid object_id.`);
391
+ if (!name || !meaning) fail(`project_business_model.business_objects[${index}] requires name and meaning.`);
392
+ const evidenceRefs = normalizePlanAssetRefs(row.evidence_refs, `business object ${objectId}`);
393
+ if (!evidenceRefs.length) fail(`business object ${objectId} requires evidence_refs.`);
394
+ return { object_id: objectId, name, meaning, evidence_refs: evidenceRefs };
395
+ });
396
+ const objectNames = new Set(businessObjects.map((item) => item.name));
397
+ const objectRelationships = array(model.object_relationships).map((item, index) => {
398
+ const row = object(item);
399
+ const fromObject = text(row.from_object);
400
+ const toObject = text(row.to_object);
401
+ const relationship = text(row.relationship);
402
+ if (!fromObject || !toObject || !relationship) {
403
+ fail(`project_business_model.object_relationships[${index}] requires from_object, to_object and relationship.`);
404
+ }
405
+ if (!objectNames.has(fromObject) || !objectNames.has(toObject)) {
406
+ fail(`object relationship ${fromObject || '<empty>'}->${toObject || '<empty>'} must reference declared business object names.`);
407
+ }
408
+ const evidenceRefs = normalizePlanAssetRefs(row.evidence_refs, `object relationship ${fromObject}->${toObject}`);
409
+ if (!evidenceRefs.length) fail(`object relationship ${fromObject}->${toObject} requires evidence_refs.`);
410
+ return { from_object: fromObject, to_object: toObject, relationship, evidence_refs: evidenceRefs };
411
+ });
412
+ const decisionChains = array(model.decision_chains).map((item, index) => {
413
+ const row = object(item);
414
+ const chainId = text(row.chain_id);
415
+ const title = text(row.title);
416
+ const decisionQuestion = text(row.decision_question);
417
+ const signals = array(row.signals).map(text).filter(Boolean);
418
+ const decisionSteps = array(row.decision_steps).map(text).filter(Boolean);
419
+ const preferredDomainIds = array(row.preferred_domain_ids).map(text).filter(Boolean);
420
+ const assetRefs = normalizePlanAssetRefs(row.asset_refs, `decision chain ${chainId || index + 1}`);
421
+ const boundaries = array(row.boundaries).map(text).filter(Boolean);
422
+ if (!/^[a-z0-9][a-z0-9_-]{1,79}$/.test(chainId)) fail(`project_business_model.decision_chains[${index}] has invalid chain_id.`);
423
+ if (!title || !decisionQuestion) fail(`decision chain ${chainId} requires title and decision_question.`);
424
+ if (signals.length < 2) fail(`decision chain ${chainId} requires at least two signals.`);
425
+ if (decisionSteps.length < 2) fail(`decision chain ${chainId} requires at least two decision_steps.`);
426
+ if (!preferredDomainIds.length || preferredDomainIds.some((id) => !domainIds.has(id))) {
427
+ fail(`decision chain ${chainId} must reference existing preferred_domain_ids.`);
428
+ }
429
+ if (!assetRefs.length) fail(`decision chain ${chainId} requires asset_refs.`);
430
+ if (!boundaries.length) fail(`decision chain ${chainId} requires boundaries.`);
431
+ return {
432
+ chain_id: chainId,
433
+ title,
434
+ decision_question: decisionQuestion,
435
+ signals,
436
+ decision_steps: decisionSteps,
437
+ preferred_domain_ids: preferredDomainIds,
438
+ asset_refs: assetRefs,
439
+ requires_live_execution: Boolean(row.requires_live_execution),
440
+ boundaries,
441
+ };
442
+ });
443
+ const nonGoals = array(model.non_goals).map(text).filter(Boolean);
444
+ if (!businessPositioning) fail('project_business_model requires business_positioning.');
445
+ if (!audienceRoles.length) fail('project_business_model requires audience_roles.');
446
+ if (!businessObjects.length) fail('project_business_model requires business_objects.');
447
+ if (!objectRelationships.length) fail('project_business_model requires object_relationships.');
448
+ if (!decisionChains.length) fail('project_business_model requires decision_chains.');
449
+ if (!nonGoals.length) fail('project_business_model requires non_goals.');
450
+ const combined = [
451
+ businessPositioning, ...audienceRoles, ...businessObjects.flatMap((item) => [item.name, item.meaning]),
452
+ ...objectRelationships.map((item) => item.relationship),
453
+ ...decisionChains.flatMap((item) => [item.title, item.decision_question, ...item.signals, ...item.decision_steps, ...item.boundaries]),
454
+ ...nonGoals,
455
+ ].join('\n');
456
+ if (isMechanicalPlanText(combined)) fail('project_business_model looks mechanically generated; Agent must author project-level business semantics from current asset evidence.');
457
+ return {
458
+ business_positioning: businessPositioning,
459
+ audience_roles: audienceRoles,
460
+ business_objects: businessObjects,
461
+ object_relationships: objectRelationships,
462
+ decision_chains: decisionChains,
463
+ non_goals: nonGoals,
464
+ };
465
+ }
466
+
467
+ function normalizeDomainJudgmentModel(value, domainId, domainDashboardIds, domainReportIds, domainMetricIds) {
468
+ const model = object(value);
469
+ const businessStateJudged = text(model.business_state_judged);
470
+ const mainObjects = array(model.main_objects).map(text).filter(Boolean);
471
+ const signals = array(model.signals).map(text).filter(Boolean);
472
+ const decisionPath = array(model.decision_path).map(text).filter(Boolean);
473
+ const boundaries = array(model.boundaries).map(text).filter(Boolean);
474
+ const attachmentLogic = array(model.asset_attachment_logic).map((item, index) => {
475
+ const row = object(item);
476
+ const assetRef = normalizePlanAssetRef(row.asset_ref, `domain ${domainId} asset_attachment_logic[${index}]`);
477
+ const role = text(row.role);
478
+ const reason = text(row.reason);
479
+ if (!['primary_entry', 'supporting_evidence', 'drilldown', 'exclusion'].includes(role)) {
480
+ fail(`domain ${domainId} asset_attachment_logic[${index}] has invalid role.`);
481
+ }
482
+ if (!reason || reason.length < 10) fail(`domain ${domainId} asset_attachment_logic[${index}] requires concrete reason.`);
483
+ const inDomain = assetRef.resource_type === 'dashboard' ? domainDashboardIds.has(assetRef.resource_key)
484
+ : assetRef.resource_type === 'report' ? domainReportIds.has(assetRef.resource_key)
485
+ : assetRef.resource_type === 'metric' ? domainMetricIds.has(assetRef.resource_key) : false;
486
+ if (!inDomain) fail(`domain ${domainId} asset_attachment_logic references ${assetRef.resource_type}:${assetRef.resource_key} outside domain closure.`);
487
+ return { asset_ref: assetRef, role, reason };
488
+ });
489
+ if (!businessStateJudged) fail(`Domain ${domainId} is missing domain_judgment_model.business_state_judged.`);
490
+ if (!mainObjects.length) fail(`Domain ${domainId} is missing domain_judgment_model.main_objects.`);
491
+ if (signals.length < 2) fail(`Domain ${domainId} domain_judgment_model requires at least two signals.`);
492
+ if (decisionPath.length < 2) fail(`Domain ${domainId} domain_judgment_model requires at least two decision_path steps.`);
493
+ if (!attachmentLogic.length) fail(`Domain ${domainId} domain_judgment_model requires asset_attachment_logic.`);
494
+ if (!boundaries.length) fail(`Domain ${domainId} domain_judgment_model requires boundaries.`);
495
+ const combined = [businessStateJudged, ...mainObjects, ...signals, ...decisionPath, ...attachmentLogic.map((item) => item.reason), ...boundaries].join('\n');
496
+ if (isMechanicalPlanText(combined)) fail(`Domain ${domainId} judgment model looks mechanically generated.`);
497
+ return {
498
+ business_state_judged: businessStateJudged,
499
+ main_objects: mainObjects,
500
+ signals,
501
+ decision_path: decisionPath,
502
+ asset_attachment_logic: attachmentLogic,
503
+ boundaries,
504
+ };
505
+ }
506
+
507
+ function normalizePlanAssetRefs(values, label) {
508
+ return array(values).map((value, index) => normalizePlanAssetRef(value, `${label} evidence_refs[${index}]`));
509
+ }
510
+
511
+ function normalizePlanAssetRef(value, label) {
512
+ const ref = object(value);
513
+ const type = text(ref.resource_type);
514
+ const key = text(ref.resource_key);
515
+ const exists = type === 'dashboard' ? dashboardById.has(key)
516
+ : type === 'report' ? reportById.has(key)
517
+ : type === 'metric' ? metrics.some((metric) => text(metric.resource_key) === key) : false;
518
+ if (!exists) fail(`${label} references unknown asset ${type || '<type>'}:${key || '<key>'}.`);
519
+ return { resource_type: type, resource_key: key };
520
+ }
521
+
370
522
 
371
523
  function assertAgentAuthoredDomain(domain, label) {
372
524
  const values = [domain.title, domain.summary, domain.merge_rationale, ...array(domain.primary_questions)].map(text);
@@ -464,7 +616,48 @@ function renderBriefIndex() {
464
616
 
465
617
  function renderDomainSource(domain) {
466
618
  const key = text(domain.domain_id);
467
- return `---\ntype: project-business-domain-source\nproject_id: ${projectId}\ndomain_id: ${yaml(key)}\ntitle: ${yaml(domain.title)}\nclassification_method: agent_semantic_synthesis\nsnapshot_hash: ${yaml(snapshotHash)}\n---\n\n# ${heading(domain.title)}\n\n${text(domain.summary)}\n\n## 主要业务问题\n\n${array(domain.primary_questions).map((question) => `- ${question}`).join('\n')}\n\n## 归类依据\n\n${text(domain.merge_rationale)}\n\n${renderDomainAgentUsageSummary(domain)}\n## 看板(${array(domain.dashboard_refs).length})\n\n${refLinks(array(domain.dashboard_refs), 'dashboard_id', 'dashboard_name', dashboardFiles, '../dashboards')}\n\n## 涉及报表(${array(domain.report_refs).length})\n\n${refLinks(array(domain.report_refs), 'report_id', 'report_name', reportFiles, '../reports')}\n\n## 物理来源容器\n\n${containerLinks(domain.source_container_refs, '../source-containers')}\n\n## 规划依据摘要\n\n- Domain ID: \`${key}\`\n- 归类方法: agent_semantic_synthesis\n- 快照: \`${snapshotHash}\`\n- 纳入看板: ${array(domain.dashboard_refs).length}\n- 涉及报表: ${array(domain.report_refs).length}\n- 物理来源容器: ${array(domain.source_container_refs).length}\n`;
619
+ return `---\ntype: project-business-domain-source\nproject_id: ${projectId}\ndomain_id: ${yaml(key)}\ntitle: ${yaml(domain.title)}\nclassification_method: agent_semantic_synthesis\nsnapshot_hash: ${yaml(snapshotHash)}\n---\n\n# ${heading(domain.title)}\n\n${text(domain.summary)}\n\n## 这个域判断什么\n\n${renderDomainJudgmentModel(domain)}\n\n## 主要业务问题\n\n${array(domain.primary_questions).map((question) => `- ${question}`).join('\n')}\n\n## 归类依据\n\n${text(domain.merge_rationale)}\n\n${renderDomainAgentUsageSummary(domain)}\n## 看板(${array(domain.dashboard_refs).length})\n\n${refLinks(array(domain.dashboard_refs), 'dashboard_id', 'dashboard_name', dashboardFiles, '../dashboards')}\n\n## 涉及报表(${array(domain.report_refs).length})\n\n${refLinks(array(domain.report_refs), 'report_id', 'report_name', reportFiles, '../reports')}\n\n## 物理来源容器\n\n${containerLinks(domain.source_container_refs, '../source-containers')}\n\n## 规划依据摘要\n\n- Domain ID: \`${key}\`\n- 归类方法: agent_semantic_synthesis\n- 快照: \`${snapshotHash}\`\n- 纳入看板: ${array(domain.dashboard_refs).length}\n- 涉及报表: ${array(domain.report_refs).length}\n- 物理来源容器: ${array(domain.source_container_refs).length}\n`;
620
+ }
621
+
622
+ function renderDomainJudgmentModel(domain) {
623
+ const model = object(domain.domain_judgment_model);
624
+ const attachmentLines = array(model.asset_attachment_logic).map((item) => {
625
+ const row = object(item);
626
+ const ref = object(row.asset_ref);
627
+ const label = domainAssetLabel(ref);
628
+ return `- ${domainAttachmentRoleLabel(row.role)}:${label}。${stripClausePunctuation(row.reason)}`;
629
+ });
630
+ return `- 判断状态:${stripClausePunctuation(model.business_state_judged)}\n- 主要对象:${array(model.main_objects).map(text).filter(Boolean).join('、')}\n- 判断信号:${joinChineseClauses(model.signals)}\n- 判断路径:\n${renderNumberedSteps(model.decision_path, ' ')}\n- 资产挂载:\n${attachmentLines.join('\n') || ' - 无'}\n- 边界:${joinChineseClauses(model.boundaries)}`;
631
+ }
632
+
633
+ function domainAssetLabel(ref) {
634
+ const type = text(ref.resource_type);
635
+ const key = text(ref.resource_key);
636
+ if (type === 'dashboard') {
637
+ const dashboard = dashboardById.get(key);
638
+ const title = text(dashboard?.title) || key;
639
+ return `[${link(title)}](../dashboards/${dashboardFiles.get(key)})(dashboard:${key})`;
640
+ }
641
+ if (type === 'report') {
642
+ const report = reportById.get(key);
643
+ const title = text(report?.title) || key;
644
+ return `[${link(title)}](../reports/${reportFiles.get(key)})(report:${key})`;
645
+ }
646
+ if (type === 'metric') {
647
+ const metric = metrics.find((row) => text(row.resource_key) === key);
648
+ const title = text(metric?.title) || key;
649
+ return `[${link(title)}](../metrics/${metricFiles.get(key)})(metric:${key})`;
650
+ }
651
+ return `${type}:${key}`;
652
+ }
653
+
654
+ function domainAttachmentRoleLabel(value) {
655
+ return ({
656
+ primary_entry: '首选入口',
657
+ supporting_evidence: '辅助证据',
658
+ drilldown: '下钻资产',
659
+ exclusion: '排除资产',
660
+ })[text(value)] ?? text(value);
468
661
  }
469
662
 
470
663
  function renderDomainAgentUsageSummary(domain) {
@@ -838,7 +1031,7 @@ function renderDashboardAgentUsageSummary(dashboard, reportRows, notes, domain,
838
1031
  const purposes = reportRows.slice(0, 8).map((row) => reportPurpose(row));
839
1032
  const metrics = topDomainFacts(reportRows.flatMap((row) => reportMetricSummaryItems(row)), 10);
840
1033
  const dimensions = topDomainFacts(reportRows.flatMap((row) => reportDimensionSummaryItems(row)), 10);
841
- return `- 用途:${classification}${reportRows.length ? `这张看板承载 ${reportRows.length} 张报表,用于从看板层进入相关分析。` : '当前没有关联报表。'}\n- 适合回答:\n${purposes.map((item) => ` - ${item}`).join('\n') || ' - 当前看板没有足够报表证据概括适用问题。'}\n- 主要指标:${metrics.join('、') || '未从关联报表识别到稳定指标。'}\n- 常用下钻维度:${dimensions.join('、') || '未从关联报表识别到稳定下钻维度。'}\n- 便签规则:${notes.length ? '看板便签可用于理解业务目的、注意事项和共享边界;精确口径仍以下属报表为准。' : '没有看板便签。'}\n- 使用边界:当前数值、客户名单和趋势结果需要实时执行看板或下属报表;知识库只提供路由、口径和边界。`;
1034
+ return `- 用途:${classification}${reportRows.length ? `这张看板承载 ${reportRows.length} 张报表,用于从看板层进入相关分析。` : '当前没有关联报表。'}\n- 适合回答:\n${purposes.map((item) => ` - ${item}`).join('\n') || ' - 当前看板没有足够报表证据概括适用问题。'}\n- 主要指标:${metrics.join('、') || '关联报表没有暴露可稳定概括的指标,使用时需要先进入下属报表核验。'}\n- 常用下钻维度:${dimensions.join('、') || '关联报表没有暴露稳定拆分维度,执行前需要按报表页面设置确认。'}\n- 便签规则:${notes.length ? '看板便签可用于理解业务目的、注意事项和共享边界;精确口径仍以下属报表为准。' : '没有看板便签。'}\n- 使用边界:当前数值、客户名单和趋势结果需要实时执行看板或下属报表;知识库只提供路由、口径和边界。`;
842
1035
  }
843
1036
 
844
1037
  function renderReportAgentUsageSummary(report, metadataRefs, calculationContext, sqlSummary) {
@@ -847,8 +1040,8 @@ function renderReportAgentUsageSummary(report, metadataRefs, calculationContext,
847
1040
  const purpose = usefulSemanticText(sqlSummary?.business_purpose) || reportPurpose(report);
848
1041
  const questions = usefulSemanticList(sqlSummary?.applicable_questions)
849
1042
  ? array(sqlSummary.applicable_questions).map(renderPlainSemanticItem)
850
- : [reportPurpose(report)];
851
- const metrics = array(report.metric_occurrences).map(occurrenceSentence);
1043
+ : reportApplicableQuestions(report);
1044
+ const metrics = topDomainFacts(array(report.metric_occurrences).map(occurrenceSentence), 12);
852
1045
  const parameters = reportParameterSummaryItems(calculationContext);
853
1046
  const dimensions = reportDimensionSummaryItems(report, sqlSummary, calculationContext, metadataRefs);
854
1047
  const filters = reportFilterSummaryItems(report, calculationContext);
@@ -856,7 +1049,21 @@ function renderReportAgentUsageSummary(report, metadataRefs, calculationContext,
856
1049
  const boundaries = usefulSemanticList(sqlSummary?.non_applicable_questions)
857
1050
  ? array(sqlSummary.non_applicable_questions).map(renderPlainSemanticItem)
858
1051
  : ['不能替代实时执行结果;当前知识库只说明报表语义和使用边界。'];
859
- return `- 用途:${purpose}\n- 适合回答:\n${questions.map((item) => ` - ${item}`).join('\n') || ' - 暂无明确适用问题。'}\n- 主要指标/输出:\n${metrics.map((item) => ` - ${item}`).join('\n') || ' - 未识别到稳定指标或输出字段。'}\n- 输入参数:\n${parameters.map((item) => ` - ${item}`).join('\n') || ' - 未发现需要 Agent 解释的输入参数。'}\n- 常用下钻维度:${dimensions.join('、') || '未识别到稳定下钻维度。'}\n- 关键过滤与限制:\n${[...filters, ...limits].map((item) => ` - ${item}`).join('\n') || ' - 未发现明确过滤、排序或行数限制。'}\n- 使用边界:\n${boundaries.map((item) => ` - ${item}`).join('\n')}`;
1052
+ return `- 用途:${purpose}\n- 适合回答:\n${questions.map((item) => ` - ${item}`).join('\n') || ' - 适合在进入报表后核验该资产标题对应的业务问题。'}\n- 主要指标/输出:\n${metrics.map((item) => ` - ${item}`).join('\n') || ' - 资产包没有暴露稳定指标或输出字段,使用时需要回到源报表核验。'}\n- 输入参数:\n${parameters.map((item) => ` - ${item}`).join('\n') || ' - 本报表没有需要 Agent 预先解释的运行参数。'}\n- 常用下钻维度:${dimensions.join('、') || '资产包没有暴露稳定拆分维度;如需分组比较,应先打开报表确认可用维度。'}\n- 关键过滤与限制:\n${[...filters, ...limits].map((item) => ` - ${item}`).join('\n') || ' - 资产包没有暴露固定过滤、排序或行数限制;执行前按报表页面设置确认。'}\n- 使用边界:\n${boundaries.map((item) => ` - ${item}`).join('\n')}`;
1053
+ }
1054
+
1055
+ function reportApplicableQuestions(report) {
1056
+ const title = text(report.title || report.resource_key);
1057
+ const metricNames = topDomainFacts(array(report.metric_occurrences).map(occurrenceLabel).filter(Boolean), 4);
1058
+ const dimensions = reportDimensionSummaryItems(report, null, calculationContextForKb(report), metadataRefsForReport(report)).slice(0, 4);
1059
+ const filters = reportFilterSummaryItems(report, object(report.calculation_context)).slice(0, 2);
1060
+ const subject = metricNames.length ? metricNames.join('、') : title;
1061
+ const result = [
1062
+ `查看“${title}”对应的${subject}。`,
1063
+ dimensions.length ? `按${dimensions.join('、')}拆分比较${subject}。` : '',
1064
+ filters.length ? `在${filters.join(';')}条件下解释${subject}。` : '',
1065
+ ].filter(Boolean);
1066
+ return [...new Set(result)];
860
1067
  }
861
1068
 
862
1069
  function renderReportMetricOccurrencesSection(report) {
@@ -871,13 +1078,17 @@ function renderSqlReportAgentUsageSummary(report, summary) {
871
1078
  return '_当前 SQL 报表没有可用的 Agent 语义摘要。该报表只能作为待核验线索,不能直接回答确定口径。_';
872
1079
  }
873
1080
  const purpose = usefulSemanticText(summary.business_purpose) || reportPurpose(report);
874
- return `该摘要由 CLI Agent 基于同一资产快照生成,作为 SQL 报表的源文件级业务语义输入;KB 可以直接消费这段摘要并结合项目其他资料组织 Wiki,不需要重新从长 SQL 中完整推导业务含义。\n\n- 摘要来源: ${text(summary.summary_source) || 'semantic_plan'}\n- 定义状态: ${text(summary.definition_state) || 'unknown'}\n- 用途:${purpose}\n- 统计粒度: ${text(summary.statistical_grain) || 'unknown'}\n- 证据定位: \`${text(summary.evidence_locator) || 'unknown'}\`\n\n### 输入参数\n\n${renderSemanticList(summary.input_parameters, renderSemanticItem)}\n\n### 输出字段\n\n${renderSemanticList(summary.output_fields, (item) => renderSqlSemanticItem(item, summary))}\n\n### 关键过滤与排除\n\n${renderSemanticList(summary.key_filters, (item) => renderSqlSemanticItem(item, summary))}\n\n### 默认限制与排序\n\n${renderSemanticList(summary.default_limits, (item) => renderSqlSemanticItem(item, summary))}\n\n### 可回答的问题\n\n${renderSemanticList(summary.applicable_questions, (item) => renderSqlSemanticItem(item, summary))}\n\n### 不可安全回答的问题\n\n${renderSemanticList(summary.non_applicable_questions, (item) => renderSqlSemanticItem(item, summary))}`;
1081
+ return `- 定义状态: ${text(summary.definition_state) || 'unknown'}\n- 用途:${purpose}\n- 统计粒度: ${text(summary.statistical_grain) || 'unknown'}\n- 证据定位: \`${text(summary.evidence_locator) || 'unknown'}\`\n\n### 输入参数\n\n${renderSemanticList(summary.input_parameters, renderSqlInputParameterItem)}\n\n### 输出字段\n\n${renderSemanticList(summary.output_fields, (item) => renderSqlSemanticItem(item, summary))}\n\n### 关键过滤与排除\n\n${renderSemanticList(summary.key_filters, (item) => renderSqlSemanticItem(item, summary))}\n\n### 默认限制与排序\n\n${renderSemanticList(summary.default_limits, (item) => renderSqlSemanticItem(item, summary))}\n\n### 可回答的问题\n\n${renderSemanticList(summary.applicable_questions, (item) => renderSqlSemanticItem(item, summary))}\n\n### 不可安全回答的问题\n\n${renderSemanticList(summary.non_applicable_questions, (item) => renderSqlSemanticItem(item, summary))}`;
875
1082
  }
876
1083
 
877
1084
  function renderSqlSemanticItem(value, summary) {
878
1085
  return replaceSqlParameterMentions(renderSemanticItem(value), summary);
879
1086
  }
880
1087
 
1088
+ function renderSqlInputParameterItem(value) {
1089
+ return sanitizeVisibleSqlSemanticText(renderSemanticItem(value));
1090
+ }
1091
+
881
1092
  function replaceSqlParameterMentions(value, summary) {
882
1093
  let result = sanitizeVisibleSqlSemanticText(value);
883
1094
  for (const [name, meaning] of sqlParameterMeaningEntries(summary)) {
@@ -923,6 +1134,11 @@ function genericSqlParameterMeaning(name) {
923
1134
  return normalized;
924
1135
  }
925
1136
 
1137
+ function isGeneratedSqlParameterName(name) {
1138
+ return /^(?:selector|variable|text|number|partdate)\d*$/i.test(text(name))
1139
+ || /^date\d+$/i.test(text(name));
1140
+ }
1141
+
926
1142
  function escapeRegExp(value) {
927
1143
  return text(value).replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
928
1144
  }
@@ -944,9 +1160,12 @@ function renderPlainSemanticItem(value) {
944
1160
  if (item.default_value != null && text(item.default_value)) extras.push(`default=${text(item.default_value)}`);
945
1161
  const expressionKind = text(item.expression_kind);
946
1162
  if (expressionKind) extras.push(expressionKind);
947
- if (isInputParameterSemanticItem(item) && meaning && name) {
1163
+ if (isInputParameterSemanticItem(item) && meaning && name && !isGeneratedSqlParameterName(name)) {
948
1164
  return [`${meaning}(\`${name}\`)`, extras.join(',')].filter(Boolean).join(':');
949
1165
  }
1166
+ if (isInputParameterSemanticItem(item) && meaning) {
1167
+ return [meaning, extras.join(',')].filter(Boolean).join(':');
1168
+ }
950
1169
  return [name ? `\`${name}\`` : '', meaning, extras.join(',')].filter(Boolean).join(':') || jsonInline(item);
951
1170
  }
952
1171
 
@@ -981,7 +1200,7 @@ function reportDimensionSummaryItems(report, summary = null, calculationContext
981
1200
  ...array(object(context.event_view).groups).map(text),
982
1201
  ...array(refs).map((ref) => namedCodeFact(ref.title || ref.display_name, ref.resource_key)),
983
1202
  ];
984
- return topDomainFacts(candidates.filter((item) => isLikelyDrilldownDimensionText(item) && !isExcludedDrilldownDimensionText(item)), 12);
1203
+ return topDomainFacts(candidates.filter(isUsefulDomainDimensionFact), 12);
985
1204
  }
986
1205
 
987
1206
  function reportParameterSummaryItems(calculationContext) {
@@ -1096,6 +1315,8 @@ function sanitizeKeyFilters(values) {
1096
1315
 
1097
1316
  function sanitizeVisibleSqlSemanticText(value) {
1098
1317
  let normalized = text(value)
1318
+ .replace(/SQL\s*使用\s*select\s+\*\s*[,,]\s*输出字段需以源表\s*([^,。]+?)\s*当前结构为准/gi, '源表全字段输出,字段清单需回源表 $1 当前结构确认')
1319
+ .replace(/\bselect\s+\*/gi, '源表全字段输出')
1099
1320
  .replace(/\$\{(?:(?:Text|Selector|PartDate|Number|Date|DateTime|MultiSelector)\s*:)?\s*([A-Za-z_][\w.-]*)\s*\}/g, '$1')
1100
1321
  .replace(/SQL\s*输出字段[::]?/gi, '')
1101
1322
  .replace(/来源表达式[::]?/g, '')
@@ -1127,6 +1348,21 @@ function dedupeSemicolonSegments(value) {
1127
1348
  return result.join(';');
1128
1349
  }
1129
1350
 
1351
+ function stripClausePunctuation(value) {
1352
+ return text(value).replace(/[。;;,,\s]+$/u, '');
1353
+ }
1354
+
1355
+ function joinChineseClauses(value) {
1356
+ const values = array(value).map(stripClausePunctuation).filter(Boolean);
1357
+ return values.join(';') || '无';
1358
+ }
1359
+
1360
+ function renderNumberedSteps(value, indent = '') {
1361
+ const values = array(value).map(stripClausePunctuation).filter(Boolean);
1362
+ if (!values.length) return `${indent}- 无`;
1363
+ return values.map((item, index) => `${indent}${index + 1}. ${item}`).join('\n');
1364
+ }
1365
+
1130
1366
  function renderSemanticList(value, renderer) {
1131
1367
  const values = array(value);
1132
1368
  if (!values.length) return '- 无';
@@ -1143,9 +1379,12 @@ function renderSemanticItem(value) {
1143
1379
  .filter(([key]) => !['name', 'field', 'key', 'column', 'title', 'meaning', 'description', 'remark', 'purpose'].includes(key))
1144
1380
  .map(([key, itemValue]) => `${key}=${inlineSemanticValue(itemValue)}`)
1145
1381
  .join(', ');
1146
- if (isInputParameterSemanticItem(item) && meaning && name) {
1382
+ if (isInputParameterSemanticItem(item) && meaning && name && !isGeneratedSqlParameterName(name)) {
1147
1383
  return [`${meaning}(\`${name}\`)`, details].filter(Boolean).join(':') || jsonInline(item);
1148
1384
  }
1385
+ if (isInputParameterSemanticItem(item) && meaning) {
1386
+ return [meaning, details].filter(Boolean).join(':') || jsonInline(item);
1387
+ }
1149
1388
  return [name ? `\`${name}\`` : '', meaning, details].filter(Boolean).join(':') || jsonInline(item);
1150
1389
  }
1151
1390
 
@@ -1817,10 +2056,20 @@ function appendConflict(target, id, message) {
1817
2056
  }
1818
2057
 
1819
2058
  function semanticConflictSummary(kind, key, values, signatureLabelFn) {
1820
- const rows = values.map((row) => `${assetDisplayName(row)}(${assetIdentity(row)},${signatureLabelFn(row)})`);
2059
+ const rows = [...values]
2060
+ .sort((left, right) => semanticConflictAssetSortKey(left).localeCompare(semanticConflictAssetSortKey(right), 'en'))
2061
+ .map((row) => `${assetDisplayName(row)}(${assetIdentity(row)},${signatureLabelFn(row)})`);
1821
2062
  return `语义“${key}”存在 ${values.length} 个${kind}候选,但口径不同:${rows.join(';')}。Agent 不应仅凭名称选择,需要优先使用业务域/召回卡或让用户确认。`;
1822
2063
  }
1823
2064
 
2065
+ function semanticConflictAssetSortKey(row) {
2066
+ return [
2067
+ assetIdentity(row),
2068
+ text(row.resource_type),
2069
+ text(row.evidence_id),
2070
+ ].join('\u0000');
2071
+ }
2072
+
1824
2073
  function reportSemanticKey(report) {
1825
2074
  return normalizeSemanticIntent(text(report.title)) || normalizeSemanticIntent(array(report.metric_occurrences).map(occurrenceLabel).join(' '));
1826
2075
  }
@@ -1929,7 +2178,7 @@ function renderOccurrence(item, index) {
1929
2178
 
1930
2179
  - 事件: ${event}
1931
2180
  - 复用指标 key: ${text(item.metric_key) ? `\`${text(item.metric_key)}\`` : '无'}
1932
- - 聚合方式: ${aggregationLabel(item) || '未说明'}
2181
+ - 聚合方式: ${aggregationLabel(item) || '资产包未暴露聚合方式'}
1933
2182
  - 度量字段: ${readableMetricText(item.measure_title || item.measure_key) || '无'}
1934
2183
  - 公式表达式: ${expression ? `\`${expression}\`` : '无'}
1935
2184
  - 公式依赖: ${formulaSummary || '无'}
@@ -1945,9 +2194,10 @@ function occurrenceSentence(item) {
1945
2194
  return `${label}:公式指标${expression ? `,表达式为 \`${expression}\`` : ''}${dependencies.length ? `,依赖${dependencies.join('、')}` : ''}。`;
1946
2195
  }
1947
2196
  const event = metricEventLabel(item) || '未知事件';
1948
- const aggregation = aggregationLabel(item) || '未说明聚合';
2197
+ const aggregation = aggregationLabel(item);
1949
2198
  const measure = readableMetricText(item.measure_title || item.measure_key);
1950
- return `${label}:基于“${event}”按“${aggregation}”计算${measure ? `,度量字段为“${measure}”` : ''}${text(item.metric_key) ? `,复用指标 key 为 \`${text(item.metric_key)}\`` : ''}。`;
2199
+ const aggregationPart = aggregation ? `按“${aggregation}”计算` : '计算,资产包未暴露聚合方式';
2200
+ return `${label}:基于“${event}”${aggregationPart}${measure ? `,度量字段为“${measure}”` : ''}${text(item.metric_key) ? `,复用指标 key 为 \`${text(item.metric_key)}\`` : ''}。`;
1951
2201
  }
1952
2202
 
1953
2203
  function formulaExpression(item) {
@@ -1972,7 +2222,7 @@ function formulaDependencies(item) {
1972
2222
  const event = object(object(dependency).event);
1973
2223
  const quota = object(object(dependency).quota);
1974
2224
  const eventName = text(event.eventDesc || event.eventName) || '未知事件';
1975
- const aggregation = text(quota.quotaDesc || quota.quotaName) || '未说明聚合';
2225
+ const aggregation = text(quota.quotaDesc || quota.quotaName) || '资产包未暴露聚合方式';
1976
2226
  const filters = array(object(event.filter).filts)
1977
2227
  .map((filter) => text(object(filter).columnDesc || object(filter).quotDesc || object(filter).prop))
1978
2228
  .filter(Boolean);
@@ -2163,9 +2413,13 @@ function readableMetricText(value) {
2163
2413
  function groupLabel(group) { return text(object(group).columnDesc || object(group).columnName || group); }
2164
2414
 
2165
2415
  function renderSemanticConflictList() {
2166
- const reportLines = [...reportConflicts.entries()].map(([id, conflicts]) =>
2167
- `- [${link(reportById.get(id)?.title || id)}](../briefs/reports/${reportFiles.get(id)}):${conflicts.join('')}`);
2168
- const metadataLines = [...metadataSemanticConflicts.entries()].map(([id, conflicts]) => {
2416
+ const reportLines = [...reportConflicts.entries()]
2417
+ .sort(([left], [right]) => left.localeCompare(right, 'en'))
2418
+ .map(([id, conflicts]) =>
2419
+ `- [${link(reportById.get(id)?.title || id)}](../briefs/reports/${reportFiles.get(id)}):${conflicts.join(';')}`);
2420
+ const metadataLines = [...metadataSemanticConflicts.entries()]
2421
+ .sort(([left], [right]) => left.localeCompare(right, 'en'))
2422
+ .map(([id, conflicts]) => {
2169
2423
  const item = metadata.find((row) => text(row.evidence_id) === id);
2170
2424
  if (!item) return `- \`${id}\`:${conflicts.join(';')}`;
2171
2425
  return `- [${link(metadataDisplayTitle(item))}](../briefs/metadata/${metadataDirectory(item.resource_type)}/${metadataFiles.get(id)}):${conflicts.join(';')}`;
@@ -2202,7 +2456,7 @@ function renderCoverage() {
2202
2456
 
2203
2457
  ## 技术附录
2204
2458
 
2205
- ${appendices.map((row) => `- [${link(row.title)}](../appendices/${appendixFiles.get(text(row.appendix_id))})`).join('\n') || '- 无'}
2459
+ ${sort(appendices, 'appendix_id').map((row) => `- [${link(row.title)}](../appendices/${appendixFiles.get(text(row.appendix_id))})`).join('\n') || '- 无'}
2206
2460
 
2207
2461
  ## 技术来源容器
2208
2462
 
@@ -2210,13 +2464,13 @@ ${containerAssetLinks(technicalContainers)}
2210
2464
 
2211
2465
  ## 无物理空间看板
2212
2466
 
2213
- ${assetLinks(unassignedDashboards, dashboardFiles, '../dashboards')}
2467
+ ${assetLinks(sort(unassignedDashboards, 'resource_key'), dashboardFiles, '../dashboards')}
2214
2468
 
2215
2469
  ## 独立报表
2216
2470
 
2217
2471
  这些报表没有看板承载,保留为可检索资产,但不根据名称强行推断业务域。
2218
2472
 
2219
- ${assetLinks(standaloneReports, reportFiles, '../reports')}
2473
+ ${assetLinks(sort(standaloneReports, 'resource_key'), reportFiles, '../reports')}
2220
2474
 
2221
2475
  ## 语义冲突
2222
2476
 
@@ -2224,7 +2478,7 @@ ${renderSemanticConflictList()}
2224
2478
 
2225
2479
  ## 缺少源标题的元数据
2226
2480
 
2227
- ${metadataMissingTitles.map((item) => `- [\`${link(item.resource_key)}\`](../metadata/${metadataDirectory(item.resource_type)}/${metadataFiles.get(text(item.evidence_id))})`).join('\n') || '- 无'}
2481
+ ${sort(metadataMissingTitles, 'evidence_id').map((item) => `- [\`${link(item.resource_key)}\`](../metadata/${metadataDirectory(item.resource_type)}/${metadataFiles.get(text(item.evidence_id))})`).join('\n') || '- 无'}
2228
2482
  `;
2229
2483
  }
2230
2484
 
@@ -2347,7 +2601,7 @@ function assetLinks(values, files, prefix) {
2347
2601
  }
2348
2602
 
2349
2603
  function containerAssetLinks(values) {
2350
- return values.map((row) => `- [${link(row.container_title || row.container_key)}](../source-containers/${containerFiles.get(text(row.container_key))})(${text(row.container_kind)})`).join('\n') || '- 无';
2604
+ return sort(values, 'container_key').map((row) => `- [${link(row.container_title || row.container_key)}](../source-containers/${containerFiles.get(text(row.container_key))})(${text(row.container_kind)})`).join('\n') || '- 无';
2351
2605
  }
2352
2606
 
2353
2607
  function topNames(values, limit = 5) {
@@ -2524,6 +2778,9 @@ function metadataUsage() {
2524
2778
  result.get(id).push({ report_id: text(report.resource_key), report_name: text(report.title) });
2525
2779
  }
2526
2780
  }
2781
+ for (const [id, values] of result.entries()) {
2782
+ result.set(id, values.sort((left, right) => text(left.report_id).localeCompare(text(right.report_id), 'en')));
2783
+ }
2527
2784
  return result;
2528
2785
  }
2529
2786
 
@@ -2536,6 +2793,9 @@ function metadataMetricUsage() {
2536
2793
  result.get(id).push({ metric_id: text(metric.resource_key), metric_name: text(metric.title) });
2537
2794
  }
2538
2795
  }
2796
+ for (const [id, values] of result.entries()) {
2797
+ result.set(id, values.sort((left, right) => text(left.metric_id).localeCompare(text(right.metric_id), 'en')));
2798
+ }
2539
2799
  return result;
2540
2800
  }
2541
2801
 
@@ -2622,7 +2882,7 @@ function parseArgs(values) {
2622
2882
  const token = values[index];
2623
2883
  if (!token.startsWith('--')) fail(`Unexpected argument: ${token}`);
2624
2884
  const name = token.slice(2);
2625
- if (['force', 'allow-truncated', 'allow-all-visible'].includes(name)) result[name] = true;
2885
+ if (['force', 'allow-truncated', 'allow-all-visible', 'allow-semantic-plan-snapshot-drift'].includes(name)) result[name] = true;
2626
2886
  else {
2627
2887
  const value = values[index + 1];
2628
2888
  if (!value || value.startsWith('--')) fail(`Missing value for --${name}.`);