@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.
- package/README.md +28 -9
- package/README.zh.md +25 -9
- package/bin/ae-cli.js +6 -1
- package/cli-installation-guide.md +91 -0
- package/dist/{auth-LHM7NQPR.js → auth-RQBO6MTY.js} +9 -11
- package/dist/automation-VAAQBM7Y.js +459 -0
- package/dist/{capability-F2RIR5DS.js → capability-JRIFC4AJ.js} +6 -6
- package/dist/{chunk-647BRKZK.js → chunk-374V5R6N.js} +3 -3
- package/dist/{chunk-KJDOTPYU.js → chunk-3YVEAZUN.js} +59 -26
- package/dist/{chunk-KS4DE3MX.js → chunk-5XJYLEVC.js} +4 -4
- package/dist/{chunk-BW7JUDEI.js → chunk-6VNIR4AP.js} +514 -213
- package/dist/{chunk-5XRQ3CZR.js → chunk-7KU2WNQE.js} +43 -8
- package/dist/{chunk-7WZACOAI.js → chunk-AP7YP2NX.js} +2 -2
- package/dist/chunk-CI24BDR2.js +43 -0
- package/dist/{chunk-SO5LKIWP.js → chunk-CT7QDJS6.js} +2 -2
- package/dist/{chunk-DU72X2IO.js → chunk-GHXZK54S.js} +22 -10
- package/dist/{chunk-N4BLPJC7.js → chunk-OP44TT4H.js} +1 -1
- package/dist/chunk-Q5US7JC5.js +133 -0
- package/dist/{chunk-PGCIKZRE.js → chunk-RGYPSEYD.js} +3 -3
- package/dist/chunk-RK6GLDGI.js +27 -0
- package/dist/chunk-RPBOAQQQ.js +56 -0
- package/dist/{chunk-GK7WDY7E.js → chunk-S6M42KND.js} +1 -1
- package/dist/{chunk-QYXDJOLA.js → chunk-VZZNLV3I.js} +19 -11
- package/dist/{chunk-YVGXGTD6.js → chunk-WNGFSIOB.js} +7 -1
- package/dist/{chunk-IG3DYEIR.js → chunk-ZL7SGNIA.js} +5 -3
- package/dist/{community-report-client-6VJDDMJJ.js → community-report-client-PEERQCX2.js} +1 -1
- package/dist/{config-XQWOPDHN.js → config-SICKWZ2A.js} +8 -8
- package/dist/context-NGVFPCBK.js +136 -0
- package/dist/{data-integration-XBTCGXQT.js → data-integration-BCLQFTGU.js} +6 -6
- package/dist/index.js +125 -48
- package/dist/{local-data-upload-client-3P7NIVQ4.js → local-data-upload-client-TWMHFLUA.js} +1 -1
- package/dist/memory-CQKH2JAV.js +580 -0
- package/dist/{metadata-V5ZDTKWZ.js → metadata-EMFGUI2W.js} +7 -7
- package/dist/{model-ZS7HVGKN.js → model-VX5M2CJ6.js} +9 -5
- package/dist/{personal-semantic-preference-2XANU3F6.js → personal-semantic-preference-SLWVVYZ4.js} +117 -11
- package/dist/{project-semantic-LP3LY7QP.js → project-semantic-DPSZBKQD.js} +6 -6
- package/dist/{sync-EQLSTUS7.js → sync-RZIAV37C.js} +12 -8
- package/dist/{te-agent-Z3HCTKR7.js → te-agent-AORRE2FM.js} +90 -454
- package/dist/{te-analysis-3EVYQ3IO.js → te-analysis-ISXKJKRT.js} +297 -20
- package/dist/{te-community-Z7JACGF3.js → te-community-42M7ICDO.js} +3 -3
- package/dist/{te-dataops-F7GQOIS6.js → te-dataops-BNKNZ4F2.js} +5 -5
- package/dist/{te-engage-LFBU6WWH.js → te-engage-BZE3MBKT.js} +6 -6
- package/dist/{te-kb-QJLJEX5K.js → te-kb-ULATOTQ3.js} +494 -214
- package/dist/{te-system-IXC43QHZ.js → te-system-UGHVKBVV.js} +10 -9
- package/dist/{te-team-BIZIKIHZ.js → te-team-YA6SNOJ3.js} +6 -6
- package/dist/things-DTCOQIQB.js +1091 -0
- package/dist/{update-W2SGQNKT.js → update-ESNYKUVR.js} +59 -51
- package/dist/workspace-JEOHKFPX.js +554 -0
- package/package.json +21 -9
- package/skills/ae-agent/SKILL.md +91 -139
- package/skills/ae-agent/references/add-model.md +1 -1
- package/skills/ae-agent/references/create-agent.md +1 -1
- package/skills/ae-agent/references/find-archived-conversations.md +12 -5
- package/skills/ae-agent/references/get-agent.md +1 -1
- package/skills/ae-agent/references/list-agents.md +2 -2
- package/skills/ae-agent/references/list-models.md +2 -2
- package/skills/ae-agent/references/restore-conversation.md +3 -1
- package/skills/ae-analysis/SKILL.md +15 -6
- package/skills/ae-analysis/references/ai_models/funnel.md +5 -3
- package/skills/ae-analysis/references/ai_models.md +27 -0
- package/skills/ae-analysis/references/command_index.md +12 -4
- package/skills/ae-analysis/references/dashboard_move.md +21 -0
- package/skills/ae-analysis/references/dashboard_report_add.md +2 -0
- package/skills/ae-analysis/references/dashboard_report_get.md +19 -0
- package/skills/ae-analysis/references/dashboard_report_update.md +27 -0
- package/skills/ae-analysis/references/dashboard_update.md +1 -1
- package/skills/ae-analysis/references/event_report_presentation.md +76 -0
- package/skills/ae-analysis/references/feedback_submit.md +37 -0
- package/skills/ae-analysis/references/metadata_resolution.md +15 -0
- package/skills/ae-analysis/references/personal_semantic_preference_review.md +9 -0
- package/skills/ae-analysis/references/personal_semantic_preference_review_list.md +7 -0
- package/skills/ae-analysis/references/personal_semantic_preference_submit.md +7 -0
- package/skills/ae-analysis/references/project_semantic_knowledge_wiki.md +90 -31
- package/skills/ae-analysis/references/project_semantic_knowledge_wiki_plan_schema.md +99 -1
- package/skills/ae-analysis/references/report_create.md +13 -3
- package/skills/ae-analysis/references/report_get.md +5 -1
- package/skills/ae-analysis/references/report_update.md +15 -4
- package/skills/ae-analysis/references/system_query_monitor_overview.md +2 -0
- package/skills/ae-analysis/references/system_query_task_cancel.md +2 -0
- package/skills/ae-analysis/references/system_query_task_export.md +6 -2
- package/skills/ae-analysis/references/system_query_task_get.md +2 -0
- package/skills/ae-analysis/references/system_query_task_list.md +7 -3
- package/skills/ae-analysis/references/system_query_task_options.md +2 -2
- package/skills/ae-analysis/references/system_query_task_realtime_list.md +15 -0
- package/skills/ae-analysis/references/user_tag_models.md +2 -0
- package/skills/ae-analysis/scripts/project-semantic-knowledge-wiki/build-empty-skeleton.mjs +105 -0
- package/skills/ae-analysis/scripts/project-semantic-knowledge-wiki/build-project-semantic-wiki.mjs +669 -29
- package/skills/ae-analysis/scripts/project-semantic-knowledge-wiki/company-kb.mjs +21 -8
- package/skills/ae-analysis/scripts/project-semantic-knowledge-wiki/default-compile-rules.md +28 -10
- package/skills/ae-analysis/scripts/project-semantic-knowledge-wiki/default-compile-schema.md +66 -0
- package/skills/ae-analysis/scripts/project-semantic-knowledge-wiki/generate-build-ir.mjs +285 -24
- package/skills/ae-analysis/scripts/project-semantic-knowledge-wiki/package-wiki-source-zip.mjs +262 -30
- package/skills/ae-analysis/scripts/project-semantic-knowledge-wiki/plan-kb-source-sync.mjs +1 -1
- package/skills/ae-analysis/scripts/project-semantic-knowledge-wiki/precompiled-source.mjs +46 -2
- package/skills/ae-analysis/scripts/project-semantic-knowledge-wiki/prepare-compile-schema.mjs +64 -0
- package/skills/ae-analysis/scripts/project-semantic-knowledge-wiki/refresh-state-parts.mjs +91 -0
- package/skills/ae-automation/SKILL.md +16 -0
- package/skills/ae-automation/references/cli.md +53 -0
- package/skills/ae-automation/references/command_index.md +17 -0
- package/skills/ae-capability/SKILL.md +15 -6
- package/skills/ae-current-context/SKILL.md +3 -2
- package/skills/ae-kb/SKILL.md +62 -12
- package/skills/ae-kb/references/analysis-workflow.md +73 -36
- package/skills/ae-kb/references/query-workflow.md +67 -33
- package/skills/ae-kb/references/schema-import.md +5 -3
- package/skills/ae-kb/references/source-mutations.md +6 -4
- package/skills/ae-kb/references/text-sources.md +53 -0
- package/skills/ae-kb-discovery/SKILL.md +1 -1
- package/skills/ae-team/SKILL.md +23 -17
- package/skills/ae-things/SKILL.md +85 -0
- package/skills/ae-things/references/cli.md +103 -0
- package/skills/ae-things-progress/SKILL.md +43 -0
- package/skills/ae-things-review/SKILL.md +42 -0
- package/skills/ae-workspace/SKILL.md +43 -0
- package/skills/ae-workspace/references/commands.md +51 -0
- package/dist/chunk-E2JKXMVO.js +0 -29
- package/dist/context-M3HDGEIR.js +0 -46
- package/dist/memory-6APEB2JB.js +0 -892
- package/skills/ae-agent/references/create-automation.md +0 -125
- package/skills/ae-agent/references/list-automations.md +0 -56
- 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('、') || '
|
|
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
|
-
:
|
|
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') || ' -
|
|
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
|
|
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(
|
|
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
|
|
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
|
-
|
|
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()]
|
|
2167
|
-
|
|
2168
|
-
|
|
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}.`);
|