@thinkingai/ae-cli 6.1.23 → 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 (142) 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 +44 -21
  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 +3 -1
  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 +76 -6
  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 +30 -12
  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 +8 -3
  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-data-integration-helper/references/cpp_server_sdk_faq.md +2 -2
  112. package/skills/ae-data-integration-helper/references/logbus2_parser_plugin.md +6 -6
  113. package/skills/ae-data-integration-helper/references/sdk_log_guide.md +4 -4
  114. package/skills/ae-dataops/SKILL.md +54 -74
  115. package/skills/ae-dataops/references/collaboration.md +48 -0
  116. package/skills/ae-dataops/references/dataops-backfill.md +23 -18
  117. package/skills/ae-dataops/references/dataops-flow-create.md +74 -23
  118. package/skills/ae-dataops/references/dataops-flow-monitor.md +45 -13
  119. package/skills/ae-dataops/references/dataops-integration.md +57 -30
  120. package/skills/ae-dataops/references/dataops-query.md +21 -4
  121. package/skills/ae-dataops/references/dataops-table.md +143 -11
  122. package/skills/ae-engage/SKILL.md +21 -6
  123. package/skills/ae-engage/references/add-channel.md +3 -3
  124. package/skills/ae-engage/references/channel-mgmt.md +3 -3
  125. package/skills/ae-engage/references/collaboration.md +48 -0
  126. package/skills/ae-engage/references/common-metric.md +2 -2
  127. package/skills/ae-engage/references/save-task.md +2 -2
  128. package/skills/ae-engage/references/scene-config-channel.md +4 -4
  129. package/skills/ae-generate-tracking-code/SKILL.md +1 -1
  130. package/skills/ae-generate-tracking-plan/SKILL.md +4 -0
  131. package/skills/ae-generate-tracking-plan/references/collaboration.md +48 -0
  132. package/skills/ae-kb/SKILL.md +9 -1
  133. package/skills/ae-kb/references/collaboration.md +48 -0
  134. package/skills/ae-kb/references/schema-import.md +30 -0
  135. package/skills/ae-kb-discovery/SKILL.md +7 -2
  136. package/skills/ae-kb-discovery/references/collaboration.md +48 -0
  137. package/skills/ae-metadata/SKILL.md +20 -12
  138. package/skills/ae-metadata/references/collaboration.md +48 -0
  139. package/dist/project-semantic-3LF6Q6JZ.js +0 -1119
  140. package/skills/ae-project-semantic/SKILL.md +0 -193
  141. package/skills/ae-project-semantic/references/query-routing-v5.md +0 -165
  142. package/skills/ae-project-semantic/references/recommendation-quality.md +0 -68
@@ -23,6 +23,7 @@ const projectNameOverride = clean(args['project-name']);
23
23
  const force = Boolean(args.force);
24
24
  const keepBuildIr = Boolean(args['keep-build-ir']);
25
25
  const allowAllVisible = Boolean(args['allow-all-visible']);
26
+ const allowSemanticPlanSnapshotDrift = Boolean(args['allow-semantic-plan-snapshot-drift']);
26
27
 
27
28
  assertSafePaths(packageRoot, outputRoot, archivePath, uploadSourcesPath);
28
29
 
@@ -59,7 +60,9 @@ const snapshotDate = normalizeSnapshotDate(args['snapshot-date'] || descriptor.g
59
60
 
60
61
  const semanticPlan = readJson(semanticPlanPath);
61
62
  if (clean(semanticPlan.schema_version) !== '2.0') fail('Project semantic knowledge Wiki plan schema 2.0 is required.');
62
- if (clean(semanticPlan.source_snapshot_hash) !== snapshotHash) fail('Semantic plan snapshot hash does not match the asset package.');
63
+ if (clean(semanticPlan.source_snapshot_hash) !== snapshotHash && !allowSemanticPlanSnapshotDrift) {
64
+ fail('Semantic plan snapshot hash does not match the asset package.');
65
+ }
63
66
  if (clean(semanticPlan.generation_method) !== 'agent_semantic_synthesis') {
64
67
  fail('Semantic plan generation_method must be agent_semantic_synthesis.');
65
68
  }
@@ -84,6 +87,7 @@ for (const domain of domains) for (const id of asArray(domain.dashboard_ids).map
84
87
  for (const appendix of appendices) for (const id of asArray(appendix.dashboard_ids).map(clean)) appendixByDashboardId.set(id, appendix);
85
88
 
86
89
  const planModel = validateSemanticPlan();
90
+ const semanticPlanState = buildSemanticPlanState();
87
91
  const scratch = fs.mkdtempSync(path.join(os.tmpdir(), 'project-semantic-knowledge-wiki-'));
88
92
  const buildIr = path.join(scratch, 'build-ir');
89
93
 
@@ -92,6 +96,7 @@ try {
92
96
  const generatorArgs = [generator, '--asset-package', packageRoot, '--domain-plan', semanticPlanPath,
93
97
  '--output', buildIr, '--project-name', projectName];
94
98
  if (allowAllVisible) generatorArgs.push('--allow-all-visible');
99
+ if (allowSemanticPlanSnapshotDrift) generatorArgs.push('--allow-semantic-plan-snapshot-drift');
95
100
  execFileSync(process.execPath, generatorArgs, { stdio: 'pipe' });
96
101
 
97
102
  const corpus = readJson(path.join(buildIr, 'corpus.json'));
@@ -117,7 +122,7 @@ try {
117
122
  const id = clean(domain.domain_id);
118
123
  const source = readText(path.join(buildIr, 'domains', fileName('domain', id)));
119
124
  const cards = asArray(domain.recall_cards);
120
- const cardLinks = cards.map((card) => `- [${escapeLink(card.questions?.[0] || card.intent || card.card_id)}](../recall-cards/${fileName('recall-card', card.card_id)})`).join('\n');
125
+ const cardLinks = cards.map((card) => `- [${escapeLink(card.intent || card.card_id)}](../recall-cards/${fileName('recall-card', card.card_id)})`).join('\n');
121
126
  writeText(path.join(wikiRoot, 'domains', fileName('domain', id)),
122
127
  replaceFrontmatter(source, domainFrontmatter(domain))
123
128
  + `\n## 场景召回卡\n\n${cardLinks || '- 无'}\n`);
@@ -201,6 +206,7 @@ try {
201
206
  writeText(path.join(wikiRoot, 'index.md'), renderWikiIndex(corpus));
202
207
  writeText(path.join(wikiRoot, 'log.md'), renderLog(corpus));
203
208
  writeText(path.join(staging, 'index.md'), renderSnapshotIndex(corpus));
209
+ writeText(path.join(staging, 'refresh-state.md'), renderRefreshState(semanticPlanState));
204
210
 
205
211
  const sourceProjectRefs = sourceProjectReferencesFromPackage();
206
212
  const sourceProjectIds = [...new Set(sourceProjectRefs.map((row) => row.source_project_id))].sort((a, b) => a - b);
@@ -215,7 +221,9 @@ try {
215
221
  source_project_id_status: sourceProjectRefs.length ? 'detected_in_packaged_definitions' : 'not_exposed',
216
222
  project_name: projectName,
217
223
  source_snapshot_hash: snapshotHash,
218
- semantic_plan_hash: sha256(fs.readFileSync(semanticPlanPath)),
224
+ semantic_plan_hash: semanticPlanState.plan_hash,
225
+ semantic_plan_state_schema_version: semanticPlanState.schema_version,
226
+ semantic_plan_fragment_count: semanticPlanState.fragments.length,
219
227
  asset_package_schema_version: '3.0',
220
228
  asset_scope: packageAssetScope,
221
229
  wiki_schema_version: '1.0',
@@ -265,6 +273,8 @@ function validateSemanticPlan() {
265
273
  const cardIds = new Set();
266
274
  const excludedByAsset = new Map();
267
275
  const preferredByAsset = new Map();
276
+ const domainIds = new Set(domains.map((domain) => clean(domain.domain_id)).filter(Boolean));
277
+ const projectBusinessModel = validateProjectBusinessModel(semanticPlan.project_business_model, domainIds);
268
278
  for (const domain of domains) {
269
279
  const domainId = clean(domain.domain_id);
270
280
  const domainDashboardIds = new Set(asArray(domain.dashboard_ids).map(clean));
@@ -272,6 +282,7 @@ function validateSemanticPlan() {
272
282
  .some((ref) => domainDashboardIds.has(clean(ref.dashboard_id)))).map((report) => clean(report.resource_key)));
273
283
  const domainMetricIds = new Set(reports.filter((report) => domainReportIds.has(clean(report.resource_key)))
274
284
  .flatMap((report) => asArray(report.metric_occurrences).map((item) => clean(item.metric_key)).filter(Boolean)));
285
+ validateDomainJudgmentModel(domain.domain_judgment_model, domainId, domainDashboardIds, domainReportIds, domainMetricIds);
275
286
  const recallCards = asArray(domain.recall_cards);
276
287
  if (!recallCards.length) fail(`Domain ${domainId || '<unknown>'} must contain at least one recall card.`);
277
288
  for (const value of recallCards) {
@@ -334,7 +345,138 @@ function validateSemanticPlan() {
334
345
  fail(`Semantic plan is missing SQL semantics for ${missingSql.length} report(s): ${missingSql.join(', ')}`);
335
346
  }
336
347
 
337
- return { cards, excludedByAsset, preferredByAsset, sqlByReportId, missingSqlByReportId: new Set(missingSql) };
348
+ return {
349
+ cards,
350
+ excludedByAsset,
351
+ preferredByAsset,
352
+ sqlByReportId,
353
+ missingSqlByReportId: new Set(missingSql),
354
+ projectBusinessModel,
355
+ };
356
+ }
357
+
358
+ function validateProjectBusinessModel(value, domainIds) {
359
+ const model = asObject(value);
360
+ const businessPositioning = clean(model.business_positioning);
361
+ const audienceRoles = asArray(model.audience_roles).map(clean).filter(Boolean);
362
+ const businessObjects = asArray(model.business_objects).map((item, index) => {
363
+ const row = asObject(item);
364
+ const objectId = clean(row.object_id);
365
+ const name = clean(row.name);
366
+ const meaning = clean(row.meaning);
367
+ if (!/^[a-z0-9][a-z0-9_-]{1,79}$/.test(objectId)) fail(`project_business_model.business_objects[${index}] has invalid object_id.`);
368
+ if (!name || !meaning) fail(`project_business_model.business_objects[${index}] requires name and meaning.`);
369
+ const evidenceRefs = normalizePlanAssetRefs(row.evidence_refs, `business object ${objectId}`);
370
+ if (!evidenceRefs.length) fail(`business object ${objectId} requires evidence_refs.`);
371
+ return { object_id: objectId, name, meaning, evidence_refs: evidenceRefs };
372
+ });
373
+ const objectNames = new Set(businessObjects.map((item) => item.name));
374
+ const objectRelationships = asArray(model.object_relationships).map((item, index) => {
375
+ const row = asObject(item);
376
+ const fromObject = clean(row.from_object);
377
+ const toObject = clean(row.to_object);
378
+ const relationship = clean(row.relationship);
379
+ if (!fromObject || !toObject || !relationship) {
380
+ fail(`project_business_model.object_relationships[${index}] requires from_object, to_object and relationship.`);
381
+ }
382
+ if (!objectNames.has(fromObject) || !objectNames.has(toObject)) {
383
+ fail(`object relationship ${fromObject || '<empty>'}->${toObject || '<empty>'} must reference declared business object names.`);
384
+ }
385
+ const evidenceRefs = normalizePlanAssetRefs(row.evidence_refs, `object relationship ${fromObject}->${toObject}`);
386
+ if (!evidenceRefs.length) fail(`object relationship ${fromObject}->${toObject} requires evidence_refs.`);
387
+ return { from_object: fromObject, to_object: toObject, relationship, evidence_refs: evidenceRefs };
388
+ });
389
+ const decisionChains = asArray(model.decision_chains).map((item, index) => {
390
+ const row = asObject(item);
391
+ const chainId = clean(row.chain_id);
392
+ const title = clean(row.title);
393
+ const decisionQuestion = clean(row.decision_question);
394
+ const signals = asArray(row.signals).map(clean).filter(Boolean);
395
+ const decisionSteps = asArray(row.decision_steps).map(clean).filter(Boolean);
396
+ const preferredDomainIds = asArray(row.preferred_domain_ids).map(clean).filter(Boolean);
397
+ const assetRefs = normalizePlanAssetRefs(row.asset_refs, `decision chain ${chainId || index + 1}`);
398
+ const boundaries = asArray(row.boundaries).map(clean).filter(Boolean);
399
+ if (!/^[a-z0-9][a-z0-9_-]{1,79}$/.test(chainId)) fail(`project_business_model.decision_chains[${index}] has invalid chain_id.`);
400
+ if (!title || !decisionQuestion) fail(`decision chain ${chainId} requires title and decision_question.`);
401
+ if (signals.length < 2) fail(`decision chain ${chainId} requires at least two signals.`);
402
+ if (decisionSteps.length < 2) fail(`decision chain ${chainId} requires at least two decision_steps.`);
403
+ if (!preferredDomainIds.length || preferredDomainIds.some((id) => !domainIds.has(id))) {
404
+ fail(`decision chain ${chainId} must reference existing preferred_domain_ids.`);
405
+ }
406
+ if (!assetRefs.length) fail(`decision chain ${chainId} requires asset_refs.`);
407
+ if (!boundaries.length) fail(`decision chain ${chainId} requires boundaries.`);
408
+ return {
409
+ chain_id: chainId,
410
+ title,
411
+ decision_question: decisionQuestion,
412
+ signals,
413
+ decision_steps: decisionSteps,
414
+ preferred_domain_ids: preferredDomainIds,
415
+ asset_refs: assetRefs,
416
+ requires_live_execution: Boolean(row.requires_live_execution),
417
+ boundaries,
418
+ };
419
+ });
420
+ const nonGoals = asArray(model.non_goals).map(clean).filter(Boolean);
421
+ if (!businessPositioning) fail('project_business_model requires business_positioning.');
422
+ if (!audienceRoles.length) fail('project_business_model requires audience_roles.');
423
+ if (!businessObjects.length) fail('project_business_model requires business_objects.');
424
+ if (!objectRelationships.length) fail('project_business_model requires object_relationships.');
425
+ if (!decisionChains.length) fail('project_business_model requires decision_chains.');
426
+ if (!nonGoals.length) fail('project_business_model requires non_goals.');
427
+ return {
428
+ business_positioning: businessPositioning,
429
+ audience_roles: audienceRoles,
430
+ business_objects: businessObjects,
431
+ object_relationships: objectRelationships,
432
+ decision_chains: decisionChains,
433
+ non_goals: nonGoals,
434
+ };
435
+ }
436
+
437
+ function validateDomainJudgmentModel(value, domainId, domainDashboardIds, domainReportIds, domainMetricIds) {
438
+ const model = asObject(value);
439
+ const businessStateJudged = clean(model.business_state_judged);
440
+ const mainObjects = asArray(model.main_objects).map(clean).filter(Boolean);
441
+ const signals = asArray(model.signals).map(clean).filter(Boolean);
442
+ const decisionPath = asArray(model.decision_path).map(clean).filter(Boolean);
443
+ const boundaries = asArray(model.boundaries).map(clean).filter(Boolean);
444
+ const attachmentLogic = asArray(model.asset_attachment_logic);
445
+ if (!businessStateJudged) fail(`Domain ${domainId} is missing domain_judgment_model.business_state_judged.`);
446
+ if (!mainObjects.length) fail(`Domain ${domainId} is missing domain_judgment_model.main_objects.`);
447
+ if (signals.length < 2) fail(`Domain ${domainId} domain_judgment_model requires at least two signals.`);
448
+ if (decisionPath.length < 2) fail(`Domain ${domainId} domain_judgment_model requires at least two decision_path steps.`);
449
+ if (!attachmentLogic.length) fail(`Domain ${domainId} domain_judgment_model requires asset_attachment_logic.`);
450
+ if (!boundaries.length) fail(`Domain ${domainId} domain_judgment_model requires boundaries.`);
451
+ for (const [index, item] of attachmentLogic.entries()) {
452
+ const row = asObject(item);
453
+ const ref = normalizePlanAssetRef(row.asset_ref, `domain ${domainId} asset_attachment_logic[${index}]`);
454
+ const role = clean(row.role);
455
+ const reason = clean(row.reason);
456
+ if (!['primary_entry', 'supporting_evidence', 'drilldown', 'exclusion'].includes(role)) {
457
+ fail(`domain ${domainId} asset_attachment_logic[${index}] has invalid role.`);
458
+ }
459
+ if (!reason || reason.length < 10) fail(`domain ${domainId} asset_attachment_logic[${index}] requires concrete reason.`);
460
+ const inDomain = ref.resource_type === 'dashboard' ? domainDashboardIds.has(ref.resource_key)
461
+ : ref.resource_type === 'report' ? domainReportIds.has(ref.resource_key)
462
+ : ref.resource_type === 'metric' ? domainMetricIds.has(ref.resource_key) : false;
463
+ if (!inDomain) fail(`domain ${domainId} asset_attachment_logic references ${ref.resource_type}:${ref.resource_key} outside domain closure.`);
464
+ }
465
+ }
466
+
467
+ function normalizePlanAssetRefs(values, label) {
468
+ return asArray(values).map((value, index) => normalizePlanAssetRef(value, `${label} asset_refs[${index}]`));
469
+ }
470
+
471
+ function normalizePlanAssetRef(value, label) {
472
+ const ref = asObject(value);
473
+ const type = clean(ref.resource_type);
474
+ const key = clean(ref.resource_key);
475
+ const exists = type === 'dashboard' ? dashboardById.has(key)
476
+ : type === 'report' ? reportById.has(key)
477
+ : type === 'metric' ? metricById.has(key) : false;
478
+ if (!exists) fail(`${label} references unknown asset ${type || '<type>'}:${key || '<key>'}.`);
479
+ return { resource_type: type, resource_key: key };
338
480
  }
339
481
 
340
482
  function assertAgentAuthoredRecallCard(card, domainId) {
@@ -436,8 +578,36 @@ function writeMergedAsset(wikiRoot, directory, asset, authority, extra = '') {
436
578
  const brief = readText(path.join(buildIr, 'briefs', directory, fileName(directory.slice(0, -1), id)));
437
579
  const type = directory === 'dashboards' ? 'project-dashboard' : directory === 'reports' ? 'project-report' : 'project-reusable-metric';
438
580
  const title = assetReadableTitle(asset, directory.slice(0, -1));
581
+ const authorityNotice = renderAssetAuthorityNotice(authority);
582
+ const mergedExtra = [authorityNotice, extra.trim()].filter(Boolean).join('\n\n');
439
583
  writeText(path.join(wikiRoot, directory, assetOutputFileName(directory, id)),
440
- ensureReadableHeading(mergePage(source, brief, authorityFrontmatter(type, id, title, authority), extra), title));
584
+ ensureReadableHeading(mergePage(source, brief, authorityFrontmatter(type, id, title, authority), mergedExtra), title));
585
+ }
586
+
587
+ function renderAssetAuthorityNotice(authority) {
588
+ const runtimePolicy = clean(authority.runtime_policy);
589
+ const definitionState = clean(authority.definition_state);
590
+ const authorityRole = clean(authority.authority_role);
591
+ const canonical = clean(authority.canonical_asset_id);
592
+ const lines = [];
593
+ if (runtimePolicy === 'block') {
594
+ lines.push('- 召回限制:该资产不能作为直接回答入口。');
595
+ } else if (runtimePolicy === 'warn') {
596
+ lines.push('- 召回限制:该资产只能作为辅助线索,回答时需要说明其依赖或待确认边界。');
597
+ }
598
+ if (authorityRole === 'dependency_only') {
599
+ lines.push('- 权威边界:该资产来自依赖闭包,只能支撑承载看板或业务域,不能单独代表正式业务入口。');
600
+ }
601
+ if (definitionState === 'conflict') {
602
+ lines.push('- 口径状态:存在同名或近义资产口径冲突,应优先回到业务域、召回卡或让用户确认。');
603
+ } else if (definitionState === 'unknown') {
604
+ lines.push('- 口径状态:定义证据不足,只能作为待核验线索。');
605
+ }
606
+ if (canonical) {
607
+ lines.push(`- 推荐入口:优先使用 \`${canonical}\`,本页只保留来源和排除理由。`);
608
+ }
609
+ if (!lines.length) return '';
610
+ return `## 召回与权威边界\n\n${lines.join('\n')}`;
441
611
  }
442
612
 
443
613
  function mergePage(source, brief, frontmatter, extra = '') {
@@ -525,7 +695,7 @@ function recallAssetLabel(ref) {
525
695
  }
526
696
 
527
697
  function renderRecallCardIndex(cards) {
528
- return `# 场景召回卡\n\n首轮召回先使用这里的业务问题与权威资产路由,不直接宽搜全部资产页。\n\n${cards.map((card) => `- [${escapeLink(card.questions?.[0] || card.intent || card.card_id)}](${fileName('recall-card', card.card_id)})(${clean(card.domain_title)})`).join('\n')}\n`;
698
+ return `# 场景召回卡\n\n首轮召回先使用这里的业务问题与权威资产路由,不直接宽搜全部资产页。\n\n${cards.map((card) => `- [${escapeLink(card.intent || card.card_id)}](${fileName('recall-card', card.card_id)})(${clean(card.domain_title)})`).join('\n')}\n`;
529
699
  }
530
700
 
531
701
  function renderAuthorityIndex(rows) {
@@ -555,16 +725,48 @@ function renderGovernanceIndex(conflicts) {
555
725
  }
556
726
 
557
727
  function renderWikiIndex(corpus) {
558
- return `---\ntype: project-semantic-knowledge-wiki-index\nsemantic_project_id: ${semanticProjectId}\nproject_name: ${yaml(projectName)}\nsource_snapshot_hash: ${yaml(snapshotHash)}\n---\n\n# ${projectName} 项目语义知识库 Wiki\n\n## 查询顺序\n\n1. [场景召回卡](recall-cards/index.md)\n2. [业务域](domains/index.md)\n3. [看板](dashboards/index.md)、[报表](reports/index.md)、[可复用指标](metrics/index.md)\n4. [分析元数据](metadata/index.md)\n5. [治理与质量](governance/index.md)\n\n技术、演示、副本和定向诊断资产只在[技术附录](technical-appendices/index.md)中追溯。物理空间在[source containers](source-containers/index.md)中保留,不作为业务域。\n\n${countSummary(corpus)}\n`;
728
+ return `---\ntype: project-semantic-knowledge-wiki-index\nsemantic_project_id: ${semanticProjectId}\nproject_name: ${yaml(projectName)}\nsource_snapshot_hash: ${yaml(snapshotHash)}\n---\n\n# ${projectName} 项目语义知识库 Wiki\n\n## 查询顺序\n\n1. [项目业务模型](project-overview.md)\n2. [场景召回卡](recall-cards/index.md)\n3. [业务域](domains/index.md)\n4. [看板](dashboards/index.md)、[报表](reports/index.md)、[可复用指标](metrics/index.md)\n5. [分析元数据](metadata/index.md)\n6. [治理与质量](governance/index.md)\n\n技术、演示、副本和定向诊断资产只在[技术附录](technical-appendices/index.md)中追溯。物理空间在[source containers](source-containers/index.md)中保留,不作为业务域。\n\n${countSummary(corpus)}\n`;
559
729
  }
560
730
 
561
731
  function renderSnapshotIndex(corpus) {
562
732
  const title = String(projectName).replace(/\r?\n/g, ' ').replace(/^#+\s*/, '');
563
- return `# ${title} 项目语义知识库 Wiki\n\n这是可导入知识库源文件体系的项目语义 Wiki,共包含 ${corpus.counts.dashboards} 张看板、${corpus.counts.reports} 张报表和 ${corpus.counts.analysis_metadata} 条分析元数据。\n\n## 查询顺序\n\n1. [场景召回卡](wiki/recall-cards/index.md)\n2. [业务域](wiki/domains/index.md)\n3. [看板](wiki/dashboards/index.md)、[报表](wiki/reports/index.md)、[可复用指标](wiki/metrics/index.md)\n4. [分析元数据](wiki/metadata/index.md)\n5. [治理与质量](wiki/governance/index.md)\n\n技术、演示、副本和定向诊断资产只在[技术附录](wiki/technical-appendices/index.md)中追溯。物理空间在[source containers](wiki/source-containers/index.md)中保留,不作为业务域。\n\n${countSummary(corpus)}\n`;
733
+ return `# ${title} 项目语义知识库 Wiki\n\n这是可导入知识库源文件体系的项目语义 Wiki,共包含 ${corpus.counts.dashboards} 张看板、${corpus.counts.reports} 张报表和 ${corpus.counts.analysis_metadata} 条分析元数据。\n\n## 查询顺序\n\n1. [项目业务模型](wiki/project-overview.md)\n2. [场景召回卡](wiki/recall-cards/index.md)\n3. [业务域](wiki/domains/index.md)\n4. [看板](wiki/dashboards/index.md)、[报表](wiki/reports/index.md)、[可复用指标](wiki/metrics/index.md)\n5. [分析元数据](wiki/metadata/index.md)\n6. [治理与质量](wiki/governance/index.md)\n\n技术、演示、副本和定向诊断资产只在[技术附录](wiki/technical-appendices/index.md)中追溯。物理空间在[source containers](wiki/source-containers/index.md)中保留,不作为业务域。\n\n${countSummary(corpus)}\n`;
564
734
  }
565
735
 
566
736
  function renderProjectOverview(corpus) {
567
- return `# ${projectName} 项目语义知识库概览\n\n- Semantic project ID: ${semanticProjectId}\n- Snapshot hash: \`${snapshotHash}\`\n- Asset scope: ${packageAssetScope}\n- Business domains: ${corpus.counts.business_domains}\n- Dashboards: ${corpus.counts.dashboards}\n- Reports: ${corpus.counts.reports}\n- Reusable metrics: ${corpus.counts.reusable_metrics}\n- Metadata: ${corpus.counts.analysis_metadata}\n\nCommon 资产包是权威来源。本 Wiki 将用途总结和精确证据合并为单一页面,不再发布第二套 raw/briefs 语料。\n`;
737
+ return `# ${projectName} 项目业务模型\n\n${renderProjectBusinessModel(planModel.projectBusinessModel)}\n\n## 资产与治理范围\n\n- Semantic project ID: ${semanticProjectId}\n- Snapshot hash: \`${snapshotHash}\`\n- Asset scope: ${packageAssetScope}\n- Business domains: ${corpus.counts.business_domains}\n- Dashboards: ${corpus.counts.dashboards}\n- Reports: ${corpus.counts.reports}\n- Reusable metrics: ${corpus.counts.reusable_metrics}\n- Metadata: ${corpus.counts.analysis_metadata}\n\nCommon 资产包是权威来源。本 Wiki 将业务判断模型、用途总结和精确证据合并为单一页面,不再发布第二套 raw/briefs 语料。\n`;
738
+ }
739
+
740
+ function renderProjectBusinessModel(model) {
741
+ return `## 项目定位\n\n${clean(model.business_positioning)}\n\n## 使用角色\n\n${asArray(model.audience_roles).map((role) => `- ${clean(role)}`).join('\n')}\n\n## 业务对象\n\n${asArray(model.business_objects).map((item) => {
742
+ const evidence = asArray(item.evidence_refs).map(renderOverviewAssetRef).join('、');
743
+ return `- ${clean(item.name)}(\`${clean(item.object_id)}\`):${appendEvidenceSentence(item.meaning, evidence)}`;
744
+ }).join('\n')}\n\n## 对象关系\n\n${asArray(model.object_relationships).map((item) => {
745
+ const evidence = asArray(item.evidence_refs).map(renderOverviewAssetRef).join('、');
746
+ return `- ${clean(item.from_object)} -> ${clean(item.to_object)}:${appendEvidenceSentence(item.relationship, evidence)}`;
747
+ }).join('\n')}\n\n## 业务判断链路\n\n${asArray(model.decision_chains).map(renderDecisionChain).join('\n\n')}\n\n## 不承载的内容\n\n${asArray(model.non_goals).map((item) => `- ${clean(item)}`).join('\n')}`;
748
+ }
749
+
750
+ function appendEvidenceSentence(value, evidence) {
751
+ const body = clean(value).replace(/[。;;,,\s]+$/u, '');
752
+ return `${body}${evidence ? `。证据:${evidence}` : ''}`;
753
+ }
754
+
755
+ function renderDecisionChain(chain) {
756
+ const domains = asArray(chain.preferred_domain_ids)
757
+ .map((id) => `[\`${clean(id)}\`](domains/${fileName('domain', id)})`).join('、');
758
+ const assets = asArray(chain.asset_refs).map(renderOverviewAssetRef).join('、');
759
+ return `### ${clean(chain.title)}\n\n- 判断问题:${stripClausePunctuation(chain.decision_question)}\n- 判断信号:${joinChineseClauses(chain.signals)}\n- 判断步骤:\n${renderNumberedSteps(chain.decision_steps, ' ')}\n- 业务域:${domains || '未指定'}\n- 关联资产:${assets || '未指定'}\n- 实时执行:${chain.requires_live_execution ? '需要' : '不需要'}\n- 边界:${joinChineseClauses(chain.boundaries)}`;
760
+ }
761
+
762
+ function renderOverviewAssetRef(ref) {
763
+ const type = clean(ref.resource_type);
764
+ const key = clean(ref.resource_key);
765
+ const label = recallAssetLabel(ref);
766
+ if (type === 'dashboard') return `[${escapeLink(label)}](dashboards/${dashboardFileNames.get(key) ?? fileName('dashboard', key)})`;
767
+ if (type === 'report') return `[${escapeLink(label)}](reports/${reportFileNames.get(key) ?? fileName('report', key)})`;
768
+ if (type === 'metric') return `[${escapeLink(label)}](metrics/${metricFileNames.get(key) ?? fileName('metric', key)})`;
769
+ return `${type}:${key}`;
568
770
  }
569
771
 
570
772
  function renderLog(corpus) {
@@ -744,6 +946,7 @@ function hideMarkdownFrontmatter(root) {
744
946
  function assertSnapshotEntryPoint(root) {
745
947
  const content = readText(path.join(root, 'index.md'));
746
948
  const requiredLinks = [
949
+ 'wiki/project-overview.md',
747
950
  'wiki/recall-cards/index.md', 'wiki/domains/index.md', 'wiki/dashboards/index.md',
748
951
  'wiki/reports/index.md', 'wiki/metrics/index.md', 'wiki/metadata/index.md',
749
952
  'wiki/governance/index.md', 'wiki/technical-appendices/index.md',
@@ -876,6 +1079,7 @@ function writeFlatUploadSources(sourceRoot, targetRoot, replace) {
876
1079
  const content = precompiledSource(rewriteLinksForFlatUpload(readText(file), rel, relToFlat));
877
1080
  const contentHash = syncContentHash(content);
878
1081
  const identity = uploadSourceIdentity(rel, content);
1082
+ const semanticPlanFragmentRefs = semanticPlanFragmentRefsForSource(identity, rel);
879
1083
  manifestSources.push({
880
1084
  display_name: flatName,
881
1085
  file: flatName,
@@ -888,8 +1092,9 @@ function writeFlatUploadSources(sourceRoot, targetRoot, replace) {
888
1092
  title: firstHeading(content),
889
1093
  content_hash: contentHash,
890
1094
  source_path: rel,
1095
+ semantic_plan_fragment_refs: semanticPlanFragmentRefs,
891
1096
  });
892
- writeText(path.join(staging, flatName), `<!--\nkb_source_namespace: ${uploadSourceNamespace}\nsnapshot_date: ${snapshotDate ?? 'null'}\nsnapshot_hash: ${snapshotHash}\nsource_kind: ${identity.source_kind}\nasset_type: ${identity.asset_type ?? 'null'}\nasset_key: ${identity.asset_key ?? 'null'}\ncontent_hash: ${contentHash}\nsource_path: ${rel}\n-->\n\n${content}`);
1097
+ writeText(path.join(staging, flatName), `<!--\nkb_source_namespace: ${uploadSourceNamespace}\nsnapshot_date: ${snapshotDate ?? 'null'}\nsnapshot_hash: ${snapshotHash}\nsource_kind: ${identity.source_kind}\nasset_type: ${identity.asset_type ?? 'null'}\nasset_key: ${identity.asset_key ?? 'null'}\ncontent_hash: ${contentHash}\nsource_path: ${rel}\nsemantic_plan_fragment_refs: ${semanticPlanFragmentRefs.join(',') || 'null'}\n-->\n\n${content}`);
893
1098
  }
894
1099
  const manifest = readJson(path.join(sourceRoot, 'manifest.json'));
895
1100
  const manifestContent = precompiledSource(`# Package manifest\n\n\`\`\`json\n${JSON.stringify(manifest, null, 2)}\n\`\`\`\n`);
@@ -907,8 +1112,9 @@ function writeFlatUploadSources(sourceRoot, targetRoot, replace) {
907
1112
  title: 'Package manifest',
908
1113
  content_hash: manifestHash,
909
1114
  source_path: 'manifest.json',
1115
+ semantic_plan_fragment_refs: [],
910
1116
  });
911
- writeText(path.join(staging, manifestName), `<!--\nkb_source_namespace: ${uploadSourceNamespace}\nsnapshot_date: ${snapshotDate ?? 'null'}\nsnapshot_hash: ${snapshotHash}\nsource_kind: manifest\nasset_type: null\nasset_key: null\ncontent_hash: ${manifestHash}\nsource_path: manifest.json\n-->\n\n${manifestContent}`);
1117
+ writeText(path.join(staging, manifestName), `<!--\nkb_source_namespace: ${uploadSourceNamespace}\nsnapshot_date: ${snapshotDate ?? 'null'}\nsnapshot_hash: ${snapshotHash}\nsource_kind: manifest\nasset_type: null\nasset_key: null\ncontent_hash: ${manifestHash}\nsource_path: manifest.json\nsemantic_plan_fragment_refs: null\n-->\n\n${manifestContent}`);
912
1118
  writeJson(path.join(staging, 'kb-upload-manifest.json'), {
913
1119
  schema_version: '1.0',
914
1120
  manifest_kind: 'ae_project_semantic_kb_upload_sources',
@@ -917,6 +1123,7 @@ function writeFlatUploadSources(sourceRoot, targetRoot, replace) {
917
1123
  project_name: projectName,
918
1124
  snapshot_date: snapshotDate,
919
1125
  snapshot_hash: snapshotHash,
1126
+ semantic_plan_state: semanticPlanState,
920
1127
  source_count: manifestSources.length,
921
1128
  sources: manifestSources.sort((a, b) => a.display_name.localeCompare(b.display_name, 'en')),
922
1129
  });
@@ -940,6 +1147,7 @@ function flatUploadFileName(rel, namespace) {
940
1147
  function uploadSourceIdentity(rel, content = '') {
941
1148
  const normalized = rel.replaceAll('\\', '/');
942
1149
  if (normalized === 'index.md') return { source_kind: 'index', asset_type: null, asset_key: null, file_stem: 'index' };
1150
+ if (normalized === 'refresh-state.md') return { source_kind: 'refresh-state', asset_type: null, asset_key: null, file_stem: 'refresh-state' };
943
1151
  if (normalized === 'wiki/index.md') return { source_kind: 'index', asset_type: null, asset_key: null, file_stem: 'wiki-index' };
944
1152
  const match = normalized.match(/^wiki\/([^/]+)\/(.+)\.md$/);
945
1153
  if (!match) {
@@ -966,6 +1174,108 @@ function uploadSourceIdentity(rel, content = '') {
966
1174
  return namedUploadSource(section, stem);
967
1175
  }
968
1176
 
1177
+ function semanticPlanFragmentRefsForSource(identity, rel) {
1178
+ const refs = new Set();
1179
+ const sourceKind = clean(identity.source_kind);
1180
+ const assetType = clean(identity.asset_type);
1181
+ const assetKey = clean(identity.asset_key);
1182
+ if (sourceKind === 'refresh-state') {
1183
+ for (const fragment of semanticPlanState.fragments) refs.add(fragment.id);
1184
+ return [...refs].sort();
1185
+ }
1186
+ if (rel === 'wiki/project-overview.md') refs.add('project:model');
1187
+ if (sourceKind === 'domain' && assetKey) refs.add(`domain:${assetKey}`);
1188
+ if (sourceKind === 'appendix' && assetKey) refs.add(`appendix:${assetKey}`);
1189
+ if (sourceKind === 'recall-card' && assetKey) refs.add(`recall-card:${assetKey}`);
1190
+ if (assetType === 'dashboard' && assetKey) {
1191
+ const domain = domainByDashboardId.get(assetKey);
1192
+ const appendix = appendixByDashboardId.get(assetKey);
1193
+ if (domain) refs.add(`domain:${clean(domain.domain_id)}`);
1194
+ if (appendix) refs.add(`appendix:${clean(appendix.appendix_id)}`);
1195
+ }
1196
+ if (assetType === 'report' && assetKey) {
1197
+ if (planModel.sqlByReportId.has(assetKey)) refs.add(`report:${assetKey}:sql_semantics`);
1198
+ const report = reportById.get(assetKey);
1199
+ for (const dashboardRef of asArray(report?.dashboard_refs)) {
1200
+ const dashboardId = clean(dashboardRef.dashboard_id);
1201
+ const domain = domainByDashboardId.get(dashboardId);
1202
+ const appendix = appendixByDashboardId.get(dashboardId);
1203
+ if (domain) refs.add(`domain:${clean(domain.domain_id)}`);
1204
+ if (appendix) refs.add(`appendix:${clean(appendix.appendix_id)}`);
1205
+ }
1206
+ }
1207
+ if (sourceKind === 'index' && rel === 'wiki/recall-cards/index.md') {
1208
+ for (const card of planModel.cards) refs.add(`recall-card:${clean(card.card_id)}`);
1209
+ }
1210
+ if (sourceKind === 'index' && rel === 'wiki/domains/index.md') {
1211
+ for (const domain of domains) refs.add(`domain:${clean(domain.domain_id)}`);
1212
+ }
1213
+ return [...refs].filter(Boolean).sort();
1214
+ }
1215
+
1216
+ function buildSemanticPlanState() {
1217
+ const fragments = [];
1218
+ const add = (id, kind, value, fields = {}) => {
1219
+ const fragmentId = clean(id);
1220
+ if (!fragmentId) return;
1221
+ fragments.push({
1222
+ id: fragmentId,
1223
+ kind,
1224
+ ...fields,
1225
+ hash: sha256(stableJson(value)),
1226
+ });
1227
+ };
1228
+ add('project:model', 'project', semanticPlan.project_business_model ?? null);
1229
+ for (const domain of domains) {
1230
+ const id = clean(domain.domain_id);
1231
+ const { recall_cards: _recallCards, ...domainWithoutCards } = asObject(domain);
1232
+ add(`domain:${id}`, 'domain', domainWithoutCards, { domain_id: id });
1233
+ for (const card of asArray(domain.recall_cards)) {
1234
+ const cardId = clean(card.card_id);
1235
+ add(`recall-card:${cardId}`, 'recall-card', { ...asObject(card), domain_id: id }, { card_id: cardId, domain_id: id });
1236
+ }
1237
+ }
1238
+ for (const appendix of appendices) {
1239
+ const id = clean(appendix.appendix_id);
1240
+ add(`appendix:${id}`, 'appendix', appendix, { appendix_id: id });
1241
+ }
1242
+ for (const [reportId, summary] of [...planModel.sqlByReportId.entries()].sort(([left], [right]) => left.localeCompare(right, 'en'))) {
1243
+ add(`report:${reportId}:sql_semantics`, 'sql-report', summary, { report_id: reportId });
1244
+ }
1245
+ fragments.sort((left, right) => left.id.localeCompare(right.id, 'en'));
1246
+ return {
1247
+ schema_version: '1.0',
1248
+ plan_schema_version: clean(semanticPlan.schema_version),
1249
+ plan_hash: sha256(stableJson(semanticPlan)),
1250
+ fragment_count: fragments.length,
1251
+ fragments,
1252
+ };
1253
+ }
1254
+
1255
+ function renderRefreshState(state) {
1256
+ const payload = {
1257
+ schema_version: '1.0',
1258
+ semantic_plan: semanticPlan,
1259
+ semantic_plan_state: state,
1260
+ };
1261
+ const encoded = Buffer.from(JSON.stringify(payload, null, 2)).toString('base64url');
1262
+ return `# Refresh State
1263
+
1264
+ This managed source stores the reusable semantic plan and incremental-refresh state for generated project semantic sources. It is not business evidence.
1265
+
1266
+ <!-- pskb_refresh_state_base64
1267
+ ${encoded}
1268
+ -->
1269
+ `;
1270
+ }
1271
+
1272
+ function stableJson(value) {
1273
+ if (Array.isArray(value)) return `[${value.map(stableJson).join(',')}]`;
1274
+ if (!value || typeof value !== 'object') return JSON.stringify(value);
1275
+ const entries = Object.entries(value).sort(([left], [right]) => left.localeCompare(right, 'en'));
1276
+ return `{${entries.map(([key, item]) => `${JSON.stringify(key)}:${stableJson(item)}`).join(',')}}`;
1277
+ }
1278
+
969
1279
  function namedUploadSource(kind, stem, resourceKey = '') {
970
1280
  const key = stem === 'index' ? null : clean(resourceKey) || stem;
971
1281
  return {
@@ -1027,11 +1337,26 @@ function asObject(value) { return value && typeof value === 'object' && !Array.i
1027
1337
  function indexBy(values, key) { return new Map(values.map((value) => [clean(value[key]), value])); }
1028
1338
  function readText(file) { return fs.readFileSync(file, 'utf8'); }
1029
1339
  function readJson(file) { return JSON.parse(readText(file)); }
1340
+
1341
+ function stripClausePunctuation(value) {
1342
+ return clean(value).replace(/[。;;,,\s]+$/u, '');
1343
+ }
1344
+
1345
+ function joinChineseClauses(value) {
1346
+ const values = asArray(value).map(stripClausePunctuation).filter(Boolean);
1347
+ return values.join(';') || '无';
1348
+ }
1349
+
1350
+ function renderNumberedSteps(value, indent = '') {
1351
+ const values = asArray(value).map(stripClausePunctuation).filter(Boolean);
1352
+ if (!values.length) return `${indent}- 无`;
1353
+ return values.map((item, index) => `${indent}${index + 1}. ${item}`).join('\n');
1354
+ }
1030
1355
  function writeText(file, value) { fs.mkdirSync(path.dirname(file), { recursive: true }); fs.writeFileSync(file, value.endsWith('\n') ? value : `${value}\n`, 'utf8'); }
1031
1356
  function writeJson(file, value) { writeText(file, JSON.stringify(value, null, 2)); }
1032
1357
  function readIndex(name) { return readText(path.join(packageRoot, 'indexes', `${name}.jsonl`)).split(/\r?\n/).filter(Boolean).map(JSON.parse).filter((row) => row.record_type !== 'header'); }
1033
1358
  function listFiles(root) { return fs.readdirSync(root, { withFileTypes: true }).flatMap((entry) => entry.isDirectory() ? listFiles(path.join(root, entry.name)) : [path.join(root, entry.name)]); }
1034
- function parseArgs(values) { const out = {}; for (let i = 0; i < values.length; i += 1) { const value = values[i]; if (!value.startsWith('--')) fail(`Unexpected argument: ${value}`); const key = value.slice(2); if (['force', 'keep-build-ir', 'allow-all-visible'].includes(key)) out[key] = true; else out[key] = values[++i]; } return out; }
1359
+ function parseArgs(values) { const out = {}; for (let i = 0; i < values.length; i += 1) { const value = values[i]; if (!value.startsWith('--')) fail(`Unexpected argument: ${value}`); const key = value.slice(2); if (['force', 'keep-build-ir', 'allow-all-visible', 'allow-semantic-plan-snapshot-drift'].includes(key)) out[key] = true; else out[key] = values[++i]; } return out; }
1035
1360
  function required(values, key) { const value = clean(values[key]); if (!value) fail(`--${key} is required.`); return value; }
1036
1361
  function normalizeUploadSourceNamespace(value) {
1037
1362
  const namespace = slug(clean(value));
@@ -7,6 +7,7 @@ These rules guide Schema generation for project semantic knowledge bases built f
7
7
  - Treat `sources/README.md` and category `README.md` files as filing guides. They define where future material belongs; they are not business evidence.
8
8
  - CLI-managed asset facts live under `sources/assets/**`. Human project background, business rules, meetings, decisions, corrections, and references live outside `sources/assets/**`; asset refresh must not delete or override those human inputs.
9
9
  - Organize generated Wiki pages for knowledge consumption, not by mirroring every source file. Use project overview, business domains, reusable semantics, assets, decisions, and review pages.
10
+ - The project overview must start with the project-level business model: business positioning, audience roles, business objects, object relationships, decision chains, asset roles in those chains, and non-goals. Asset counts, snapshot hashes, source package metadata and build logs belong after that model. Do not reduce the overview to a directory, asset inventory, or build summary.
10
11
  - Do not infer business meaning from titles alone. Preserve unknowns, conflicts, source scope, certification state, and provenance when evidence is incomplete.
11
12
  - Corrections and decisions override other material only when they state target, content, date, status, and applicable scope.
12
13
 
@@ -15,16 +16,20 @@ These rules guide Schema generation for project semantic knowledge bases built f
15
16
 
16
17
  The generated top-level `index.md` must be a decision-oriented navigation page. It may contain only module navigation, common-entry guidance, and a compact category/count overview. Do not append a complete page list, sitemap, individual asset files, metadata property pages, numeric report IDs, numeric dashboard IDs, source container IDs, or raw source filenames to the top-level page. Top-level navigation should link to project overview, business domains, recall cards, asset category indexes, decisions, and review pages. List individual assets only inside the matching asset index page, domain page, dashboard page, source-container page, or search result context. If the compiler wants to expose a full sitemap, put it under `wiki/index.md` or category index pages, never in root `index.md`.
17
18
 
18
- When linking to an individual asset, use its business title as the visible link text and keep the numeric ID only as supporting identity beside the title, for example `近期客户成员聊天数量(report 13270719)`. Do not render bare numeric links such as `13270719` as navigation items.
19
+ When linking to an individual asset, use its business title as the visible link text and keep the numeric ID only as supporting identity beside the title, for example `示例报表(report 1001)`. Do not render bare numeric links such as `1001` as navigation items.
19
20
 
20
21
  ## Precompiled source contract
21
22
 
22
23
  CLI Agent has already performed the main semantic compilation for ALL assets: business meanings, SQL interpretation, calculation definitions, relationships, domains, recall boundaries, and conflict findings. These are editable source documents, not an imported final Wiki snapshot. Reuse their complete prose and calculation facts. Do not repeat project-wide extraction, SQL parsing, semantic planning, or per-field rewriting.
23
24
 
24
- Business-domain pages include an `Agent 使用摘要` for first-hop routing. Preserve it as the concise answer source for "what does this domain solve", "which events/metrics are mainly used", and "which dimensions should the agent drill down by". Do not replace it with a raw report list or regenerate it from every report page unless newer scoped evidence explicitly changes the summary.
25
+ Business-domain pages include a judgment model and an `Agent 使用摘要` for first-hop routing. Preserve them as the concise answer source for "what business state does this domain judge", "which objects/signals/decision path should the Agent follow", "which assets are primary/supporting/drilldown/excluded", "what does this domain solve", "which events/metrics are mainly used", and "which dimensions should the agent drill down by". Do not replace them with a raw report list or regenerate them from every report page unless newer scoped evidence explicitly changes the summary.
26
+
27
+ Reject or repair any generated Wiki shape where the root, overview, or business-domain pages only list questions, events, metrics, dimensions, reports, dashboards or counts without explaining the business judgment chain that makes those assets useful. Examples in source instructions are illustrative only; never hardcode project-specific business domains, assets, metrics, event names or decision chains into the compiled Wiki.
25
28
 
26
29
  KB must compile and publish the final Wiki: preserve prepared asset sections, map source references to Wiki links, build concise titled navigation, and integrate project background, meetings, decisions and corrections. Batch independent page writes and link checks. Do not read each source repeatedly or regenerate unchanged pages.
27
30
 
28
- For SQL assets, preserve the complete Agent-authored purpose, parameters, output fields, grain, filters, limits, applicable/non-applicable questions and evidence locator in the report page's single `Agent 使用摘要`. Raw SQL/index JSON stays in the local evidence package; do not attempt to fetch it or infer omitted details. Missing facts remain unknown. Preserve certification, dependency-only status, conflicts and blocked recall; do not dismiss safety states as stale placeholders.
31
+ For SQL assets, preserve the complete Agent-authored purpose, parameters, output fields, grain, filters, limits, applicable/non-applicable questions and evidence locator in the report page's single `Agent 使用摘要`. Raw SQL/index JSON stays in the local evidence package; do not attempt to fetch it or infer omitted details. Missing facts remain unknown. Preserve certification, dependency-only status, conflicts and blocked recall; do not dismiss safety states as stale placeholders. Keep build-process markers out of visible Wiki prose: do not expose `CLI Agent`, `摘要来源`, `semantic_plan`, parser fallback labels, or builder/debug terminology as user-facing business content.
32
+
33
+ For non-SQL report pages, preserve the `Agent 使用摘要` as a usable business routing summary, not a mechanical parser echo. The purpose and applicable-question bullets must not be identical. Metric, dimension and filter sections should preserve structured evidence when present and use source-confirmation wording for unavailable facts. Do not introduce broad placeholder phrases such as `未识别到稳定下钻维度` or `未发现明确过滤` when a safer "open the source report to confirm" boundary is already present.
29
34
 
30
35
  During incremental compilation, inspect changed sources and their existing dependent pages. Update affected asset pages, indexes and cross-source conclusions; retain unrelated pages. Remove obsolete current assertions when a source changes or is removed. Integrate human evidence according to explicit scope, status and authority, never recency alone. Scope differences belong separately from same-scope contradictions. Preserve provenance to the actual uploaded source.