@thinkingai/ae-cli 6.0.53 → 6.0.55

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 (121) hide show
  1. package/README.md +28 -9
  2. package/README.zh.md +25 -9
  3. package/bin/ae-cli.js +6 -1
  4. package/cli-installation-guide.md +91 -0
  5. package/dist/{auth-LHM7NQPR.js → auth-RQBO6MTY.js} +9 -11
  6. package/dist/automation-VAAQBM7Y.js +459 -0
  7. package/dist/{capability-F2RIR5DS.js → capability-JRIFC4AJ.js} +6 -6
  8. package/dist/{chunk-647BRKZK.js → chunk-374V5R6N.js} +3 -3
  9. package/dist/{chunk-KJDOTPYU.js → chunk-3YVEAZUN.js} +59 -26
  10. package/dist/{chunk-KS4DE3MX.js → chunk-5XJYLEVC.js} +4 -4
  11. package/dist/{chunk-BW7JUDEI.js → chunk-6VNIR4AP.js} +514 -213
  12. package/dist/{chunk-5XRQ3CZR.js → chunk-7KU2WNQE.js} +43 -8
  13. package/dist/{chunk-7WZACOAI.js → chunk-AP7YP2NX.js} +2 -2
  14. package/dist/chunk-CI24BDR2.js +43 -0
  15. package/dist/{chunk-SO5LKIWP.js → chunk-CT7QDJS6.js} +2 -2
  16. package/dist/{chunk-DU72X2IO.js → chunk-GHXZK54S.js} +22 -10
  17. package/dist/{chunk-N4BLPJC7.js → chunk-OP44TT4H.js} +1 -1
  18. package/dist/chunk-Q5US7JC5.js +133 -0
  19. package/dist/{chunk-PGCIKZRE.js → chunk-RGYPSEYD.js} +3 -3
  20. package/dist/chunk-RK6GLDGI.js +27 -0
  21. package/dist/chunk-RPBOAQQQ.js +56 -0
  22. package/dist/{chunk-GK7WDY7E.js → chunk-S6M42KND.js} +1 -1
  23. package/dist/{chunk-QYXDJOLA.js → chunk-VZZNLV3I.js} +19 -11
  24. package/dist/{chunk-YVGXGTD6.js → chunk-WNGFSIOB.js} +7 -1
  25. package/dist/{chunk-IG3DYEIR.js → chunk-ZL7SGNIA.js} +5 -3
  26. package/dist/{community-report-client-6VJDDMJJ.js → community-report-client-PEERQCX2.js} +1 -1
  27. package/dist/{config-XQWOPDHN.js → config-SICKWZ2A.js} +8 -8
  28. package/dist/context-NGVFPCBK.js +136 -0
  29. package/dist/{data-integration-XBTCGXQT.js → data-integration-BCLQFTGU.js} +6 -6
  30. package/dist/index.js +125 -48
  31. package/dist/{local-data-upload-client-3P7NIVQ4.js → local-data-upload-client-TWMHFLUA.js} +1 -1
  32. package/dist/memory-CQKH2JAV.js +580 -0
  33. package/dist/{metadata-V5ZDTKWZ.js → metadata-EMFGUI2W.js} +7 -7
  34. package/dist/{model-ZS7HVGKN.js → model-VX5M2CJ6.js} +9 -5
  35. package/dist/{personal-semantic-preference-2XANU3F6.js → personal-semantic-preference-SLWVVYZ4.js} +117 -11
  36. package/dist/{project-semantic-LP3LY7QP.js → project-semantic-DPSZBKQD.js} +6 -6
  37. package/dist/{sync-EQLSTUS7.js → sync-RZIAV37C.js} +12 -8
  38. package/dist/{te-agent-Z3HCTKR7.js → te-agent-AORRE2FM.js} +90 -454
  39. package/dist/{te-analysis-3EVYQ3IO.js → te-analysis-ISXKJKRT.js} +297 -20
  40. package/dist/{te-community-Z7JACGF3.js → te-community-42M7ICDO.js} +3 -3
  41. package/dist/{te-dataops-F7GQOIS6.js → te-dataops-BNKNZ4F2.js} +5 -5
  42. package/dist/{te-engage-LFBU6WWH.js → te-engage-BZE3MBKT.js} +6 -6
  43. package/dist/{te-kb-QJLJEX5K.js → te-kb-ULATOTQ3.js} +494 -214
  44. package/dist/{te-system-IXC43QHZ.js → te-system-UGHVKBVV.js} +10 -9
  45. package/dist/{te-team-BIZIKIHZ.js → te-team-YA6SNOJ3.js} +6 -6
  46. package/dist/things-DTCOQIQB.js +1091 -0
  47. package/dist/{update-W2SGQNKT.js → update-ESNYKUVR.js} +59 -51
  48. package/dist/workspace-JEOHKFPX.js +554 -0
  49. package/package.json +21 -9
  50. package/skills/ae-agent/SKILL.md +91 -139
  51. package/skills/ae-agent/references/add-model.md +1 -1
  52. package/skills/ae-agent/references/create-agent.md +1 -1
  53. package/skills/ae-agent/references/find-archived-conversations.md +12 -5
  54. package/skills/ae-agent/references/get-agent.md +1 -1
  55. package/skills/ae-agent/references/list-agents.md +2 -2
  56. package/skills/ae-agent/references/list-models.md +2 -2
  57. package/skills/ae-agent/references/restore-conversation.md +3 -1
  58. package/skills/ae-analysis/SKILL.md +15 -6
  59. package/skills/ae-analysis/references/ai_models/funnel.md +5 -3
  60. package/skills/ae-analysis/references/ai_models.md +27 -0
  61. package/skills/ae-analysis/references/command_index.md +12 -4
  62. package/skills/ae-analysis/references/dashboard_move.md +21 -0
  63. package/skills/ae-analysis/references/dashboard_report_add.md +2 -0
  64. package/skills/ae-analysis/references/dashboard_report_get.md +19 -0
  65. package/skills/ae-analysis/references/dashboard_report_update.md +27 -0
  66. package/skills/ae-analysis/references/dashboard_update.md +1 -1
  67. package/skills/ae-analysis/references/event_report_presentation.md +76 -0
  68. package/skills/ae-analysis/references/feedback_submit.md +37 -0
  69. package/skills/ae-analysis/references/metadata_resolution.md +15 -0
  70. package/skills/ae-analysis/references/personal_semantic_preference_review.md +9 -0
  71. package/skills/ae-analysis/references/personal_semantic_preference_review_list.md +7 -0
  72. package/skills/ae-analysis/references/personal_semantic_preference_submit.md +7 -0
  73. package/skills/ae-analysis/references/project_semantic_knowledge_wiki.md +90 -31
  74. package/skills/ae-analysis/references/project_semantic_knowledge_wiki_plan_schema.md +99 -1
  75. package/skills/ae-analysis/references/report_create.md +13 -3
  76. package/skills/ae-analysis/references/report_get.md +5 -1
  77. package/skills/ae-analysis/references/report_update.md +15 -4
  78. package/skills/ae-analysis/references/system_query_monitor_overview.md +2 -0
  79. package/skills/ae-analysis/references/system_query_task_cancel.md +2 -0
  80. package/skills/ae-analysis/references/system_query_task_export.md +6 -2
  81. package/skills/ae-analysis/references/system_query_task_get.md +2 -0
  82. package/skills/ae-analysis/references/system_query_task_list.md +7 -3
  83. package/skills/ae-analysis/references/system_query_task_options.md +2 -2
  84. package/skills/ae-analysis/references/system_query_task_realtime_list.md +15 -0
  85. package/skills/ae-analysis/references/user_tag_models.md +2 -0
  86. package/skills/ae-analysis/scripts/project-semantic-knowledge-wiki/build-empty-skeleton.mjs +105 -0
  87. package/skills/ae-analysis/scripts/project-semantic-knowledge-wiki/build-project-semantic-wiki.mjs +669 -29
  88. package/skills/ae-analysis/scripts/project-semantic-knowledge-wiki/company-kb.mjs +21 -8
  89. package/skills/ae-analysis/scripts/project-semantic-knowledge-wiki/default-compile-rules.md +28 -10
  90. package/skills/ae-analysis/scripts/project-semantic-knowledge-wiki/default-compile-schema.md +66 -0
  91. package/skills/ae-analysis/scripts/project-semantic-knowledge-wiki/generate-build-ir.mjs +285 -24
  92. package/skills/ae-analysis/scripts/project-semantic-knowledge-wiki/package-wiki-source-zip.mjs +262 -30
  93. package/skills/ae-analysis/scripts/project-semantic-knowledge-wiki/plan-kb-source-sync.mjs +1 -1
  94. package/skills/ae-analysis/scripts/project-semantic-knowledge-wiki/precompiled-source.mjs +46 -2
  95. package/skills/ae-analysis/scripts/project-semantic-knowledge-wiki/prepare-compile-schema.mjs +64 -0
  96. package/skills/ae-analysis/scripts/project-semantic-knowledge-wiki/refresh-state-parts.mjs +91 -0
  97. package/skills/ae-automation/SKILL.md +16 -0
  98. package/skills/ae-automation/references/cli.md +53 -0
  99. package/skills/ae-automation/references/command_index.md +17 -0
  100. package/skills/ae-capability/SKILL.md +15 -6
  101. package/skills/ae-current-context/SKILL.md +3 -2
  102. package/skills/ae-kb/SKILL.md +62 -12
  103. package/skills/ae-kb/references/analysis-workflow.md +73 -36
  104. package/skills/ae-kb/references/query-workflow.md +67 -33
  105. package/skills/ae-kb/references/schema-import.md +5 -3
  106. package/skills/ae-kb/references/source-mutations.md +6 -4
  107. package/skills/ae-kb/references/text-sources.md +53 -0
  108. package/skills/ae-kb-discovery/SKILL.md +1 -1
  109. package/skills/ae-team/SKILL.md +23 -17
  110. package/skills/ae-things/SKILL.md +85 -0
  111. package/skills/ae-things/references/cli.md +103 -0
  112. package/skills/ae-things-progress/SKILL.md +43 -0
  113. package/skills/ae-things-review/SKILL.md +42 -0
  114. package/skills/ae-workspace/SKILL.md +43 -0
  115. package/skills/ae-workspace/references/commands.md +51 -0
  116. package/dist/chunk-E2JKXMVO.js +0 -29
  117. package/dist/context-M3HDGEIR.js +0 -46
  118. package/dist/memory-6APEB2JB.js +0 -892
  119. package/skills/ae-agent/references/create-automation.md +0 -125
  120. package/skills/ae-agent/references/list-automations.md +0 -56
  121. package/skills/ae-agent/references/update-automation.md +0 -107
@@ -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) {
@@ -551,6 +744,7 @@ function domainReportEvents(report) {
551
744
  .map((item) => namedCodeFact(item.title, item.resource_key))
552
745
  .filter(isUsefulDomainEventFact);
553
746
  const occurrenceEvents = array(report.metric_occurrences)
747
+ .filter((item) => !isInternalMetricExpression(item.event_title))
554
748
  .map((item) => namedCodeFact(item.event_title, item.event_name))
555
749
  .filter(isUsefulDomainEventFact);
556
750
  return [...refs, ...occurrenceEvents];
@@ -838,7 +1032,7 @@ function renderDashboardAgentUsageSummary(dashboard, reportRows, notes, domain,
838
1032
  const purposes = reportRows.slice(0, 8).map((row) => reportPurpose(row));
839
1033
  const metrics = topDomainFacts(reportRows.flatMap((row) => reportMetricSummaryItems(row)), 10);
840
1034
  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- 使用边界:当前数值、客户名单和趋势结果需要实时执行看板或下属报表;知识库只提供路由、口径和边界。`;
1035
+ 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
1036
  }
843
1037
 
844
1038
  function renderReportAgentUsageSummary(report, metadataRefs, calculationContext, sqlSummary) {
@@ -847,8 +1041,8 @@ function renderReportAgentUsageSummary(report, metadataRefs, calculationContext,
847
1041
  const purpose = usefulSemanticText(sqlSummary?.business_purpose) || reportPurpose(report);
848
1042
  const questions = usefulSemanticList(sqlSummary?.applicable_questions)
849
1043
  ? array(sqlSummary.applicable_questions).map(renderPlainSemanticItem)
850
- : [reportPurpose(report)];
851
- const metrics = array(report.metric_occurrences).map(occurrenceSentence);
1044
+ : reportApplicableQuestions(report);
1045
+ const metrics = topDomainFacts(array(report.metric_occurrences).map(occurrenceSentence), 12);
852
1046
  const parameters = reportParameterSummaryItems(calculationContext);
853
1047
  const dimensions = reportDimensionSummaryItems(report, sqlSummary, calculationContext, metadataRefs);
854
1048
  const filters = reportFilterSummaryItems(report, calculationContext);
@@ -856,7 +1050,21 @@ function renderReportAgentUsageSummary(report, metadataRefs, calculationContext,
856
1050
  const boundaries = usefulSemanticList(sqlSummary?.non_applicable_questions)
857
1051
  ? array(sqlSummary.non_applicable_questions).map(renderPlainSemanticItem)
858
1052
  : ['不能替代实时执行结果;当前知识库只说明报表语义和使用边界。'];
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')}`;
1053
+ 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')}`;
1054
+ }
1055
+
1056
+ function reportApplicableQuestions(report) {
1057
+ const title = text(report.title || report.resource_key);
1058
+ const metricNames = topDomainFacts(array(report.metric_occurrences).map(occurrenceLabel).filter(Boolean), 4);
1059
+ const dimensions = reportDimensionSummaryItems(report, null, calculationContextForKb(report), metadataRefsForReport(report)).slice(0, 4);
1060
+ const filters = reportFilterSummaryItems(report, object(report.calculation_context)).slice(0, 2);
1061
+ const subject = metricNames.length ? metricNames.join('、') : title;
1062
+ const result = [
1063
+ `查看“${title}”对应的${subject}。`,
1064
+ dimensions.length ? `按${dimensions.join('、')}拆分比较${subject}。` : '',
1065
+ filters.length ? `在${filters.join(';')}条件下解释${subject}。` : '',
1066
+ ].filter(Boolean);
1067
+ return [...new Set(result)];
860
1068
  }
861
1069
 
862
1070
  function renderReportMetricOccurrencesSection(report) {
@@ -871,13 +1079,17 @@ function renderSqlReportAgentUsageSummary(report, summary) {
871
1079
  return '_当前 SQL 报表没有可用的 Agent 语义摘要。该报表只能作为待核验线索,不能直接回答确定口径。_';
872
1080
  }
873
1081
  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))}`;
1082
+ 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
1083
  }
876
1084
 
877
1085
  function renderSqlSemanticItem(value, summary) {
878
1086
  return replaceSqlParameterMentions(renderSemanticItem(value), summary);
879
1087
  }
880
1088
 
1089
+ function renderSqlInputParameterItem(value) {
1090
+ return sanitizeVisibleSqlSemanticText(renderSemanticItem(value));
1091
+ }
1092
+
881
1093
  function replaceSqlParameterMentions(value, summary) {
882
1094
  let result = sanitizeVisibleSqlSemanticText(value);
883
1095
  for (const [name, meaning] of sqlParameterMeaningEntries(summary)) {
@@ -923,6 +1135,11 @@ function genericSqlParameterMeaning(name) {
923
1135
  return normalized;
924
1136
  }
925
1137
 
1138
+ function isGeneratedSqlParameterName(name) {
1139
+ return /^(?:selector|variable|text|number|partdate)\d*$/i.test(text(name))
1140
+ || /^date\d+$/i.test(text(name));
1141
+ }
1142
+
926
1143
  function escapeRegExp(value) {
927
1144
  return text(value).replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
928
1145
  }
@@ -944,9 +1161,12 @@ function renderPlainSemanticItem(value) {
944
1161
  if (item.default_value != null && text(item.default_value)) extras.push(`default=${text(item.default_value)}`);
945
1162
  const expressionKind = text(item.expression_kind);
946
1163
  if (expressionKind) extras.push(expressionKind);
947
- if (isInputParameterSemanticItem(item) && meaning && name) {
1164
+ if (isInputParameterSemanticItem(item) && meaning && name && !isGeneratedSqlParameterName(name)) {
948
1165
  return [`${meaning}(\`${name}\`)`, extras.join(',')].filter(Boolean).join(':');
949
1166
  }
1167
+ if (isInputParameterSemanticItem(item) && meaning) {
1168
+ return [meaning, extras.join(',')].filter(Boolean).join(':');
1169
+ }
950
1170
  return [name ? `\`${name}\`` : '', meaning, extras.join(',')].filter(Boolean).join(':') || jsonInline(item);
951
1171
  }
952
1172
 
@@ -981,7 +1201,7 @@ function reportDimensionSummaryItems(report, summary = null, calculationContext
981
1201
  ...array(object(context.event_view).groups).map(text),
982
1202
  ...array(refs).map((ref) => namedCodeFact(ref.title || ref.display_name, ref.resource_key)),
983
1203
  ];
984
- return topDomainFacts(candidates.filter((item) => isLikelyDrilldownDimensionText(item) && !isExcludedDrilldownDimensionText(item)), 12);
1204
+ return topDomainFacts(candidates.filter(isUsefulDomainDimensionFact), 12);
985
1205
  }
986
1206
 
987
1207
  function reportParameterSummaryItems(calculationContext) {
@@ -1096,6 +1316,8 @@ function sanitizeKeyFilters(values) {
1096
1316
 
1097
1317
  function sanitizeVisibleSqlSemanticText(value) {
1098
1318
  let normalized = text(value)
1319
+ .replace(/SQL\s*使用\s*select\s+\*\s*[,,]\s*输出字段需以源表\s*([^,。]+?)\s*当前结构为准/gi, '源表全字段输出,字段清单需回源表 $1 当前结构确认')
1320
+ .replace(/\bselect\s+\*/gi, '源表全字段输出')
1099
1321
  .replace(/\$\{(?:(?:Text|Selector|PartDate|Number|Date|DateTime|MultiSelector)\s*:)?\s*([A-Za-z_][\w.-]*)\s*\}/g, '$1')
1100
1322
  .replace(/SQL\s*输出字段[::]?/gi, '')
1101
1323
  .replace(/来源表达式[::]?/g, '')
@@ -1127,6 +1349,21 @@ function dedupeSemicolonSegments(value) {
1127
1349
  return result.join(';');
1128
1350
  }
1129
1351
 
1352
+ function stripClausePunctuation(value) {
1353
+ return text(value).replace(/[。;;,,\s]+$/u, '');
1354
+ }
1355
+
1356
+ function joinChineseClauses(value) {
1357
+ const values = array(value).map(stripClausePunctuation).filter(Boolean);
1358
+ return values.join(';') || '无';
1359
+ }
1360
+
1361
+ function renderNumberedSteps(value, indent = '') {
1362
+ const values = array(value).map(stripClausePunctuation).filter(Boolean);
1363
+ if (!values.length) return `${indent}- 无`;
1364
+ return values.map((item, index) => `${indent}${index + 1}. ${item}`).join('\n');
1365
+ }
1366
+
1130
1367
  function renderSemanticList(value, renderer) {
1131
1368
  const values = array(value);
1132
1369
  if (!values.length) return '- 无';
@@ -1143,9 +1380,12 @@ function renderSemanticItem(value) {
1143
1380
  .filter(([key]) => !['name', 'field', 'key', 'column', 'title', 'meaning', 'description', 'remark', 'purpose'].includes(key))
1144
1381
  .map(([key, itemValue]) => `${key}=${inlineSemanticValue(itemValue)}`)
1145
1382
  .join(', ');
1146
- if (isInputParameterSemanticItem(item) && meaning && name) {
1383
+ if (isInputParameterSemanticItem(item) && meaning && name && !isGeneratedSqlParameterName(name)) {
1147
1384
  return [`${meaning}(\`${name}\`)`, details].filter(Boolean).join(':') || jsonInline(item);
1148
1385
  }
1386
+ if (isInputParameterSemanticItem(item) && meaning) {
1387
+ return [meaning, details].filter(Boolean).join(':') || jsonInline(item);
1388
+ }
1149
1389
  return [name ? `\`${name}\`` : '', meaning, details].filter(Boolean).join(':') || jsonInline(item);
1150
1390
  }
1151
1391
 
@@ -1817,10 +2057,20 @@ function appendConflict(target, id, message) {
1817
2057
  }
1818
2058
 
1819
2059
  function semanticConflictSummary(kind, key, values, signatureLabelFn) {
1820
- const rows = values.map((row) => `${assetDisplayName(row)}(${assetIdentity(row)},${signatureLabelFn(row)})`);
2060
+ const rows = [...values]
2061
+ .sort((left, right) => semanticConflictAssetSortKey(left).localeCompare(semanticConflictAssetSortKey(right), 'en'))
2062
+ .map((row) => `${assetDisplayName(row)}(${assetIdentity(row)},${signatureLabelFn(row)})`);
1821
2063
  return `语义“${key}”存在 ${values.length} 个${kind}候选,但口径不同:${rows.join(';')}。Agent 不应仅凭名称选择,需要优先使用业务域/召回卡或让用户确认。`;
1822
2064
  }
1823
2065
 
2066
+ function semanticConflictAssetSortKey(row) {
2067
+ return [
2068
+ assetIdentity(row),
2069
+ text(row.resource_type),
2070
+ text(row.evidence_id),
2071
+ ].join('\u0000');
2072
+ }
2073
+
1824
2074
  function reportSemanticKey(report) {
1825
2075
  return normalizeSemanticIntent(text(report.title)) || normalizeSemanticIntent(array(report.metric_occurrences).map(occurrenceLabel).join(' '));
1826
2076
  }
@@ -1929,7 +2179,7 @@ function renderOccurrence(item, index) {
1929
2179
 
1930
2180
  - 事件: ${event}
1931
2181
  - 复用指标 key: ${text(item.metric_key) ? `\`${text(item.metric_key)}\`` : '无'}
1932
- - 聚合方式: ${aggregationLabel(item) || '未说明'}
2182
+ - 聚合方式: ${aggregationLabel(item) || '资产包未暴露聚合方式'}
1933
2183
  - 度量字段: ${readableMetricText(item.measure_title || item.measure_key) || '无'}
1934
2184
  - 公式表达式: ${expression ? `\`${expression}\`` : '无'}
1935
2185
  - 公式依赖: ${formulaSummary || '无'}
@@ -1945,9 +2195,10 @@ function occurrenceSentence(item) {
1945
2195
  return `${label}:公式指标${expression ? `,表达式为 \`${expression}\`` : ''}${dependencies.length ? `,依赖${dependencies.join('、')}` : ''}。`;
1946
2196
  }
1947
2197
  const event = metricEventLabel(item) || '未知事件';
1948
- const aggregation = aggregationLabel(item) || '未说明聚合';
2198
+ const aggregation = aggregationLabel(item);
1949
2199
  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)}\`` : ''}。`;
2200
+ const aggregationPart = aggregation ? `按“${aggregation}”计算` : '计算,资产包未暴露聚合方式';
2201
+ return `${label}:基于“${event}”${aggregationPart}${measure ? `,度量字段为“${measure}”` : ''}${text(item.metric_key) ? `,复用指标 key 为 \`${text(item.metric_key)}\`` : ''}。`;
1951
2202
  }
1952
2203
 
1953
2204
  function formulaExpression(item) {
@@ -1972,7 +2223,7 @@ function formulaDependencies(item) {
1972
2223
  const event = object(object(dependency).event);
1973
2224
  const quota = object(object(dependency).quota);
1974
2225
  const eventName = text(event.eventDesc || event.eventName) || '未知事件';
1975
- const aggregation = text(quota.quotaDesc || quota.quotaName) || '未说明聚合';
2226
+ const aggregation = text(quota.quotaDesc || quota.quotaName) || '资产包未暴露聚合方式';
1976
2227
  const filters = array(object(event.filter).filts)
1977
2228
  .map((filter) => text(object(filter).columnDesc || object(filter).quotDesc || object(filter).prop))
1978
2229
  .filter(Boolean);
@@ -2163,9 +2414,13 @@ function readableMetricText(value) {
2163
2414
  function groupLabel(group) { return text(object(group).columnDesc || object(group).columnName || group); }
2164
2415
 
2165
2416
  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]) => {
2417
+ const reportLines = [...reportConflicts.entries()]
2418
+ .sort(([left], [right]) => left.localeCompare(right, 'en'))
2419
+ .map(([id, conflicts]) =>
2420
+ `- [${link(reportById.get(id)?.title || id)}](../briefs/reports/${reportFiles.get(id)}):${conflicts.join(';')}`);
2421
+ const metadataLines = [...metadataSemanticConflicts.entries()]
2422
+ .sort(([left], [right]) => left.localeCompare(right, 'en'))
2423
+ .map(([id, conflicts]) => {
2169
2424
  const item = metadata.find((row) => text(row.evidence_id) === id);
2170
2425
  if (!item) return `- \`${id}\`:${conflicts.join(';')}`;
2171
2426
  return `- [${link(metadataDisplayTitle(item))}](../briefs/metadata/${metadataDirectory(item.resource_type)}/${metadataFiles.get(id)}):${conflicts.join(';')}`;
@@ -2202,7 +2457,7 @@ function renderCoverage() {
2202
2457
 
2203
2458
  ## 技术附录
2204
2459
 
2205
- ${appendices.map((row) => `- [${link(row.title)}](../appendices/${appendixFiles.get(text(row.appendix_id))})`).join('\n') || '- 无'}
2460
+ ${sort(appendices, 'appendix_id').map((row) => `- [${link(row.title)}](../appendices/${appendixFiles.get(text(row.appendix_id))})`).join('\n') || '- 无'}
2206
2461
 
2207
2462
  ## 技术来源容器
2208
2463
 
@@ -2210,13 +2465,13 @@ ${containerAssetLinks(technicalContainers)}
2210
2465
 
2211
2466
  ## 无物理空间看板
2212
2467
 
2213
- ${assetLinks(unassignedDashboards, dashboardFiles, '../dashboards')}
2468
+ ${assetLinks(sort(unassignedDashboards, 'resource_key'), dashboardFiles, '../dashboards')}
2214
2469
 
2215
2470
  ## 独立报表
2216
2471
 
2217
2472
  这些报表没有看板承载,保留为可检索资产,但不根据名称强行推断业务域。
2218
2473
 
2219
- ${assetLinks(standaloneReports, reportFiles, '../reports')}
2474
+ ${assetLinks(sort(standaloneReports, 'resource_key'), reportFiles, '../reports')}
2220
2475
 
2221
2476
  ## 语义冲突
2222
2477
 
@@ -2224,7 +2479,7 @@ ${renderSemanticConflictList()}
2224
2479
 
2225
2480
  ## 缺少源标题的元数据
2226
2481
 
2227
- ${metadataMissingTitles.map((item) => `- [\`${link(item.resource_key)}\`](../metadata/${metadataDirectory(item.resource_type)}/${metadataFiles.get(text(item.evidence_id))})`).join('\n') || '- 无'}
2482
+ ${sort(metadataMissingTitles, 'evidence_id').map((item) => `- [\`${link(item.resource_key)}\`](../metadata/${metadataDirectory(item.resource_type)}/${metadataFiles.get(text(item.evidence_id))})`).join('\n') || '- 无'}
2228
2483
  `;
2229
2484
  }
2230
2485
 
@@ -2347,7 +2602,7 @@ function assetLinks(values, files, prefix) {
2347
2602
  }
2348
2603
 
2349
2604
  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') || '- 无';
2605
+ 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
2606
  }
2352
2607
 
2353
2608
  function topNames(values, limit = 5) {
@@ -2524,6 +2779,9 @@ function metadataUsage() {
2524
2779
  result.get(id).push({ report_id: text(report.resource_key), report_name: text(report.title) });
2525
2780
  }
2526
2781
  }
2782
+ for (const [id, values] of result.entries()) {
2783
+ result.set(id, values.sort((left, right) => text(left.report_id).localeCompare(text(right.report_id), 'en')));
2784
+ }
2527
2785
  return result;
2528
2786
  }
2529
2787
 
@@ -2536,6 +2794,9 @@ function metadataMetricUsage() {
2536
2794
  result.get(id).push({ metric_id: text(metric.resource_key), metric_name: text(metric.title) });
2537
2795
  }
2538
2796
  }
2797
+ for (const [id, values] of result.entries()) {
2798
+ result.set(id, values.sort((left, right) => text(left.metric_id).localeCompare(text(right.metric_id), 'en')));
2799
+ }
2539
2800
  return result;
2540
2801
  }
2541
2802
 
@@ -2622,7 +2883,7 @@ function parseArgs(values) {
2622
2883
  const token = values[index];
2623
2884
  if (!token.startsWith('--')) fail(`Unexpected argument: ${token}`);
2624
2885
  const name = token.slice(2);
2625
- if (['force', 'allow-truncated', 'allow-all-visible'].includes(name)) result[name] = true;
2886
+ if (['force', 'allow-truncated', 'allow-all-visible', 'allow-semantic-plan-snapshot-drift'].includes(name)) result[name] = true;
2626
2887
  else {
2627
2888
  const value = values[index + 1];
2628
2889
  if (!value || value.startsWith('--')) fail(`Missing value for --${name}.`);