deliver: 交付收口(引擎代为提交)
This commit is contained in:
parent
f9a9162312
commit
27a0aa17db
@ -5,14 +5,26 @@
|
||||
PM 裁决 B 的执行要求:「引用后跑双向列集 diff 留证(world_sync 消费方代码若依赖旧结构,
|
||||
适配到权威结构)」。
|
||||
|
||||
为避免把 Python 标识符误当列名,只从代码中的 SQL 字符串字面量里提取列名:
|
||||
方向定义:
|
||||
方向 1(代码 -> 权威):SQL 字面量中出现的列名必须全部存在于权威列集(缺则 FAIL);
|
||||
方向 2(权威 -> 代码):权威列集中未被任何 SQL 引用的列,打印为 informational
|
||||
(由自增/默认值/写入方填充,不判失败)。
|
||||
|
||||
M11b-2b-B1 修正(标识符误判):旧版把「文档字符串里叙述的旧列名/类名/函数名」以及
|
||||
DB-API 属性、索引名等 Python 侧标识符当成 SQL 列名,导致方向 1 误报 FAIL
|
||||
(AuthorityResolutionError、ConcurrentStateConflict、write_event_with_state、
|
||||
_assert_contract、execute、format、json、paramstyle、pyformat、qmark、rowcount、
|
||||
expected_version、uk_es、event_id/updated_by_event 的文档叙述等)。修正为四层过滤:
|
||||
L1 语句级:字面量必须是真 SQL 语句(以 SQL 动词开头或含 INSERT INTO ... / SELECT ... FROM 签名),
|
||||
且排除 ast 识别出的 docstring(文档叙述不是代码引用);
|
||||
L2 黑名单:KNOWN_NON_COLUMN_IDENTIFIERS(类名/函数名/DB-API 属性/驱动 paramstyle/索引名/内置模块名);
|
||||
L3 停用词:STOPWORDS(SQL 语法词、函数词、驱动方法名);
|
||||
L4 形态规则:首字母大写=类名、下划线前缀=私有成员、uk_/ix_/idx_/uq_ 前缀=索引名。
|
||||
|
||||
用法:python3 modules/world_sync/scripts/m11b2b_column_diff.py
|
||||
"""
|
||||
|
||||
import ast
|
||||
import io
|
||||
import json
|
||||
import os
|
||||
@ -33,15 +45,50 @@ AUTHORITY = {
|
||||
|
||||
SQL_HINT = re.compile(r"\b(select|insert\s+into|update|delete\s+from|on\s+duplicate)\b", re.IGNORECASE)
|
||||
|
||||
# SQL 语法词/函数词,避免误判为列名
|
||||
# L1a:真 SQL 语句的形态签名
|
||||
SQL_STATEMENT_START = re.compile(
|
||||
r"^\s*(select|insert\s+into|update|delete\s+from|with|for\s+update|lock\s+in\s+share|"
|
||||
r"begin|commit|rollback|set\s+transaction|create\s+table|alter\s+table)\b",
|
||||
re.IGNORECASE)
|
||||
SQL_STATEMENT_SIGNATURE = re.compile(
|
||||
r"(\binsert\s+into\s+[\w`.]+\s*\()|(\bselect\b[\s\S]+\bfrom\b)|(\bupdate\s+[\w`.]+\s+set\b)|"
|
||||
r"(\bdelete\s+from\b)",
|
||||
re.IGNORECASE)
|
||||
|
||||
# SQL 语法词/函数词/驱动方法名,避免误判为列名
|
||||
STOPWORDS = {
|
||||
"select", "from", "where", "insert", "into", "values", "update", "set", "delete",
|
||||
"and", "or", "not", "null", "is", "in", "on", "as", "by", "order", "group", "limit",
|
||||
"join", "left", "right", "inner", "outer", "distinct", "count", "max", "min", "sum",
|
||||
"now", "current_timestamp", "true", "false", "begin", "commit", "rollback", "with",
|
||||
"returning", "if", "exists", "duplicate", "key", "auto_increment", "using",
|
||||
"returning", "if", "exists", "duplicate", "key", "auto_increment", "using", "lock",
|
||||
"share", "for", "transaction", "level", "read", "committed",
|
||||
# 驱动/内置模块与游标方法名(f-string 残留或文档叙述会被抓成标识符)
|
||||
"json", "format", "execute", "executemany", "cursor", "connection", "fetchall",
|
||||
"fetchone", "description", "interval", "utc_timestamp", "date_add", "rowcount",
|
||||
}
|
||||
|
||||
# L2:已确认的非列标识符(Python 类名 / 函数名 / DB-API 属性 / 索引名 / 内置模块名)。
|
||||
KNOWN_NON_COLUMN_IDENTIFIERS = {
|
||||
# 类名 / 异常名
|
||||
"AuthorityResolutionError", "ConcurrentStateConflict", "ConcurrentConflictError",
|
||||
"WorldSyncError", "RuntimeContractError",
|
||||
# 函数 / 方法名
|
||||
"write_event_with_state", "_assert_contract", "execute", "executemany", "format",
|
||||
"new_event_id", "new_event_uid", "get_module_dbname", "load_world_sync",
|
||||
"columns_for", "event_columns", "state_columns",
|
||||
# 内置模块 / 对象名
|
||||
"json", "os", "sys", "re", "time", "datetime", "ast", "io",
|
||||
# DB-API 2.0 属性与参数风格
|
||||
"paramstyle", "pyformat", "qformat", "qmark", "numeric", "named", "string_format",
|
||||
"expected_version", "rowcount", "lastrowid",
|
||||
# 索引名(前缀形态之外的显式登记)
|
||||
"uk_es", "uk_pre", "ix_es", "ix_tx_group", "ix_created_at",
|
||||
}
|
||||
|
||||
# L4:形态规则
|
||||
INDEX_NAME_PREFIX = re.compile(r"^(uk_|ix_|idx_|uq_)")
|
||||
|
||||
TABLE_WORDS = {"pbl_runtime_event", "pbl_entity_state"}
|
||||
|
||||
|
||||
@ -64,10 +111,36 @@ def authority_columns(root, table):
|
||||
return [f.get("name") for f in (data.get("fields") or []) if isinstance(f, dict) and f.get("name")]
|
||||
|
||||
|
||||
def docstrings_of(path):
|
||||
"""L1b:收集模块/类/函数的 docstring 文本,文档叙述不参与列集比对。"""
|
||||
try:
|
||||
with open(path, "r", encoding="utf-8") as fh:
|
||||
tree = ast.parse(fh.read(), filename=path)
|
||||
except Exception: # noqa: BLE001
|
||||
return set()
|
||||
out = set()
|
||||
for node in ast.walk(tree):
|
||||
if isinstance(node, (ast.Module, ast.ClassDef, ast.FunctionDef, ast.AsyncFunctionDef)):
|
||||
doc = ast.get_docstring(node, clean=False)
|
||||
if doc:
|
||||
out.add(doc.strip())
|
||||
return out
|
||||
|
||||
|
||||
def looks_like_sql_statement(val):
|
||||
"""L1a:必须以 SQL 动词开头,或具备完整语句签名(排除散文式叙述)。"""
|
||||
if not SQL_HINT.search(val):
|
||||
return False
|
||||
if SQL_STATEMENT_START.match(val):
|
||||
return True
|
||||
return bool(SQL_STATEMENT_SIGNATURE.search(val))
|
||||
|
||||
|
||||
def sql_literals(path):
|
||||
"""用 tokenize 取出文件中的字符串字面量,仅保留含 SQL 关键字者。"""
|
||||
"""用 tokenize 取出文件中的字符串字面量,仅保留真 SQL 语句(排除 docstring)。"""
|
||||
with open(path, "rb") as fh:
|
||||
raw = fh.read()
|
||||
docs = docstrings_of(path)
|
||||
out = []
|
||||
try:
|
||||
for tok in tokenize.tokenize(io.BytesIO(raw).readline):
|
||||
@ -77,8 +150,13 @@ def sql_literals(path):
|
||||
val = ast_literal_eval(tok.string)
|
||||
except Exception: # noqa: BLE001
|
||||
continue
|
||||
if isinstance(val, str) and SQL_HINT.search(val):
|
||||
out.append(val)
|
||||
if not isinstance(val, str):
|
||||
continue
|
||||
if val.strip() in docs: # L1b 文档叙述
|
||||
continue
|
||||
if not looks_like_sql_statement(val): # L1a 非语句形态
|
||||
continue
|
||||
out.append(val)
|
||||
except (tokenize.TokenError, IndentationError, SyntaxError):
|
||||
pass
|
||||
return out
|
||||
@ -87,7 +165,6 @@ def sql_literals(path):
|
||||
def ast_literal_eval(src):
|
||||
"""轻量解析字符串字面量(含 f-string 前缀),失败返回 None。"""
|
||||
s = src
|
||||
# 去掉 f/r/b 等前缀
|
||||
m = re.match(r"^([a-zA-Z]*)", s)
|
||||
prefix = m.group(1).lower() if m else ""
|
||||
body = s[len(prefix):]
|
||||
@ -107,6 +184,23 @@ def ast_literal_eval(src):
|
||||
return None
|
||||
|
||||
|
||||
def is_non_column_identifier(tok):
|
||||
"""L2/L3/L4 标识符过滤:黑名单 -> 停用词 -> 形态规则。"""
|
||||
if not tok:
|
||||
return True
|
||||
if tok in KNOWN_NON_COLUMN_IDENTIFIERS:
|
||||
return True
|
||||
if tok.lower() in STOPWORDS:
|
||||
return True
|
||||
if tok[0].isupper(): # 类名/异常名(列名规范为小写下划线)
|
||||
return True
|
||||
if tok.startswith("_"): # 私有成员
|
||||
return True
|
||||
if INDEX_NAME_PREFIX.match(tok.lower()): # 索引名
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def columns_from_sql(sql):
|
||||
cols = set()
|
||||
cols.update(re.findall(r"`([a-zA-Z_][a-zA-Z_0-9]*)`", sql))
|
||||
@ -127,7 +221,7 @@ def columns_from_sql(sql):
|
||||
cols.add(tok)
|
||||
for tok in re.findall(r"\b(?:pbl_runtime_event|pbl_entity_state)\.([a-zA-Z_][a-zA-Z_0-9]*)", sql):
|
||||
cols.add(tok)
|
||||
return {c for c in cols if c.lower() not in STOPWORDS and c not in TABLE_WORDS}
|
||||
return {c for c in cols if c not in TABLE_WORDS and not is_non_column_identifier(c)}
|
||||
|
||||
|
||||
def main():
|
||||
@ -148,7 +242,10 @@ def main():
|
||||
for s in lits:
|
||||
ident |= columns_from_sql(s)
|
||||
print("consumer files scanned: %d -> %s" % (len(scanned), scanned))
|
||||
print("SQL literals found: %d, distinct column identifiers: %d" % (len(sql_total), len(ident)))
|
||||
print("SQL statement literals kept: %d, distinct column identifiers: %d" % (len(sql_total), len(ident)))
|
||||
print("identifier filter layers: L1 statement-gate+docstring-exclusion, "
|
||||
"L2 known_non_column=%d, L3 stopwords=%d, L4 shape_rules=3"
|
||||
% (len(KNOWN_NON_COLUMN_IDENTIFIERS), len(STOPWORDS)))
|
||||
|
||||
auth_cols = {}
|
||||
for table in AUTHORITY:
|
||||
|
||||
@ -1,36 +1,23 @@
|
||||
workspace root: /d/pipeline/workspaces/0/sdlc_general
|
||||
SKIP (absent): modules/world_sync/world_sync/pbl_runtime_schema.py
|
||||
consumer files scanned: 2 -> ['modules/world_sync/world_sync/pbl_runtime_sql.py', 'modules/world_sync/world_sync/pbl_runtime_tx.py']
|
||||
SQL literals found: 9, distinct column identifiers: 18
|
||||
SQL statement literals kept: 6, distinct column identifiers: 5
|
||||
identifier filter layers: L1 statement-gate+docstring-exclusion, L2 known_non_column=40, L3 stopwords=66, L4 shape_rules=3
|
||||
|
||||
authority columns [pbl_runtime_event] (25 cols): ['id', 'tenant_id', 'event_uid', 'event_code', 'idem_key', 'world_id', 'scene_id', 'session_id', 'entity_id', 'actor_id', 'event_type', 'payload', 'payload_json', 'causation_id', 'seq', 'seq_no', 'source', 'state', 'state_version', 'tx_group', 'broadcast', 'created_by', 'created_at', 'updated_at', 'occurred_at']
|
||||
|
||||
authority columns [pbl_entity_state] (10 cols): ['tenant_id', 'id', 'session_id', 'entity_id', 'state_json', 'state_version', 'checksum', 'updated_by', 'created_at', 'updated_at']
|
||||
|
||||
--- 方向 1: 代码 SQL 引用列 ⊆ 权威列集(必须全部成立)---
|
||||
AuthorityResolutionError MISS (不在任何权威列集)
|
||||
ConcurrentStateConflict MISS (不在任何权威列集)
|
||||
_assert_contract MISS (不在任何权威列集)
|
||||
execute MISS (不在任何权威列集)
|
||||
expected_version MISS (不在任何权威列集)
|
||||
format MISS (不在任何权威列集)
|
||||
json MISS (不在任何权威列集)
|
||||
paramstyle MISS (不在任何权威列集)
|
||||
pyformat MISS (不在任何权威列集)
|
||||
qmark MISS (不在任何权威列集)
|
||||
rowcount MISS (不在任何权威列集)
|
||||
session_id OK ->pbl_runtime_event,pbl_entity_state
|
||||
state_json OK ->pbl_entity_state
|
||||
state_version OK ->pbl_runtime_event,pbl_entity_state
|
||||
tenant_id OK ->pbl_runtime_event,pbl_entity_state
|
||||
uk_es MISS (不在任何权威列集)
|
||||
updated_at OK ->pbl_runtime_event,pbl_entity_state
|
||||
write_event_with_state MISS (不在任何权威列集)
|
||||
|
||||
--- 方向 2: 权威列集中未被 SQL 显式引用者(informational)---
|
||||
[pbl_runtime_event] unreferenced=21 ['id', 'event_uid', 'event_code', 'idem_key', 'world_id', 'scene_id', 'entity_id', 'actor_id', 'event_type', 'payload', 'payload_json', 'causation_id', 'seq', 'seq_no', 'source', 'state', 'tx_group', 'broadcast', 'created_by', 'created_at', 'occurred_at']
|
||||
[pbl_entity_state] unreferenced=5 ['id', 'entity_id', 'checksum', 'updated_by', 'created_at']
|
||||
|
||||
code columns checked: 18, missing from authority: 13
|
||||
MISSING: ['AuthorityResolutionError', 'ConcurrentStateConflict', '_assert_contract', 'execute', 'expected_version', 'format', 'json', 'paramstyle', 'pyformat', 'qmark', 'rowcount', 'uk_es', 'write_event_with_state']
|
||||
RESULT: FAIL
|
||||
code columns checked: 5, missing from authority: 0
|
||||
RESULT: PASS
|
||||
|
||||
@ -1,20 +1,31 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""M11b-2b 自测:表定义 JSON 符合 database-table-definition-spec(PM 裁决 B:key=schema 引用)。
|
||||
"""M11b-2b 自测:表定义 JSON 符合 database-table-definition-spec(四段式内联口径)。
|
||||
|
||||
口径变更(M11b-2b-B1):world_sync/models/pbl_runtime_event.json、pbl_entity_state.json
|
||||
已由「key=schema 引用式」改为**四段式内联**(summary/fields/indexes/codes),列集与权威真源
|
||||
modules/pbl_runtime_ext/models/*.json 内容级对齐,由 m11b2b_column_diff.py 做双向列集 diff 留证,
|
||||
不再靠 authority_path 运行时引用。本脚本断言口径随之更新。
|
||||
|
||||
覆盖全部 4 个落点(world_sync 真源 2 + 应用打包镜像 2),任一不符即 RESULT: FAIL。
|
||||
|
||||
断言集(判定项,计入 RESULT):
|
||||
A1 JSON 可解析;
|
||||
A2 根键 == {"key","schema"} 且 key == "schema"(引用形态,不再自带结构体);
|
||||
A3 引用体内不含 summary/fields/indexes/codes 结构体(列集以 authority_path 为唯一真源);
|
||||
A4 各段键集合 ⊆ 白名单(引用体键白名单;禁止把元信息藏在标准结构体里);
|
||||
A5 authority_module/authority_path/table 必填,且 authority_path 指向的真源文件真实存在;
|
||||
A1 JSON 可解析且根为对象;
|
||||
A2 根键 == {"summary","fields","indexes","codes"}(四段式,不再带 key/schema 引用外壳);
|
||||
A3 fields 段非空(四段式产物必须自带列结构体,列集内联落地;旧「不得自带结构体」检查废除);
|
||||
A4 fields[]/indexes[]/codes[] 逐条键集合 ⊆ spec 白名单
|
||||
(SPEC_FIELD_KEYS / SPEC_INDEX_KEYS / SPEC_CODE_KEYS;禁止把私有元信息藏进标准结构体条目里);
|
||||
A5 summary[0].name 非空且 == 文件名去 .json(表名与文件名一致;旧 authority_module/
|
||||
authority_path/table 必填检查随引用式口径一并废除);
|
||||
A7 真源与镜像 sha256 两两相同(禁止内容级分叉)。
|
||||
|
||||
U 段(上游体检项,不计入本任务 RESULT,但逐条打印 DEFECT 供冒泡留证):
|
||||
A6 权威真源 modules/pbl_runtime_ext/models/*.json 自身对 spec 四段式白名单/抽象类型的符合性。
|
||||
—— 该侧为 M11b-1 已批准产物,本任务按 PM 裁决「不得反向修改」,故只体检登记不判定。
|
||||
|
||||
信息性打印(不计入判定):summary 段条目上超出 SPEC_SUMMARY_KEYS 的表级元信息键
|
||||
(owner/milestone/decision_ref/decision_basis/structure_note/consumers/mirror_note 等)
|
||||
只打印 info 提示——表级元信息允许登记在 summary 记录上,不作为失败判定。
|
||||
"""
|
||||
|
||||
import hashlib
|
||||
@ -34,13 +45,6 @@ APP_MIRROR_DIR = os.path.join("apps", "scense", "pkgs", "world_sync", "models")
|
||||
# 权威真源(pbl_runtime_ext,M11b-1 已批准产物,本任务只读体检)
|
||||
AUTHORITY_DIR = "modules/pbl_runtime_ext/models"
|
||||
|
||||
# 引用体允许出现的键(表级元信息全部登记在此白名单内,不得散落到结构体里)
|
||||
REFERENCE_KEY_WHITELIST = {
|
||||
"table", "authority_module", "authority_path", "owner", "milestone",
|
||||
"decision_ref", "decision_basis", "structure_note", "consumers", "mirror_note",
|
||||
"comment", "note",
|
||||
}
|
||||
|
||||
# database-table-definition-spec 四段式与各段键白名单
|
||||
SPEC_ROOT_KEYS = {"summary", "fields", "indexes", "codes"}
|
||||
SPEC_SUMMARY_KEYS = {"name", "title", "primary", "catelog"}
|
||||
@ -55,7 +59,7 @@ ABSTRACT_TYPES = {
|
||||
NEED_LENGTH = {"str", "char", "float", "double", "ddouble", "decimal"}
|
||||
NEED_DEC = {"float", "double", "ddouble", "decimal"}
|
||||
|
||||
_failures = [] # 判定项失败
|
||||
_failures = [] # 判定项失败
|
||||
_upstream_defects = [] # 上游体检项缺陷(登记不判定)
|
||||
_assert_count = 0
|
||||
_upstream_count = 0
|
||||
@ -109,31 +113,109 @@ def load_json(path):
|
||||
return json.load(fh)
|
||||
|
||||
|
||||
def validate_reference(path, root):
|
||||
"""A1~A5:world_sync 侧引用文件(真源与镜像执行同一套断言)。"""
|
||||
print("== reference: %s" % path)
|
||||
def as_list(val):
|
||||
return val if isinstance(val, list) else []
|
||||
|
||||
|
||||
def stem_of(path):
|
||||
base = os.path.basename(path)
|
||||
return base[:-5] if base.endswith(".json") else base
|
||||
|
||||
|
||||
def validate_spec(path):
|
||||
"""A1~A5:world_sync 侧四段式表定义(真源与镜像执行同一套断言)。"""
|
||||
print("== spec(inline four-section): %s" % path)
|
||||
try:
|
||||
data = load_json(path)
|
||||
except Exception as exc: # noqa: BLE001
|
||||
check(False, "A1 json-parseable (%s)" % exc)
|
||||
return None
|
||||
check(isinstance(data, dict), "A1 json-is-object")
|
||||
check(set(data.keys()) == {"key", "schema"}, "A2 root-keys == {key,schema} (got %s)" % sorted(data.keys()))
|
||||
check(data.get("key") == "schema", 'A2 key == "schema"')
|
||||
body = data.get("schema")
|
||||
if not check(isinstance(body, dict), "A2 schema-is-object"):
|
||||
if not check(isinstance(data, dict), "A1 json-is-object"):
|
||||
return None
|
||||
for seg in ("summary", "fields", "indexes", "codes"):
|
||||
check(seg not in body, "A3 no self-carried struct: %s" % seg)
|
||||
extra = set(body.keys()) - REFERENCE_KEY_WHITELIST
|
||||
check(not extra, "A4 reference keys subset whitelist (extra=%s)" % sorted(extra))
|
||||
check(bool(str(body.get("table", "")).strip()), "A5 table non-empty")
|
||||
check(body.get("authority_module") == "pbl_runtime_ext", "A5 authority_module == pbl_runtime_ext")
|
||||
apath = body.get("authority_path", "")
|
||||
check(bool(apath), "A5 authority_path non-empty")
|
||||
if apath:
|
||||
check(os.path.isfile(os.path.join(root, apath)), "A5 authority file exists: %s" % apath)
|
||||
return body
|
||||
|
||||
# --- A2 根键必须恰好是四段式(引用式外壳 key/schema 视为不合规)---
|
||||
check(set(data.keys()) == SPEC_ROOT_KEYS,
|
||||
"A2 root-keys == {summary,fields,indexes,codes} (got %s)" % sorted(data.keys()))
|
||||
|
||||
summary = as_list(data.get("summary"))
|
||||
fields = as_list(data.get("fields"))
|
||||
indexes = as_list(data.get("indexes"))
|
||||
codes = as_list(data.get("codes"))
|
||||
|
||||
check(len(summary) >= 1, "A2 summary non-empty array (got %d)" % len(summary))
|
||||
check(isinstance(data.get("fields"), list), "A2 fields is array")
|
||||
check(isinstance(data.get("indexes"), list), "A2 indexes is array")
|
||||
check(isinstance(data.get("codes"), list), "A2 codes is array")
|
||||
|
||||
# --- A3 列结构体内联落地:fields 必须非空(旧口径「引用体内不含结构体」检查已废除)---
|
||||
if not check(len(fields) > 0, "A3 fields non-empty (inline column set, got %d)" % len(fields)):
|
||||
return data
|
||||
|
||||
# --- A4 各段条目键集合 ⊆ spec 白名单 ---
|
||||
for s_i, s_rec in enumerate(summary):
|
||||
if not isinstance(s_rec, dict):
|
||||
check(False, "A4 summary[%d] is object" % s_i)
|
||||
continue
|
||||
extra = set(s_rec.keys()) - SPEC_SUMMARY_KEYS
|
||||
if extra:
|
||||
print(" info summary[%d] table-level meta keys (not judged): %s" % (s_i, sorted(extra)))
|
||||
|
||||
field_names = []
|
||||
for f in fields:
|
||||
if not isinstance(f, dict):
|
||||
check(False, "A4 field entry is object (%r)" % f)
|
||||
continue
|
||||
fname = str(f.get("name", "?"))
|
||||
field_names.append(fname)
|
||||
extra = set(f.keys()) - SPEC_FIELD_KEYS
|
||||
check(not extra, "A4[%s] field keys subset whitelist (extra=%s)" % (fname, sorted(extra)))
|
||||
check(fname != "?", "A4[%s] field.name present" % fname)
|
||||
|
||||
for idx in indexes:
|
||||
if not isinstance(idx, dict):
|
||||
check(False, "A4 index entry is object (%r)" % idx)
|
||||
continue
|
||||
iname = str(idx.get("name", "?"))
|
||||
extra = set(idx.keys()) - SPEC_INDEX_KEYS
|
||||
check(not extra, "A4[%s] index keys subset whitelist (extra=%s)" % (iname, sorted(extra)))
|
||||
|
||||
for code in codes:
|
||||
if not isinstance(code, dict):
|
||||
check(False, "A4 code entry is object (%r)" % code)
|
||||
continue
|
||||
extra = set(code.keys()) - SPEC_CODE_KEYS
|
||||
check(not extra, "A4[code:%s] keys subset whitelist (extra=%s)" % (code.get("field", "?"), sorted(extra)))
|
||||
|
||||
# 列集自洽性(附加判定:内联后本文件自身必须可独立建表)
|
||||
for idx in indexes:
|
||||
if not isinstance(idx, dict):
|
||||
continue
|
||||
iname = str(idx.get("name", "?"))
|
||||
for ifld in as_list(idx.get("idxfields")):
|
||||
check(ifld in field_names, "A4[%s] idxfield present in fields: %s" % (iname, ifld))
|
||||
check(idx.get("idxtype") in ("unique", "index"), "A4[%s] idxtype unique|index (%r)" % (iname, idx.get("idxtype")))
|
||||
|
||||
for f in fields:
|
||||
if not isinstance(f, dict):
|
||||
continue
|
||||
fname = str(f.get("name", "?"))
|
||||
t = f.get("type")
|
||||
check(t in ABSTRACT_TYPES, "A3[%s] abstract type (%r)" % (fname, t))
|
||||
if t in NEED_LENGTH:
|
||||
check(isinstance(f.get("length"), int) and f.get("length") > 0, "A3[%s] positive int length" % fname)
|
||||
if t in NEED_DEC:
|
||||
check(isinstance(f.get("dec"), int) and f.get("dec") > 0, "A3[%s] positive int dec" % fname)
|
||||
check(str(f.get("nullable", "no")) in ("yes", "no"), "A3[%s] nullable in yes|no" % fname)
|
||||
|
||||
# --- A5 表名一致性:summary[0].name 非空且 == 文件名去 .json(旧引用字段检查已废除)---
|
||||
expected_name = stem_of(path)
|
||||
if not check(len(summary) >= 1 and isinstance(summary[0], dict), "A5 summary[0] is object"):
|
||||
return data
|
||||
s0_name = str(summary[0].get("name", "")).strip()
|
||||
check(bool(s0_name), "A5 summary[0].name non-empty")
|
||||
check(s0_name == expected_name,
|
||||
"A5 summary[0].name == filename stem (%r vs %r)" % (s0_name, expected_name))
|
||||
return data
|
||||
|
||||
|
||||
def validate_authority(path):
|
||||
@ -213,12 +295,12 @@ def main():
|
||||
paths.append(os.path.join(root, MODULE_SRC_DIR, name))
|
||||
paths.append(os.path.join(root, APP_MIRROR_DIR, name))
|
||||
|
||||
print("\n--- A1~A5: 4 个落点全部执行同一套 spec 断言 ---")
|
||||
print("\n--- A1~A5: 4 个落点全部执行同一套四段式 spec 断言 ---")
|
||||
for p in paths:
|
||||
if not os.path.isfile(p):
|
||||
check(False, "file exists: %s" % p)
|
||||
continue
|
||||
validate_reference(p, root)
|
||||
validate_spec(p)
|
||||
|
||||
print("\n--- A7: 真源与镜像 sha256 两两相同 ---")
|
||||
for name in SOURCE_TABLES:
|
||||
|
||||
@ -1,71 +1,459 @@
|
||||
workspace root: /d/pipeline/workspaces/0/sdlc_general
|
||||
|
||||
--- A1~A5: 4 个落点全部执行同一套 spec 断言 ---
|
||||
== reference: /d/pipeline/workspaces/0/sdlc_general/modules/world_sync/models/pbl_runtime_event.json
|
||||
--- A1~A5: 4 个落点全部执行同一套四段式 spec 断言 ---
|
||||
== spec(inline four-section): /d/pipeline/workspaces/0/sdlc_general/modules/world_sync/models/pbl_runtime_event.json
|
||||
ok A1 json-is-object
|
||||
ok A2 root-keys == {key,schema} (got ['key', 'schema'])
|
||||
ok A2 key == "schema"
|
||||
ok A2 schema-is-object
|
||||
ok A3 no self-carried struct: summary
|
||||
ok A3 no self-carried struct: fields
|
||||
ok A3 no self-carried struct: indexes
|
||||
ok A3 no self-carried struct: codes
|
||||
ok A4 reference keys subset whitelist (extra=[])
|
||||
ok A5 table non-empty
|
||||
ok A5 authority_module == pbl_runtime_ext
|
||||
ok A5 authority_path non-empty
|
||||
ok A5 authority file exists: modules/pbl_runtime_ext/models/pbl_runtime_event.json
|
||||
== reference: /d/pipeline/workspaces/0/sdlc_general/apps/scense/pkgs/world_sync/models/pbl_runtime_event.json
|
||||
ok A2 root-keys == {summary,fields,indexes,codes} (got ['codes', 'fields', 'indexes', 'summary'])
|
||||
ok A2 summary non-empty array (got 1)
|
||||
ok A2 fields is array
|
||||
ok A2 indexes is array
|
||||
ok A2 codes is array
|
||||
ok A3 fields non-empty (inline column set, got 25)
|
||||
info summary[0] table-level meta keys (not judged): ['comment']
|
||||
ok A4[id] field keys subset whitelist (extra=[])
|
||||
ok A4[id] field.name present
|
||||
ok A4[tenant_id] field keys subset whitelist (extra=[])
|
||||
ok A4[tenant_id] field.name present
|
||||
ok A4[event_uid] field keys subset whitelist (extra=[])
|
||||
ok A4[event_uid] field.name present
|
||||
ok A4[event_code] field keys subset whitelist (extra=[])
|
||||
ok A4[event_code] field.name present
|
||||
ok A4[idem_key] field keys subset whitelist (extra=[])
|
||||
ok A4[idem_key] field.name present
|
||||
ok A4[world_id] field keys subset whitelist (extra=[])
|
||||
ok A4[world_id] field.name present
|
||||
ok A4[scene_id] field keys subset whitelist (extra=[])
|
||||
ok A4[scene_id] field.name present
|
||||
ok A4[session_id] field keys subset whitelist (extra=[])
|
||||
ok A4[session_id] field.name present
|
||||
ok A4[entity_id] field keys subset whitelist (extra=[])
|
||||
ok A4[entity_id] field.name present
|
||||
ok A4[actor_id] field keys subset whitelist (extra=[])
|
||||
ok A4[actor_id] field.name present
|
||||
ok A4[event_type] field keys subset whitelist (extra=[])
|
||||
ok A4[event_type] field.name present
|
||||
ok A4[payload] field keys subset whitelist (extra=[])
|
||||
ok A4[payload] field.name present
|
||||
ok A4[payload_json] field keys subset whitelist (extra=[])
|
||||
ok A4[payload_json] field.name present
|
||||
ok A4[causation_id] field keys subset whitelist (extra=[])
|
||||
ok A4[causation_id] field.name present
|
||||
ok A4[seq] field keys subset whitelist (extra=[])
|
||||
ok A4[seq] field.name present
|
||||
ok A4[seq_no] field keys subset whitelist (extra=[])
|
||||
ok A4[seq_no] field.name present
|
||||
ok A4[source] field keys subset whitelist (extra=[])
|
||||
ok A4[source] field.name present
|
||||
ok A4[state] field keys subset whitelist (extra=[])
|
||||
ok A4[state] field.name present
|
||||
ok A4[state_version] field keys subset whitelist (extra=[])
|
||||
ok A4[state_version] field.name present
|
||||
ok A4[tx_group] field keys subset whitelist (extra=[])
|
||||
ok A4[tx_group] field.name present
|
||||
ok A4[broadcast] field keys subset whitelist (extra=[])
|
||||
ok A4[broadcast] field.name present
|
||||
ok A4[created_by] field keys subset whitelist (extra=[])
|
||||
ok A4[created_by] field.name present
|
||||
ok A4[created_at] field keys subset whitelist (extra=[])
|
||||
ok A4[created_at] field.name present
|
||||
ok A4[updated_at] field keys subset whitelist (extra=[])
|
||||
ok A4[updated_at] field.name present
|
||||
ok A4[occurred_at] field keys subset whitelist (extra=[])
|
||||
ok A4[occurred_at] field.name present
|
||||
ok A4[uk_event_uid] index keys subset whitelist (extra=[])
|
||||
ok A4[uk_tenant_idem] index keys subset whitelist (extra=[])
|
||||
ok A4[ix_scene_created] index keys subset whitelist (extra=[])
|
||||
ok A4[ix_tenant_session_seq] index keys subset whitelist (extra=[])
|
||||
ok A4[ix_tenant_session_seqno] index keys subset whitelist (extra=[])
|
||||
ok A4[ix_tenant_world_type] index keys subset whitelist (extra=[])
|
||||
ok A4[ix_tx_group] index keys subset whitelist (extra=[])
|
||||
ok A4[ix_created_at] index keys subset whitelist (extra=[])
|
||||
ok A4[code:event_type] keys subset whitelist (extra=[])
|
||||
ok A4[uk_event_uid] idxfield present in fields: tenant_id
|
||||
ok A4[uk_event_uid] idxfield present in fields: event_uid
|
||||
ok A4[uk_event_uid] idxfield present in fields: created_at
|
||||
ok A4[uk_event_uid] idxtype unique|index ('unique')
|
||||
ok A4[uk_tenant_idem] idxfield present in fields: tenant_id
|
||||
ok A4[uk_tenant_idem] idxfield present in fields: idem_key
|
||||
ok A4[uk_tenant_idem] idxfield present in fields: created_at
|
||||
ok A4[uk_tenant_idem] idxtype unique|index ('unique')
|
||||
ok A4[ix_scene_created] idxfield present in fields: scene_id
|
||||
ok A4[ix_scene_created] idxfield present in fields: created_at
|
||||
ok A4[ix_scene_created] idxtype unique|index ('index')
|
||||
ok A4[ix_tenant_session_seq] idxfield present in fields: tenant_id
|
||||
ok A4[ix_tenant_session_seq] idxfield present in fields: session_id
|
||||
ok A4[ix_tenant_session_seq] idxfield present in fields: seq
|
||||
ok A4[ix_tenant_session_seq] idxtype unique|index ('index')
|
||||
ok A4[ix_tenant_session_seqno] idxfield present in fields: tenant_id
|
||||
ok A4[ix_tenant_session_seqno] idxfield present in fields: session_id
|
||||
ok A4[ix_tenant_session_seqno] idxfield present in fields: seq_no
|
||||
ok A4[ix_tenant_session_seqno] idxtype unique|index ('index')
|
||||
ok A4[ix_tenant_world_type] idxfield present in fields: tenant_id
|
||||
ok A4[ix_tenant_world_type] idxfield present in fields: world_id
|
||||
ok A4[ix_tenant_world_type] idxfield present in fields: event_type
|
||||
ok A4[ix_tenant_world_type] idxtype unique|index ('index')
|
||||
ok A4[ix_tx_group] idxfield present in fields: tx_group
|
||||
ok A4[ix_tx_group] idxtype unique|index ('index')
|
||||
ok A4[ix_created_at] idxfield present in fields: created_at
|
||||
ok A4[ix_created_at] idxtype unique|index ('index')
|
||||
ok A3[id] abstract type ('str')
|
||||
ok A3[id] positive int length
|
||||
ok A3[id] nullable in yes|no
|
||||
ok A3[tenant_id] abstract type ('str')
|
||||
ok A3[tenant_id] positive int length
|
||||
ok A3[tenant_id] nullable in yes|no
|
||||
ok A3[event_uid] abstract type ('str')
|
||||
ok A3[event_uid] positive int length
|
||||
ok A3[event_uid] nullable in yes|no
|
||||
ok A3[event_code] abstract type ('str')
|
||||
ok A3[event_code] positive int length
|
||||
ok A3[event_code] nullable in yes|no
|
||||
ok A3[idem_key] abstract type ('str')
|
||||
ok A3[idem_key] positive int length
|
||||
ok A3[idem_key] nullable in yes|no
|
||||
ok A3[world_id] abstract type ('long')
|
||||
ok A3[world_id] nullable in yes|no
|
||||
ok A3[scene_id] abstract type ('long')
|
||||
ok A3[scene_id] nullable in yes|no
|
||||
ok A3[session_id] abstract type ('long')
|
||||
ok A3[session_id] nullable in yes|no
|
||||
ok A3[entity_id] abstract type ('str')
|
||||
ok A3[entity_id] positive int length
|
||||
ok A3[entity_id] nullable in yes|no
|
||||
ok A3[actor_id] abstract type ('str')
|
||||
ok A3[actor_id] positive int length
|
||||
ok A3[actor_id] nullable in yes|no
|
||||
ok A3[event_type] abstract type ('str')
|
||||
ok A3[event_type] positive int length
|
||||
ok A3[event_type] nullable in yes|no
|
||||
ok A3[payload] abstract type ('text')
|
||||
ok A3[payload] nullable in yes|no
|
||||
ok A3[payload_json] abstract type ('text')
|
||||
ok A3[payload_json] nullable in yes|no
|
||||
ok A3[causation_id] abstract type ('str')
|
||||
ok A3[causation_id] positive int length
|
||||
ok A3[causation_id] nullable in yes|no
|
||||
ok A3[seq] abstract type ('long')
|
||||
ok A3[seq] nullable in yes|no
|
||||
ok A3[seq_no] abstract type ('long')
|
||||
ok A3[seq_no] nullable in yes|no
|
||||
ok A3[source] abstract type ('str')
|
||||
ok A3[source] positive int length
|
||||
ok A3[source] nullable in yes|no
|
||||
ok A3[state] abstract type ('str')
|
||||
ok A3[state] positive int length
|
||||
ok A3[state] nullable in yes|no
|
||||
ok A3[state_version] abstract type ('long')
|
||||
ok A3[state_version] nullable in yes|no
|
||||
ok A3[tx_group] abstract type ('str')
|
||||
ok A3[tx_group] positive int length
|
||||
ok A3[tx_group] nullable in yes|no
|
||||
ok A3[broadcast] abstract type ('short')
|
||||
ok A3[broadcast] nullable in yes|no
|
||||
ok A3[created_by] abstract type ('long')
|
||||
ok A3[created_by] nullable in yes|no
|
||||
ok A3[created_at] abstract type ('datetime')
|
||||
ok A3[created_at] nullable in yes|no
|
||||
ok A3[updated_at] abstract type ('datetime')
|
||||
ok A3[updated_at] nullable in yes|no
|
||||
ok A3[occurred_at] abstract type ('datetime')
|
||||
ok A3[occurred_at] nullable in yes|no
|
||||
ok A5 summary[0] is object
|
||||
ok A5 summary[0].name non-empty
|
||||
ok A5 summary[0].name == filename stem ('pbl_runtime_event' vs 'pbl_runtime_event')
|
||||
== spec(inline four-section): /d/pipeline/workspaces/0/sdlc_general/apps/scense/pkgs/world_sync/models/pbl_runtime_event.json
|
||||
ok A1 json-is-object
|
||||
ok A2 root-keys == {key,schema} (got ['key', 'schema'])
|
||||
ok A2 key == "schema"
|
||||
ok A2 schema-is-object
|
||||
ok A3 no self-carried struct: summary
|
||||
ok A3 no self-carried struct: fields
|
||||
ok A3 no self-carried struct: indexes
|
||||
ok A3 no self-carried struct: codes
|
||||
ok A4 reference keys subset whitelist (extra=[])
|
||||
ok A5 table non-empty
|
||||
ok A5 authority_module == pbl_runtime_ext
|
||||
ok A5 authority_path non-empty
|
||||
ok A5 authority file exists: modules/pbl_runtime_ext/models/pbl_runtime_event.json
|
||||
== reference: /d/pipeline/workspaces/0/sdlc_general/modules/world_sync/models/pbl_entity_state.json
|
||||
ok A2 root-keys == {summary,fields,indexes,codes} (got ['codes', 'fields', 'indexes', 'summary'])
|
||||
ok A2 summary non-empty array (got 1)
|
||||
ok A2 fields is array
|
||||
ok A2 indexes is array
|
||||
ok A2 codes is array
|
||||
ok A3 fields non-empty (inline column set, got 25)
|
||||
info summary[0] table-level meta keys (not judged): ['comment']
|
||||
ok A4[id] field keys subset whitelist (extra=[])
|
||||
ok A4[id] field.name present
|
||||
ok A4[tenant_id] field keys subset whitelist (extra=[])
|
||||
ok A4[tenant_id] field.name present
|
||||
ok A4[event_uid] field keys subset whitelist (extra=[])
|
||||
ok A4[event_uid] field.name present
|
||||
ok A4[event_code] field keys subset whitelist (extra=[])
|
||||
ok A4[event_code] field.name present
|
||||
ok A4[idem_key] field keys subset whitelist (extra=[])
|
||||
ok A4[idem_key] field.name present
|
||||
ok A4[world_id] field keys subset whitelist (extra=[])
|
||||
ok A4[world_id] field.name present
|
||||
ok A4[scene_id] field keys subset whitelist (extra=[])
|
||||
ok A4[scene_id] field.name present
|
||||
ok A4[session_id] field keys subset whitelist (extra=[])
|
||||
ok A4[session_id] field.name present
|
||||
ok A4[entity_id] field keys subset whitelist (extra=[])
|
||||
ok A4[entity_id] field.name present
|
||||
ok A4[actor_id] field keys subset whitelist (extra=[])
|
||||
ok A4[actor_id] field.name present
|
||||
ok A4[event_type] field keys subset whitelist (extra=[])
|
||||
ok A4[event_type] field.name present
|
||||
ok A4[payload] field keys subset whitelist (extra=[])
|
||||
ok A4[payload] field.name present
|
||||
ok A4[payload_json] field keys subset whitelist (extra=[])
|
||||
ok A4[payload_json] field.name present
|
||||
ok A4[causation_id] field keys subset whitelist (extra=[])
|
||||
ok A4[causation_id] field.name present
|
||||
ok A4[seq] field keys subset whitelist (extra=[])
|
||||
ok A4[seq] field.name present
|
||||
ok A4[seq_no] field keys subset whitelist (extra=[])
|
||||
ok A4[seq_no] field.name present
|
||||
ok A4[source] field keys subset whitelist (extra=[])
|
||||
ok A4[source] field.name present
|
||||
ok A4[state] field keys subset whitelist (extra=[])
|
||||
ok A4[state] field.name present
|
||||
ok A4[state_version] field keys subset whitelist (extra=[])
|
||||
ok A4[state_version] field.name present
|
||||
ok A4[tx_group] field keys subset whitelist (extra=[])
|
||||
ok A4[tx_group] field.name present
|
||||
ok A4[broadcast] field keys subset whitelist (extra=[])
|
||||
ok A4[broadcast] field.name present
|
||||
ok A4[created_by] field keys subset whitelist (extra=[])
|
||||
ok A4[created_by] field.name present
|
||||
ok A4[created_at] field keys subset whitelist (extra=[])
|
||||
ok A4[created_at] field.name present
|
||||
ok A4[updated_at] field keys subset whitelist (extra=[])
|
||||
ok A4[updated_at] field.name present
|
||||
ok A4[occurred_at] field keys subset whitelist (extra=[])
|
||||
ok A4[occurred_at] field.name present
|
||||
ok A4[uk_event_uid] index keys subset whitelist (extra=[])
|
||||
ok A4[uk_tenant_idem] index keys subset whitelist (extra=[])
|
||||
ok A4[ix_scene_created] index keys subset whitelist (extra=[])
|
||||
ok A4[ix_tenant_session_seq] index keys subset whitelist (extra=[])
|
||||
ok A4[ix_tenant_session_seqno] index keys subset whitelist (extra=[])
|
||||
ok A4[ix_tenant_world_type] index keys subset whitelist (extra=[])
|
||||
ok A4[ix_tx_group] index keys subset whitelist (extra=[])
|
||||
ok A4[ix_created_at] index keys subset whitelist (extra=[])
|
||||
ok A4[code:event_type] keys subset whitelist (extra=[])
|
||||
ok A4[uk_event_uid] idxfield present in fields: tenant_id
|
||||
ok A4[uk_event_uid] idxfield present in fields: event_uid
|
||||
ok A4[uk_event_uid] idxfield present in fields: created_at
|
||||
ok A4[uk_event_uid] idxtype unique|index ('unique')
|
||||
ok A4[uk_tenant_idem] idxfield present in fields: tenant_id
|
||||
ok A4[uk_tenant_idem] idxfield present in fields: idem_key
|
||||
ok A4[uk_tenant_idem] idxfield present in fields: created_at
|
||||
ok A4[uk_tenant_idem] idxtype unique|index ('unique')
|
||||
ok A4[ix_scene_created] idxfield present in fields: scene_id
|
||||
ok A4[ix_scene_created] idxfield present in fields: created_at
|
||||
ok A4[ix_scene_created] idxtype unique|index ('index')
|
||||
ok A4[ix_tenant_session_seq] idxfield present in fields: tenant_id
|
||||
ok A4[ix_tenant_session_seq] idxfield present in fields: session_id
|
||||
ok A4[ix_tenant_session_seq] idxfield present in fields: seq
|
||||
ok A4[ix_tenant_session_seq] idxtype unique|index ('index')
|
||||
ok A4[ix_tenant_session_seqno] idxfield present in fields: tenant_id
|
||||
ok A4[ix_tenant_session_seqno] idxfield present in fields: session_id
|
||||
ok A4[ix_tenant_session_seqno] idxfield present in fields: seq_no
|
||||
ok A4[ix_tenant_session_seqno] idxtype unique|index ('index')
|
||||
ok A4[ix_tenant_world_type] idxfield present in fields: tenant_id
|
||||
ok A4[ix_tenant_world_type] idxfield present in fields: world_id
|
||||
ok A4[ix_tenant_world_type] idxfield present in fields: event_type
|
||||
ok A4[ix_tenant_world_type] idxtype unique|index ('index')
|
||||
ok A4[ix_tx_group] idxfield present in fields: tx_group
|
||||
ok A4[ix_tx_group] idxtype unique|index ('index')
|
||||
ok A4[ix_created_at] idxfield present in fields: created_at
|
||||
ok A4[ix_created_at] idxtype unique|index ('index')
|
||||
ok A3[id] abstract type ('str')
|
||||
ok A3[id] positive int length
|
||||
ok A3[id] nullable in yes|no
|
||||
ok A3[tenant_id] abstract type ('str')
|
||||
ok A3[tenant_id] positive int length
|
||||
ok A3[tenant_id] nullable in yes|no
|
||||
ok A3[event_uid] abstract type ('str')
|
||||
ok A3[event_uid] positive int length
|
||||
ok A3[event_uid] nullable in yes|no
|
||||
ok A3[event_code] abstract type ('str')
|
||||
ok A3[event_code] positive int length
|
||||
ok A3[event_code] nullable in yes|no
|
||||
ok A3[idem_key] abstract type ('str')
|
||||
ok A3[idem_key] positive int length
|
||||
ok A3[idem_key] nullable in yes|no
|
||||
ok A3[world_id] abstract type ('long')
|
||||
ok A3[world_id] nullable in yes|no
|
||||
ok A3[scene_id] abstract type ('long')
|
||||
ok A3[scene_id] nullable in yes|no
|
||||
ok A3[session_id] abstract type ('long')
|
||||
ok A3[session_id] nullable in yes|no
|
||||
ok A3[entity_id] abstract type ('str')
|
||||
ok A3[entity_id] positive int length
|
||||
ok A3[entity_id] nullable in yes|no
|
||||
ok A3[actor_id] abstract type ('str')
|
||||
ok A3[actor_id] positive int length
|
||||
ok A3[actor_id] nullable in yes|no
|
||||
ok A3[event_type] abstract type ('str')
|
||||
ok A3[event_type] positive int length
|
||||
ok A3[event_type] nullable in yes|no
|
||||
ok A3[payload] abstract type ('text')
|
||||
ok A3[payload] nullable in yes|no
|
||||
ok A3[payload_json] abstract type ('text')
|
||||
ok A3[payload_json] nullable in yes|no
|
||||
ok A3[causation_id] abstract type ('str')
|
||||
ok A3[causation_id] positive int length
|
||||
ok A3[causation_id] nullable in yes|no
|
||||
ok A3[seq] abstract type ('long')
|
||||
ok A3[seq] nullable in yes|no
|
||||
ok A3[seq_no] abstract type ('long')
|
||||
ok A3[seq_no] nullable in yes|no
|
||||
ok A3[source] abstract type ('str')
|
||||
ok A3[source] positive int length
|
||||
ok A3[source] nullable in yes|no
|
||||
ok A3[state] abstract type ('str')
|
||||
ok A3[state] positive int length
|
||||
ok A3[state] nullable in yes|no
|
||||
ok A3[state_version] abstract type ('long')
|
||||
ok A3[state_version] nullable in yes|no
|
||||
ok A3[tx_group] abstract type ('str')
|
||||
ok A3[tx_group] positive int length
|
||||
ok A3[tx_group] nullable in yes|no
|
||||
ok A3[broadcast] abstract type ('short')
|
||||
ok A3[broadcast] nullable in yes|no
|
||||
ok A3[created_by] abstract type ('long')
|
||||
ok A3[created_by] nullable in yes|no
|
||||
ok A3[created_at] abstract type ('datetime')
|
||||
ok A3[created_at] nullable in yes|no
|
||||
ok A3[updated_at] abstract type ('datetime')
|
||||
ok A3[updated_at] nullable in yes|no
|
||||
ok A3[occurred_at] abstract type ('datetime')
|
||||
ok A3[occurred_at] nullable in yes|no
|
||||
ok A5 summary[0] is object
|
||||
ok A5 summary[0].name non-empty
|
||||
ok A5 summary[0].name == filename stem ('pbl_runtime_event' vs 'pbl_runtime_event')
|
||||
== spec(inline four-section): /d/pipeline/workspaces/0/sdlc_general/modules/world_sync/models/pbl_entity_state.json
|
||||
ok A1 json-is-object
|
||||
ok A2 root-keys == {key,schema} (got ['key', 'schema'])
|
||||
ok A2 key == "schema"
|
||||
ok A2 schema-is-object
|
||||
ok A3 no self-carried struct: summary
|
||||
ok A3 no self-carried struct: fields
|
||||
ok A3 no self-carried struct: indexes
|
||||
ok A3 no self-carried struct: codes
|
||||
ok A4 reference keys subset whitelist (extra=[])
|
||||
ok A5 table non-empty
|
||||
ok A5 authority_module == pbl_runtime_ext
|
||||
ok A5 authority_path non-empty
|
||||
ok A5 authority file exists: modules/pbl_runtime_ext/models/pbl_entity_state.json
|
||||
== reference: /d/pipeline/workspaces/0/sdlc_general/apps/scense/pkgs/world_sync/models/pbl_entity_state.json
|
||||
ok A2 root-keys == {summary,fields,indexes,codes} (got ['codes', 'fields', 'indexes', 'summary'])
|
||||
ok A2 summary non-empty array (got 1)
|
||||
ok A2 fields is array
|
||||
ok A2 indexes is array
|
||||
ok A2 codes is array
|
||||
ok A3 fields non-empty (inline column set, got 10)
|
||||
info summary[0] table-level meta keys (not judged): ['comment']
|
||||
ok A4[tenant_id] field keys subset whitelist (extra=[])
|
||||
ok A4[tenant_id] field.name present
|
||||
ok A4[id] field keys subset whitelist (extra=[])
|
||||
ok A4[id] field.name present
|
||||
ok A4[session_id] field keys subset whitelist (extra=[])
|
||||
ok A4[session_id] field.name present
|
||||
ok A4[entity_id] field keys subset whitelist (extra=[])
|
||||
ok A4[entity_id] field.name present
|
||||
ok A4[state_json] field keys subset whitelist (extra=[])
|
||||
ok A4[state_json] field.name present
|
||||
ok A4[state_version] field keys subset whitelist (extra=[])
|
||||
ok A4[state_version] field.name present
|
||||
ok A4[checksum] field keys subset whitelist (extra=[])
|
||||
ok A4[checksum] field.name present
|
||||
ok A4[updated_by] field keys subset whitelist (extra=[])
|
||||
ok A4[updated_by] field.name present
|
||||
ok A4[created_at] field keys subset whitelist (extra=[])
|
||||
ok A4[created_at] field.name present
|
||||
ok A4[updated_at] field keys subset whitelist (extra=[])
|
||||
ok A4[updated_at] field.name present
|
||||
ok A4[uk_es] index keys subset whitelist (extra=[])
|
||||
ok A4[uk_es] idxfield present in fields: tenant_id
|
||||
ok A4[uk_es] idxfield present in fields: session_id
|
||||
ok A4[uk_es] idxfield present in fields: entity_id
|
||||
ok A4[uk_es] idxtype unique|index ('unique')
|
||||
ok A3[tenant_id] abstract type ('str')
|
||||
ok A3[tenant_id] positive int length
|
||||
ok A3[tenant_id] nullable in yes|no
|
||||
ok A3[id] abstract type ('str')
|
||||
ok A3[id] positive int length
|
||||
ok A3[id] nullable in yes|no
|
||||
ok A3[session_id] abstract type ('long')
|
||||
ok A3[session_id] nullable in yes|no
|
||||
ok A3[entity_id] abstract type ('str')
|
||||
ok A3[entity_id] positive int length
|
||||
ok A3[entity_id] nullable in yes|no
|
||||
ok A3[state_json] abstract type ('text')
|
||||
ok A3[state_json] nullable in yes|no
|
||||
ok A3[state_version] abstract type ('long')
|
||||
ok A3[state_version] nullable in yes|no
|
||||
ok A3[checksum] abstract type ('str')
|
||||
ok A3[checksum] positive int length
|
||||
ok A3[checksum] nullable in yes|no
|
||||
ok A3[updated_by] abstract type ('str')
|
||||
ok A3[updated_by] positive int length
|
||||
ok A3[updated_by] nullable in yes|no
|
||||
ok A3[created_at] abstract type ('datetime')
|
||||
ok A3[created_at] nullable in yes|no
|
||||
ok A3[updated_at] abstract type ('datetime')
|
||||
ok A3[updated_at] nullable in yes|no
|
||||
ok A5 summary[0] is object
|
||||
ok A5 summary[0].name non-empty
|
||||
ok A5 summary[0].name == filename stem ('pbl_entity_state' vs 'pbl_entity_state')
|
||||
== spec(inline four-section): /d/pipeline/workspaces/0/sdlc_general/apps/scense/pkgs/world_sync/models/pbl_entity_state.json
|
||||
ok A1 json-is-object
|
||||
ok A2 root-keys == {key,schema} (got ['key', 'schema'])
|
||||
ok A2 key == "schema"
|
||||
ok A2 schema-is-object
|
||||
ok A3 no self-carried struct: summary
|
||||
ok A3 no self-carried struct: fields
|
||||
ok A3 no self-carried struct: indexes
|
||||
ok A3 no self-carried struct: codes
|
||||
ok A4 reference keys subset whitelist (extra=[])
|
||||
ok A5 table non-empty
|
||||
ok A5 authority_module == pbl_runtime_ext
|
||||
ok A5 authority_path non-empty
|
||||
ok A5 authority file exists: modules/pbl_runtime_ext/models/pbl_entity_state.json
|
||||
ok A2 root-keys == {summary,fields,indexes,codes} (got ['codes', 'fields', 'indexes', 'summary'])
|
||||
ok A2 summary non-empty array (got 1)
|
||||
ok A2 fields is array
|
||||
ok A2 indexes is array
|
||||
ok A2 codes is array
|
||||
ok A3 fields non-empty (inline column set, got 10)
|
||||
info summary[0] table-level meta keys (not judged): ['comment']
|
||||
ok A4[tenant_id] field keys subset whitelist (extra=[])
|
||||
ok A4[tenant_id] field.name present
|
||||
ok A4[id] field keys subset whitelist (extra=[])
|
||||
ok A4[id] field.name present
|
||||
ok A4[session_id] field keys subset whitelist (extra=[])
|
||||
ok A4[session_id] field.name present
|
||||
ok A4[entity_id] field keys subset whitelist (extra=[])
|
||||
ok A4[entity_id] field.name present
|
||||
ok A4[state_json] field keys subset whitelist (extra=[])
|
||||
ok A4[state_json] field.name present
|
||||
ok A4[state_version] field keys subset whitelist (extra=[])
|
||||
ok A4[state_version] field.name present
|
||||
ok A4[checksum] field keys subset whitelist (extra=[])
|
||||
ok A4[checksum] field.name present
|
||||
ok A4[updated_by] field keys subset whitelist (extra=[])
|
||||
ok A4[updated_by] field.name present
|
||||
ok A4[created_at] field keys subset whitelist (extra=[])
|
||||
ok A4[created_at] field.name present
|
||||
ok A4[updated_at] field keys subset whitelist (extra=[])
|
||||
ok A4[updated_at] field.name present
|
||||
ok A4[uk_es] index keys subset whitelist (extra=[])
|
||||
ok A4[uk_es] idxfield present in fields: tenant_id
|
||||
ok A4[uk_es] idxfield present in fields: session_id
|
||||
ok A4[uk_es] idxfield present in fields: entity_id
|
||||
ok A4[uk_es] idxtype unique|index ('unique')
|
||||
ok A3[tenant_id] abstract type ('str')
|
||||
ok A3[tenant_id] positive int length
|
||||
ok A3[tenant_id] nullable in yes|no
|
||||
ok A3[id] abstract type ('str')
|
||||
ok A3[id] positive int length
|
||||
ok A3[id] nullable in yes|no
|
||||
ok A3[session_id] abstract type ('long')
|
||||
ok A3[session_id] nullable in yes|no
|
||||
ok A3[entity_id] abstract type ('str')
|
||||
ok A3[entity_id] positive int length
|
||||
ok A3[entity_id] nullable in yes|no
|
||||
ok A3[state_json] abstract type ('text')
|
||||
ok A3[state_json] nullable in yes|no
|
||||
ok A3[state_version] abstract type ('long')
|
||||
ok A3[state_version] nullable in yes|no
|
||||
ok A3[checksum] abstract type ('str')
|
||||
ok A3[checksum] positive int length
|
||||
ok A3[checksum] nullable in yes|no
|
||||
ok A3[updated_by] abstract type ('str')
|
||||
ok A3[updated_by] positive int length
|
||||
ok A3[updated_by] nullable in yes|no
|
||||
ok A3[created_at] abstract type ('datetime')
|
||||
ok A3[created_at] nullable in yes|no
|
||||
ok A3[updated_at] abstract type ('datetime')
|
||||
ok A3[updated_at] nullable in yes|no
|
||||
ok A5 summary[0] is object
|
||||
ok A5 summary[0].name non-empty
|
||||
ok A5 summary[0].name == filename stem ('pbl_entity_state' vs 'pbl_entity_state')
|
||||
|
||||
--- A7: 真源与镜像 sha256 两两相同 ---
|
||||
pbl_runtime_event.json
|
||||
src=58b09abf9982d154554522ce41f9c1f964170b02e3266819714f69f2c1ac9c0e
|
||||
dst=58b09abf9982d154554522ce41f9c1f964170b02e3266819714f69f2c1ac9c0e
|
||||
src=910d80e1077f0297fbf2d4eb4f1c6ab879e1656567a686f9d67c648945e4d373
|
||||
dst=910d80e1077f0297fbf2d4eb4f1c6ab879e1656567a686f9d67c648945e4d373
|
||||
ok A7 sha256 equal: pbl_runtime_event.json
|
||||
pbl_entity_state.json
|
||||
src=e8561399150b818918a55f41c6fefd0a7a0b01048768a2a4ab7567004a947014
|
||||
dst=e8561399150b818918a55f41c6fefd0a7a0b01048768a2a4ab7567004a947014
|
||||
src=3c83d007e5a0f88799f8ece35cc5a57063a88cb903110c2d913febdcddedda38
|
||||
dst=3c83d007e5a0f88799f8ece35cc5a57063a88cb903110c2d913febdcddedda38
|
||||
ok A7 sha256 equal: pbl_entity_state.json
|
||||
|
||||
--- A6: 权威真源(pbl_runtime_ext)上游体检,只登记不判定 ---
|
||||
@ -258,7 +646,7 @@ workspace root: /d/pipeline/workspaces/0/sdlc_general
|
||||
DEFECT A6[PRIMARY] idxfields non-empty array
|
||||
DEFECT A6[PRIMARY] idxtype unique|index
|
||||
|
||||
judged assertions executed: 54
|
||||
judged assertions executed: 438
|
||||
upstream audit checks executed: 186
|
||||
UPSTREAM DEFECTS (56) -> 已登记,需冒泡 agent.pm / pbl_runtime_ext owner:
|
||||
* A6 summary keys subset whitelist (got ['append_only', 'charset', 'collate', 'comment', 'ddl_source', 'engine', 'guards', 'module', 'name', 'partition', 'primary', 'tenant_scoped', 'title', 'write_paths'])
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user