fix: harden skill template contracts and scaffolding
All checks were successful
技能自动化发布 / release (push) Successful in 4s
All checks were successful
技能自动化发布 / release (push) Successful in 4s
Align scaffold slug replacement, Action manifest strict boundaries, metadata prune API, field permission validation, and task_logs readonly semantics with documented template contracts. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -161,6 +161,33 @@ def _table_column_info(cursor, table_name: str) -> dict[str, dict[str, object]]:
|
||||
return info
|
||||
|
||||
|
||||
def _table_column_xinfo(cursor, table_name: str) -> dict[str, dict[str, object]]:
|
||||
"""PRAGMA table_xinfo 可识别 generated/hidden 字段(SQLite 3.31+)。"""
|
||||
cursor.execute(f"PRAGMA table_xinfo({_quote_identifier(table_name)})")
|
||||
info: dict[str, dict[str, object]] = {}
|
||||
for row in cursor.fetchall():
|
||||
hidden = int(row[6] or 0) if len(row) > 6 else 0
|
||||
info[str(row[1])] = {
|
||||
"cid": int(row[0]),
|
||||
"name": str(row[1]),
|
||||
"type": str(row[2] or ""),
|
||||
"notnull": int(row[3] or 0),
|
||||
"dflt_value": row[4],
|
||||
"pk": int(row[5] or 0),
|
||||
"hidden": hidden,
|
||||
}
|
||||
return info
|
||||
|
||||
|
||||
def _is_generated_column(xinfo: dict[str, object]) -> bool:
|
||||
hidden = int(xinfo.get("hidden") or 0)
|
||||
return hidden in (2, 3)
|
||||
|
||||
|
||||
def _is_blob_column(column_info: dict[str, object]) -> bool:
|
||||
return "BLOB" in str(column_info.get("type") or "").upper()
|
||||
|
||||
|
||||
def _default_uses_unixepoch(default_value: object) -> bool:
|
||||
if default_value is None:
|
||||
return False
|
||||
@@ -202,6 +229,7 @@ def validate_column_permissions(
|
||||
warnings: list[str] = []
|
||||
ordered = physical_columns or list_physical_column_names(cursor, table_name)
|
||||
column_info = _table_column_info(cursor, table_name)
|
||||
column_xinfo = _table_column_xinfo(cursor, table_name)
|
||||
|
||||
cursor.execute(
|
||||
"""
|
||||
@@ -216,13 +244,13 @@ def validate_column_permissions(
|
||||
for row in cursor.fetchall()
|
||||
}
|
||||
|
||||
for column_name in ordered:
|
||||
meta = meta_by_column.get(column_name)
|
||||
if not meta:
|
||||
for column_name, meta in meta_by_column.items():
|
||||
physical = column_info.get(column_name) or {}
|
||||
xinfo = column_xinfo.get(column_name) or physical
|
||||
if not physical and column_name not in column_xinfo:
|
||||
continue
|
||||
|
||||
physical = column_info.get(column_name) or {}
|
||||
pk = int(physical.get("pk") or 0)
|
||||
pk = int(physical.get("pk") or xinfo.get("pk") or 0)
|
||||
editable = meta["editable"]
|
||||
if pk and editable is not None and int(editable) != 0:
|
||||
errors.append(f"{table_name}.{column_name} 是主键,editable 应为 0")
|
||||
@@ -230,6 +258,12 @@ def validate_column_permissions(
|
||||
if column_name == "id" and editable is not None and int(editable) != 0:
|
||||
errors.append(f"{table_name}.id 的 editable 应为 0(自增主键)")
|
||||
|
||||
if _is_generated_column(xinfo) and editable is not None and int(editable) != 0:
|
||||
errors.append(f"{table_name}.{column_name} 是 generated 字段,editable 应为 0")
|
||||
|
||||
if _is_blob_column(physical or xinfo) and editable is not None and int(editable) != 0:
|
||||
errors.append(f"{table_name}.{column_name} 是 BLOB 字段,editable 应为 0")
|
||||
|
||||
opt_errors, _ = validate_options_json(meta.get("options_json"))
|
||||
for item in opt_errors:
|
||||
errors.append(f"{table_name}.{column_name}: {item}")
|
||||
|
||||
Reference in New Issue
Block a user