{
  "body": "## Summary\r\n\r\nLauncher **0.1.26** 中，QM v2 为嵌套维度 ID/caption 列显式配置 `name + alias`，模型校验、登记和刷新均成功，但实时 JSON/Markdown 仍公开 TM 原字段名；以 QM 配置的新名称构造 DSL 时，`query.validate` 返回 `FIELD_NOT_FOUND`。同一个 QM 改用原字段名即可通过校验并正确执行。\r\n\r\n此问题影响通过 QM 为 AI 提供稳定业务字段名；不是把 TM 的维度 alias 当成 QM alias 使用，也不是只测试整个维度 ref 的前缀展开。\r\n\r\n## Issue Routing\r\n\r\n- Repository: foggy-projects/foggy-data-mcp-bridge\r\n- Category: semantic metadata / QM field-name resolution\r\n- 与 #122 的跨 TM ColumnRef owner/alias 丢失不同：本例只有一个根 TM，失败发生在语义元数据/查询校验阶段，尚未进入 SQL 执行。与 #145 DISTINCT 排序无关。\r\n\r\n## Environment\r\n\r\n- Windows 11 Pro x64, build 26200; PowerShell 7.6.5\r\n- Java 17.0.12; Python 3.11.5; public CLI 0.1.24\r\n- [Launcher 0.1.26](https://github.com/foggy-projects/foggy-data-mcp-bridge/releases/tag/foggy-runtime-launcher-v0.1.26)，使用 Release 原版 README/PowerShell 启动脚本，不从源码构建\r\n- Release source commit: `8c8e461b73224d8faddbc7573e550cb549901f05`；内部 Java project version: `9.3.0-SNAPSHOT`\r\n- JAR SHA256: `051639bf738633cc63c2e4acfb5a4e91f0a3782c45bdcf767876abd465c7ff40`\r\n- 所有 6 个 Release 资源的 GitHub digest、SHA256SUMS 的 5 项均匹配；进程命令行与监听 PID 已核对，实际运行的是该 0.1.26 JAR\r\n- 全新独立 WorkDir / registry / SQLite 副本，`http://127.0.0.1:18068`，Namespace `chinook`，named datasource `chinook-sqlite`，`none-dev-test-only`，Console/FAP 关闭\r\n- Runtime API: `foggy-runtime-api/v1`, schema `2026-06-06`\r\n- 数据：[Chinook v1.4.5 SQLite](https://github.com/lerocha/chinook-database/releases/download/v1.4.5/Chinook_Sqlite.sqlite)\r\n- 数据文件 SHA256: `bdf635be69850bd3be09c9a2dbeef7ddfb80036bd3ef3381383cd03b61e4a61a`\r\n- 未覆盖原教学环境；复测结束后已移除临时 Bundle、停止本次实例。\r\n\r\n## Reproduction\r\n\r\n1. 从上述 Release 下载启动脚本及相邻 JAR，按 README 启动独立实例。示意：`./start-foggy-runtime.ps1 -Port 18068 -WorkDir ./work`。\r\n2. 将公开 Chinook SQLite 副本登记为 named datasource `chinook-sqlite`，绑定 `chinook`，连接测试成功。不是内置 demo 数据源。\r\n3. 保存以下 TM/QM 到 `models/model/`、`models/query/`，保存后面的 DSL 到 `alias-payload.json`。\r\n4. 执行以下公开 CLI 命令（各行均附 `--base-url http://127.0.0.1:18068 --namespace chinook`）：\r\n\r\n```text\r\nfoggy-runtime models validate --models-dir ./models\r\nfoggy-runtime bundles add --name qm-alias-repro --path ./models\r\nfoggy-runtime models refresh\r\nfoggy-runtime models describe MinimalArtistAliasQueryModel\r\nfoggy-runtime models describe MinimalArtistAliasQueryModel --format markdown\r\nfoggy-runtime query validate MinimalArtistAliasQueryModel --payload ./alias-payload.json\r\n```\r\n\r\n最后一条退出码为 2；被校验拒绝的请求没有继续执行。\r\n\r\n### TM: MinimalArtistAliasLineModel.tm\r\n\r\n```javascript\r\nexport const model = {\r\n    name: 'MinimalArtistAliasLineModel', caption: '账单明细别名复现', tableName: 'InvoiceLine', idColumn: 'InvoiceLineId',\r\n    properties: [{ column: 'InvoiceLineId', name: 'invoiceLineId', caption: '明细 ID', type: 'INTEGER' }],\r\n    dimensions: [\r\n        { name: 'invoice', caption: '账单', tableName: 'Invoice', foreignKey: 'InvoiceId', primaryKey: 'InvoiceId', captionColumn: 'InvoiceId',\r\n          properties: [{ column: 'InvoiceDate', name: 'invoiceDate', caption: '账单日期', type: 'DATETIME' }] },\r\n        { name: 'track', caption: '曲目', tableName: 'Track', foreignKey: 'TrackId', primaryKey: 'TrackId', captionColumn: 'Name',\r\n          dimensions: [\r\n              { name: 'album', caption: '专辑', tableName: 'Album', foreignKey: 'AlbumId', primaryKey: 'AlbumId', captionColumn: 'Title',\r\n                dimensions: [\r\n                    { name: 'artist', caption: '艺术家', tableName: 'Artist', foreignKey: 'ArtistId', primaryKey: 'ArtistId', captionColumn: 'Name' }\r\n                ] }\r\n          ] }\r\n    ],\r\n    measures: [{ column: 'UnitPrice', name: 'lineAmount', caption: '明细金额', type: 'MONEY', aggregation: 'sum',\r\n        formulaDef: { builder: (alias) => `${alias}.UnitPrice * ${alias}.Quantity` } }]\r\n};\r\n```\r\n\r\n### QM: MinimalArtistAliasQueryModel.qm\r\n\r\n```javascript\r\nconst line = loadTableModel('MinimalArtistAliasLineModel');\r\nexport const queryModel = {\r\n    name: 'MinimalArtistAliasQueryModel', caption: '艺术家别名复现', loader: 'v2', model: line,\r\n    columnGroups: [{ caption: '复现字段', items: [\r\n        { ref: line.invoiceLineId },\r\n        { ref: line.invoice$invoiceDate },\r\n        { ref: line.track.album.artist$id, name: 'albumArtistId', alias: 'albumArtistId' },\r\n        { ref: line.track.album.artist$caption, name: 'albumArtistName', alias: 'albumArtistName' },\r\n        { ref: line.lineAmount }\r\n    ] }],\r\n    accesses: []\r\n};\r\n```\r\n\r\n### DSL: alias-payload.json\r\n\r\n```json\r\n{\r\n  \"columns\": [\r\n    \"albumArtistId\",\r\n    \"albumArtistName\",\r\n    \"count(invoiceLineId) as lineCount\",\r\n    \"sum(lineAmount) as totalAmount\"\r\n  ],\r\n  \"slice\": [\r\n    {\r\n      \"field\": \"invoice$invoiceDate\",\r\n      \"op\": \"[)\",\r\n      \"value\": [\r\n        \"2012-01-01\",\r\n        \"2013-01-01\"\r\n      ]\r\n    },\r\n    {\r\n      \"field\": \"albumArtistId\",\r\n      \"op\": \"=\",\r\n      \"value\": 90\r\n    }\r\n  ],\r\n  \"groupBy\": [\r\n    \"albumArtistId\",\r\n    \"albumArtistName\"\r\n  ],\r\n  \"orderBy\": [\r\n    \"albumArtistId\"\r\n  ],\r\n  \"limit\": 5\r\n}\r\n```\r\n\r\n## Expected\r\n\r\n- 对支持的 QM 列 `name/alias`，实时 JSON、Markdown、字段解析和 DSL 校验遵循同一份公开字段约定。\r\n- 本例同时把 name/alias 设为相同值，避免混淆“查询标识”和“输出别名”：期望 `albumArtistId`、`albumArtistName` 可用于选择、筛选、分组和排序。\r\n- 如果此类引用不支持 name/alias，应在模型校验/加载阶段明确拒绝，指出配置路径、原因和支持的调整方法；不应先接受配置，再静默忽略它。\r\n\r\n## Actual / Evidence\r\n\r\n- 最小 TM/QM：2 个文件均通过 `models validate`，0 invalid；Bundle 登记及刷新成功。\r\n- JSON 与 Markdown 的字段仍是 `artist$id`、`artist$caption`，没有 `albumArtistId` / `albumArtistName`。\r\n- `query.validate` 返回：\r\n\r\n```json\r\n{\r\n  \"code\": \"FIELD_NOT_FOUND\",\r\n  \"message\": \"Field 'albumArtistId' not found in model 'MinimalArtistAliasQueryModel'. Did you mean 'track.album.artist$id'?\",\r\n  \"model\": \"MinimalArtistAliasQueryModel\",\r\n  \"phase\": \"query.validate\",\r\n  \"safeToAutoRepair\": true,\r\n  \"suggestedNextAction\": \"Call model describe and retry with an existing field.\"\r\n}\r\n```\r\n\r\n- 对照：**不改 QM**，只在 DSL 中把 `albumArtistId` 替换为 `artist$id`、`albumArtistName` 替换为 `artist$caption`（columns/slice/groupBy/orderBy 一并替换），再次 validate 后 execute，均成功：\r\n\r\n```json\r\n[\r\n  {\r\n    \"artist$caption\": \"Iron Maiden\",\r\n    \"artist$id\": 90,\r\n    \"lineCount\": 34,\r\n    \"totalAmount\": 33.66\r\n  }\r\n]\r\n```\r\n\r\n### 原有复现的三个变体也已复测\r\n\r\n| QM 写法 | 模型校验/加载 | JSON/Markdown 新名称 | 新名称 DSL 校验 | 同 QM 原名称对照 |\r\n| --- | --- | --- | --- | --- |\r\n| 整维度 `ref: line.track.album.artist, alias: 'albumArtist'` | 通过 | 未出现 | FIELD_NOT_FOUND | 通过，34 条 / 33.66 |\r\n| ID/caption 各自配置 alias | 通过 | 未出现 | FIELD_NOT_FOUND | 通过，34 条 / 33.66 |\r\n| ID/caption 各自配置相同 name + alias | 通过 | 未出现 | FIELD_NOT_FOUND | 通过，34 条 / 33.66 |\r\n\r\n三个 DSL 与此前 0.1.25 复现请求保持不变。整维度 ref 的 alias 是否应作为展开前缀，可以另行明确合同；即使该写法不支持，最小复现中对具体 ID/caption 列显式设置 name/alias 的问题仍存在。\r\n\r\n## Source leads (需要维护者确认)\r\n\r\n在该 Release commit 中，`SelectColumnDef` 描述了 QM name/alias，`DbQueryColumnImpl` 也保存这些值；而 `SemanticServiceV3Impl` 的维度字段元数据构建使用 `dimension.getEffectiveName()` 生成 ID/caption 名称。推测 QM 列覆盖名未完整传到语义字段注册链路，尚未把这一点当作已确定的根因。\r\n\r\n- [SelectColumnDef](https://github.com/foggy-projects/foggy-data-mcp-bridge/blob/8c8e461b73224d8faddbc7573e550cb549901f05/foggy-dataset-model-engine/src/main/java/com/foggyframework/dataset/model/def/query/SelectColumnDef.java)\r\n- [SemanticServiceV3Impl](https://github.com/foggy-projects/foggy-data-mcp-bridge/blob/8c8e461b73224d8faddbc7573e550cb549901f05/foggy-dataset-model-engine/src/main/java/com/foggyframework/dataset/model/semantic/service/impl/SemanticServiceV3Impl.java)\r\n\r\n建议回归覆盖：QM 具体嵌套 ID/caption 的 name/alias、JSON/Markdown 一致性、columns/slice/groupBy/orderBy、执行结果输出键，以及两个同名嵌套维度通过 QM 别名消歧。整维度 alias 不支持时也应有明确诊断。\r\n",
  "number": 146,
  "state": "OPEN",
  "title": "[Bug] QM 嵌套维度列 name/alias 未进入语义元数据，DSL 校验拒绝新字段（Launcher 0.1.26）",
  "url": "https://github.com/foggy-projects/foggy-data-mcp-bridge/issues/146"
}