Runtime Compose 的派生 .query() 中声明 calculatedFields,再在 columns 选择该计算列,校验却把它当作“来源未输出字段”。同一基础字段与同一算式改成列内表达式即可验证、预览并执行。
本单先请求明确能力合同并改善诊断:当前 Skill 明确列出基础 dsl 的 calculatedFields,但没有明确承诺派生层等价支持。因此不把“必须支持此能力”预设成已确认合同 BUG;如果派生层本来不支持,也应指出不支持的参数,而非引导 AI 去源 QM 找该计算字段。
归属:Java Runtime 的 compose.validate / 派生 schema 推导与可修复诊断。关联 #160 的结构化诊断方向,但这里是一个独立的 calculatedFields 入口差异场景。
7b6d06987e3ec90f05143a0552791f80381dcc4b,Java projectVersion:9.3.0-SNAPSHOT;不是本地源码编译结果。8228d31c104d58b1b2f1d1c5bc856a25531e4d5af06e1e9eefed2be576f20bf9。foggy-runtime 0.1.25;CLI 子进程使用 UTF-8 输出。http://127.0.0.1:18067;Namespace:chinook-composition;命名数据源:chinook-composition-sqlite(SQLite,公开 Chinook v1.4.5 副本)。securityMode=none-dev-test-only,匿名本地教学环境,Console/FAP 未启用;未使用生产数据、身份、私有接口或 SQL 直通。capabilities 实际响应节选:
{
"engine": "java",
"runtimeApiVersion": "foggy-runtime-api/v1",
"schemaVersion": "2026-06-06",
"securityMode": "none-dev-test-only",
"capabilities": {
"compose.validate": "supported",
"compose.preview": "supported",
"compose.execute": "supported",
"models.describe": "supported"
}
}
ChinookInvoiceQueryModel 对应 Chinook 的 Invoice 表,必要公开字段如下:
| 字段 | 实际物理映射 / 角色 |
|---|---|
| customerId | Invoice.CustomerId,客户键 |
| invoiceDate | Invoice.InvoiceDate,账单日期 |
| invoiceAmount | Invoice.Total,MONEY、sum;沿用样例原单位 |
下面只引用已公开字段和前一计划的输出别名。年度基线为 47 位有账单客户、83 张账单、477.53;原始数据与模型在复现前后均未修改。
01-derived-calculated-fields.fsscript。foggy-runtime --output json --base-url http://127.0.0.1:18067 --namespace chinook-composition compose validate --script ./01-derived-calculated-fields.fsscript
实际请求脚本:
const bills = dsl({
model: 'ChinookInvoiceQueryModel',
columns: ['customerId as customer_id', 'sum(invoiceAmount) as bill_amount'],
slice: [{field: 'invoiceDate', op: '[)', value: ['2012-01-01', '2013-01-01']}],
groupBy: ['customerId']
});
return {plans: bills.query({
columns: ['customer_id', 'bill_amount', 'double_amount'],
calculatedFields: [{name: 'double_amount', expression: 'bill_amount * 2'}],
orderBy: ['customer_id'], limit: 100
})};
calculatedFields 不适用 / 未支持,给出参数路径及建议写法(例如列内表达式);不要仅报新声明的计算列“不在 source schema”。CLI exitCode 2;success=false、field=double_amount、phase=compose.validate、safeToAutoRepair=true。完整实际响应:
{
"diagnostics": {
"attributes": {
"composeErrorCode": "compose-schema-error/derived-query/unknown-field",
"composeErrorPhase": "schema-derive",
"datasourceType": "sqlite",
"defaultDialect": "sqlite",
"dialectSource": "namespace-datasource-type",
"namespace": "chinook-composition",
"namespaceDatasourceId": "chinook-composition-sqlite",
"namespaceDatasourceStatus": "active",
"resolvedDialect": "sqlite"
},
"warnings": []
},
"engine": "java",
"error": {
"code": "COMPOSE_SCRIPT_INVALID",
"field": "double_amount",
"message": "derived query references unknown field 'double_amount' not present in source output schema",
"phase": "compose.validate",
"safeToAutoRepair": true,
"suggestedNextAction": "Inspect compose fields/schema and retry."
},
"runtimeApiVersion": "foggy-runtime-api/v1",
"success": false
}
实际 CLI / 诊断记录:
exitCode=2, success=false
error.code=COMPOSE_SCRIPT_INVALID
error.field=double_amount
error.phase=compose.validate
diagnostics.attributes.composeErrorCode=compose-schema-error/derived-query/unknown-field
diagnostics.attributes.composeErrorPhase=schema-derive
safeToAutoRepair=true
message=derived query references unknown field 'double_amount' not present in source output schema
本例是结构化校验拒绝,本轮服务端增量日志未见对应的独立 Java 异常堆栈;没有为它拼接另一个请求的 ClassCastException 日志。失败请求没有 execute。
仅改为列内表达式,实际完整请求:
const bills = dsl({
model: 'ChinookInvoiceQueryModel',
columns: ['customerId as customer_id', 'sum(invoiceAmount) as bill_amount'],
slice: [{field: 'invoiceDate', op: '[)', value: ['2012-01-01', '2013-01-01']}],
groupBy: ['customerId']
});
return {plans: bills.query({
columns: ['customer_id', 'bill_amount', 'bill_amount * 2 as double_amount'],
orderBy: ['customer_id'], limit: 100
})};
该请求依次 compose validate → preview → execute,退出码均为 0,实际返回 47 行,每行 double_amount 等于 bill_amount × 2。执行响应节选(items 对应 Compose 的 data.value.plans):
{
"success": true,
"data": {
"valid": true,
"value": {
"plans": [
{
"bill_amount": 15.84,
"customer_id": 1,
"double_amount": 31.68
},
{
"bill_amount": 0.99,
"customer_id": 2,
"double_amount": 1.98
}
]
}
}
}
原始账单合计仍为 477.53,独立 Decimal 核对通过。这说明不是基础字段缺失或乘法本身不可用。
问题最初发生在两个 QM 按客户聚合并 JOIN 后,以 ROUND(bill_amount - line_amount, 2) 生成 amount_difference。加入本次单 QM 最小复现,是为了排除 JOIN 和小数函数对定位的干扰。原请求及实际响应如下,明细 QM 同样为公开 Chinook 的 InvoiceLine,lineAmount=UnitPrice×Quantity,通过 Invoice 取客户及日期。
const bills = dsl({
model: 'ChinookInvoiceQueryModel',
columns: ['customerId as bill_customer_id', 'sum(invoiceAmount) as bill_amount'],
slice: [{ field: 'invoiceDate', op: '[)', value: ['2012-01-01', '2013-01-01'] }],
groupBy: ['customerId']
});
const lines = dsl({
model: 'ChinookInvoiceLineQueryModel',
columns: ['invoice$customerId as line_customer_id', 'sum(lineAmount) as line_amount'],
slice: [{ field: 'invoice$invoiceDate', op: '[)', value: ['2012-01-01', '2013-01-01'] }],
groupBy: ['invoice$customerId']
});
const joined = bills.join(lines, 'left', [
{ left: 'bill_customer_id', op: '=', right: 'line_customer_id' }
]);
const compared = joined.query({
columns: ['bill_customer_id', 'line_customer_id', 'bill_amount', 'line_amount', 'amount_difference'],
calculatedFields: [{ name: 'amount_difference', expression: 'ROUND(bill_amount - line_amount, 2)' }],
orderBy: ['bill_customer_id'], limit: 100
});
return { plans: compared };
{
"diagnostics": {
"attributes": {
"composeErrorCode": "compose-schema-error/derived-query/unknown-field",
"composeErrorPhase": "schema-derive",
"datasourceType": "sqlite",
"defaultDialect": "sqlite",
"dialectSource": "namespace-datasource-type",
"namespace": "chinook-composition",
"namespaceDatasourceId": "chinook-composition-sqlite",
"namespaceDatasourceStatus": "active",
"resolvedDialect": "sqlite"
},
"warnings": []
},
"engine": "java",
"error": {
"code": "COMPOSE_SCRIPT_INVALID",
"field": "amount_difference",
"message": "derived query references unknown field 'amount_difference' not present in source output schema",
"phase": "compose.validate",
"safeToAutoRepair": true,
"suggestedNextAction": "Inspect compose fields/schema and retry."
},
"runtimeApiVersion": "foggy-runtime-api/v1",
"success": false
}
明确派生 query 的支持矩阵;对上述一种支持实现或明确拒绝实现增加测试。错误应能让 AI 知道是参数能力边界,还是引用了真正未输出的字段;若支持计算列,覆盖投影与后续筛选对该列的引用。保留现有字段治理边界。