deliver: 交付收口(引擎代为提交)

This commit is contained in:
agent.develop 2026-09-20 18:32:45 +08:00
parent 9f97d58dba
commit 95b405fd25

View File

@ -1,6 +1,6 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""validate_models_json —— database-table-definition-spec 机械校验器M11b-2b-A 修复版 rev3)。
"""validate_models_json —— database-table-definition-spec 机械校验器M11b-2b-A 修复版 rev4)。
QC #4/#7/#9/#10 要求:表定义 JSON 必须是 spec 规定的四段式summary/fields/indexes/
codes类型必须是 spec 声明的抽象类型主键 id 必须是 str/length>=32索引必须用
@ -11,13 +11,19 @@ codes类型必须是 spec 声明的抽象类型,主键 id 必须是 str/
QC #2 抽象类型误报纯抽象类型名datetime/timestamp/date/time/text/long/…)一律放行,
只有 type 字符串本身含括号或空格方言写法 varchar(64)/decimal(15,2)/datetime(3)
或本身就是 spec 不存在的非抽象类型名varchar/bigint/json/blob/才判违规
NATIVE_TYPE_WORDS 中已删除 "datetime(" / "timestamp(" 两项
QC #3 CLI 与文档一致argparse 真正提供 --allow-registered-deviation见下方「用法」
同一轮 QC #3 追加要求「拦截分布可一条命令复现」:本版本在结尾输出
``RULE_STATS: <规则标签>=<条数>`` 逐规则计数 + ``RULE_STATS_TOTAL`` 总量
复核者只需一条命令即可核对分布不必依赖任何中间文件
python3 modules/world_sync/scripts/validate_models_json.py \
$(ls -d modules/*/models) 2>&1 | grep "^RULE_STATS"
QC #4 root_keys_ok由 validate_model 显式计算 (not missing and not extra) 并随结果返回,
main() 直接打印该布尔值不再用错误串里是否含四段式猜测
QC #5 引用式 key/schemaspec 不承认引用式结构,直接判 ERROR 并提示改为内联四段式;
COMPLIANCE_DEVIATIONS 中针对 pbl_runtime_event/pbl_entity_state 4 条失效登记项
一并删除引用式在根键检查处即 FAIL根本到不了 fields 校验登记形同虚设
QC #1轮)放行机制对 auto_increment 生效字段段「spec 未定义键」检查原先无条件报错,
QC #1第三轮)放行机制对 auto_increment 生效字段段「spec 未定义键」检查原先无条件报错,
导致 (表名,"id","auto-increment") 即使已登记即使传了 --allow-registered-deviation
仍残留 `fields[N](id) spec 未定义键: auto_increment` 而整体 FAIL id-not-str32
可豁免auto_increment 不可豁免机制自相矛盾现在该检查改为**偏离感知**
@ -35,7 +41,10 @@ codes类型必须是 spec 声明的抽象类型,主键 id 必须是 str/
典型场景分区表物理主键需要 long + auto_incrementMySQL 1503分区键必须进主键
若某表确需此类写法须先在其模块任务里把 (表名,"id","id-not-str32")
(表名,"id","auto-increment") 登记进 COMPLIANCE_DEVIATIONS再配合开关使用 两者缺一不可
只登记不传开关仍 FAIL只传开关不登记也 FAIL本文件末尾自测 5 给出四态实测输出
只登记不传开关仍 FAIL只传开关不登记也 FAIL
与并行子任务 B 的接口B pbl_runtime_event/pbl_entity_state 内联为四段式后若保留
id=long(+auto_increment) 写法须由 B 在其任务内按上述键格式登记本任务按 QC #5 选 (a)
登记表保持为空 {}不放行任何历史失效项
用法
python validate_models_json.py <models目录1> [<目录2> ...]
@ -81,6 +90,49 @@ COMPLIANCE_DEVIATIONS = {}
DEVIATION_HINT = "需 --allow-registered-deviation 放行"
# QC #3第三轮逐规则计数标签 —— 让「真·非抽象类型仍被拦截」的分布不依赖中间文件,
# 一条命令即可复现(结尾 RULE_STATS 行)。标签按下列顺序尝试匹配(首个命中即归类)。
RULE_STATS_TAGS = (
("unreadable_json", ("JSON 不可解析", "文件不可读", "根对象必须是 JSON object")),
("reference_style_key_schema", ("引用式写法",)),
("root_key_extra", ("根键多出 spec 未定义的段",)),
("root_key_missing", ("根键缺少 spec 必需段",)),
("type_db_native", ("是数据库原生类型",)),
("type_db_dialect", ("是数据库方言具体类型",)),
("type_not_in_abstract_table", ("不在 spec 抽象类型表内",)),
("type_not_string", ("type 必须是非空字符串",)),
("pk_id_not_str32", ("主键 id 必须 str 且 length>=32",)),
("id_auto_increment", ("auto_increment",)),
("field_length_dec_missing", ("必须带正整数",)),
("field_nullable_bad", ("nullable 只能是",)),
("field_name_missing", (".name 必填",)),
("field_title_missing", (".title 必填",)),
("field_unknown_key", ("fields[", "含 spec 未定义键")),
("field_dup_name", ("字段名重复",)),
("summary_shape", ("summary 必须是恰好 1 条记录的数组", "summary[0].", "summary[0] 含 spec 未定义键")),
("primary_not_array", ("primary 必须是非空数组",)),
("primary_unknown_col", ("primary 引用了不存在的列",)),
("index_unknown_key", ("indexes[", "含 spec 未定义键")),
("index_type_bad", ("idxtype 必须是",)),
("index_fields_bad", ("idxfields 必须是非空数组",)),
("index_ref_unknown_col", ("idxfields 引用不存在的列",)),
("index_name_primary", ("主键不写进 indexes",)),
("index_dup_name", ("索引名重复",)),
("code_unknown_key", ("codes[", "含 spec 未定义键")),
("code_required_missing", ("codes[", ".必填")),
("code_ref_unknown_col", ("codes[", "引用不存在的列")),
("code_cond_parentid", ("cond 必须用 parentid=",)),
("code_table_dot", ("codes.table 禁止",)),
)
def _rule_tag(message):
"""把一条 ERROR 文本归类到规则标签QC #3分布可由一条命令复现"""
for tag, needles in RULE_STATS_TAGS:
if all(n in message for n in needles):
return tag
return "other"
def _is_reference_style(data):
"""识别引用式结构:{"key": "schema", "schema": {...}} 之类spec 不承认)。"""
@ -372,13 +424,20 @@ def main(argv=None):
total_files = 0
total_errors = 0
total_deviations = 0
rule_counts = {}
report = []
for path in collect_targets(dirs, name_filters or None):
errors, deviations, table, root_keys_ok = validate_model(path, strict_id=strict_id)
total_files += 1
total_errors += len(errors)
total_deviations += len(deviations)
per_file_rules = {}
for e in errors:
tag = _rule_tag(e)
rule_counts[tag] = rule_counts.get(tag, 0) + 1
per_file_rules[tag] = per_file_rules.get(tag, 0) + 1
report.append({"path": path, "table": table, "root_keys_ok": root_keys_ok,
"rule_stats": per_file_rules,
"errors": errors, "deviations": deviations})
print("---- %s (table=%s)" % (path, table))
print(" root_keys_ok=%s" % root_keys_ok) # QC #4打印显式计算的布尔值
@ -389,6 +448,10 @@ def main(argv=None):
if not errors:
print(" OK: 符合 database-table-definition-spec偏离 %d 条已登记)" % len(deviations))
print("=" * 60)
# QC #3逐规则计数不依赖中间文件即可复核「真·非抽象类型仍被拦截、抽象类型未放水」
for tag, cnt in sorted(rule_counts.items(), key=lambda kv: (-kv[1], kv[0])):
print("RULE_STATS: %s=%d" % (tag, cnt))
print("RULE_STATS_TOTAL: errors=%d files=%d" % (total_errors, total_files))
print("files=%d errors=%d registered_deviations=%d" % (total_files, total_errors, total_deviations))
print("RESULT: %s" % ("PASS" if total_errors == 0 else "FAIL"))
if args.json_report: