@duckcodeailabs/dql-agent 1.10.1 → 1.10.3

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.
@@ -888,6 +888,33 @@ export function buildMetadataSnapshot(projectRoot, manifest, semanticLayer, skil
888
888
  * Acceptance: CONTRACT-002, AGT-018.
889
889
  */
890
890
  function addCertifiedBlockAnalyticalCapabilities(objects) {
891
+ // Existing certified blocks can carry the pre-ID-001 domain-scoped member
892
+ // identities. Canonicalize those aliases only when exactly one semantic
893
+ // object owns them; repeated leaf aliases intentionally fail closed.
894
+ const semanticIdentityTargets = new Map();
895
+ for (const object of objects.values()) {
896
+ if (!object.objectType.startsWith('semantic_'))
897
+ continue;
898
+ const payload = object.payload ?? {};
899
+ const canonicalIdentity = object.fullName ?? object.name;
900
+ for (const identity of uniqueNonBlank([
901
+ canonicalIdentity,
902
+ stringValue(payload.qualifiedId),
903
+ stringValue(payload.registryQualifiedId),
904
+ stringValue(payload.sourceNativeId),
905
+ ...metadataStringArray(payload.aliases),
906
+ ])) {
907
+ const targets = semanticIdentityTargets.get(identity) ?? new Set();
908
+ targets.add(canonicalIdentity);
909
+ semanticIdentityTargets.set(identity, targets);
910
+ }
911
+ }
912
+ const canonicalizeSemanticRef = (identity) => {
913
+ const targets = semanticIdentityTargets.get(identity);
914
+ if (!targets)
915
+ return identity;
916
+ return targets.size === 1 ? [...targets][0] : undefined;
917
+ };
891
918
  const semanticCapabilities = [...objects.values()].flatMap((object) => {
892
919
  if (object.objectType !== "semantic_metric")
893
920
  return [];
@@ -923,15 +950,22 @@ function addCertifiedBlockAnalyticalCapabilities(objects) {
923
950
  if (metrics.length !== 1)
924
951
  continue;
925
952
  const semantic = metrics[0];
926
- const grain = stringValue(payload.grain);
953
+ const authoredGrain = stringValue(payload.grain);
954
+ const grain = authoredGrain ? canonicalizeSemanticRef(authoredGrain) : undefined;
927
955
  if (!grain || !semantic.capability.resultGrainIds.includes(grain))
928
956
  continue;
929
- const groupedRefs = uniqueNonBlank([
957
+ const authoredGroupedRefs = uniqueNonBlank([
930
958
  ...metadataStringArray(payload.dimensions),
931
959
  ...metadataStringArray(payload.dimensionsRef),
932
960
  ]);
933
- const filterRefs = uniqueNonBlank(metadataStringArray(payload.allowedFilters));
934
- const declaredRefs = new Set([...groupedRefs, ...filterRefs]);
961
+ const authoredFilterRefs = uniqueNonBlank(metadataStringArray(payload.allowedFilters));
962
+ const groupedRefs = authoredGroupedRefs.map(canonicalizeSemanticRef);
963
+ const filterRefs = authoredFilterRefs.map(canonicalizeSemanticRef);
964
+ if (groupedRefs.some((ref) => !ref) || filterRefs.some((ref) => !ref))
965
+ continue;
966
+ const canonicalGroupedRefs = groupedRefs.filter((ref) => Boolean(ref));
967
+ const canonicalFilterRefs = filterRefs.filter((ref) => Boolean(ref));
968
+ const declaredRefs = new Set([...canonicalGroupedRefs, ...canonicalFilterRefs]);
935
969
  const capabilityDimensionIds = new Set(semantic.capability.dimensions.map((item) => item.dimensionId));
936
970
  const capabilityTimeIds = new Set(semantic.capability.timeDimensions.map((item) => item.dimensionId));
937
971
  if ([...declaredRefs].some((id) => !capabilityDimensionIds.has(id) && !capabilityTimeIds.has(id)))
@@ -939,14 +973,14 @@ function addCertifiedBlockAnalyticalCapabilities(objects) {
939
973
  const dimensions = semantic.capability.dimensions.flatMap((dimension) => {
940
974
  if (!declaredRefs.has(dimension.dimensionId))
941
975
  return [];
942
- const roles = dimension.supportedRoles.filter((role) => (role === "filter" && filterRefs.includes(dimension.dimensionId)) ||
976
+ const roles = dimension.supportedRoles.filter((role) => (role === "filter" && canonicalFilterRefs.includes(dimension.dimensionId)) ||
943
977
  (role === "group_by" &&
944
- groupedRefs.includes(dimension.dimensionId)) ||
978
+ canonicalGroupedRefs.includes(dimension.dimensionId)) ||
945
979
  (role === "display" &&
946
- groupedRefs.includes(dimension.dimensionId)) ||
980
+ canonicalGroupedRefs.includes(dimension.dimensionId)) ||
947
981
  (role === "rank_entity" &&
948
982
  payload.pattern === "ranking" &&
949
- groupedRefs.includes(dimension.dimensionId)));
983
+ canonicalGroupedRefs.includes(dimension.dimensionId)));
950
984
  return roles.length > 0
951
985
  ? [{ ...dimension, supportedRoles: roles }]
952
986
  : [];
@@ -961,9 +995,9 @@ function addCertifiedBlockAnalyticalCapabilities(objects) {
961
995
  !outputs.kinds.includes("metric_value"))
962
996
  continue;
963
997
  const operations = [];
964
- if (filterRefs.length > 0)
998
+ if (canonicalFilterRefs.length > 0)
965
999
  operations.push("filter");
966
- if (groupedRefs.some((id) => capabilityDimensionIds.has(id)))
1000
+ if (canonicalGroupedRefs.some((id) => capabilityDimensionIds.has(id)))
967
1001
  operations.push("group");
968
1002
  if (payload.pattern === "trend")
969
1003
  operations.push("trend");
@@ -978,8 +1012,8 @@ function addCertifiedBlockAnalyticalCapabilities(objects) {
978
1012
  blockId,
979
1013
  metricRefs,
980
1014
  grain,
981
- groupedRefs,
982
- filterRefs,
1015
+ groupedRefs: canonicalGroupedRefs,
1016
+ filterRefs: canonicalFilterRefs,
983
1017
  outputs,
984
1018
  }));
985
1019
  const capability = {
@@ -2530,6 +2564,9 @@ function isQualifiedDqlModelingNode(node) {
2530
2564
  }
2531
2565
  function qualifiedIdentityFromKGNode(node) {
2532
2566
  const payload = node.payload ?? {};
2567
+ const registryIdentity = stringValue(payload.registryQualifiedId);
2568
+ if (registryIdentity)
2569
+ return registryIdentity;
2533
2570
  const explicit = stringValue(payload.qualifiedId);
2534
2571
  if (explicit)
2535
2572
  return explicit;