Files
skill-template/references/SCHEMA.md
chendelian 766d162245
All checks were successful
技能自动化发布 / release (push) Successful in 4s
fix: harden skill template contracts and scaffolding
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>
2026-07-11 11:55:46 +08:00

15 KiB
Raw Blame History

数据存储与数据管理展示规范

本文是 skill 本地 SQLite 与匠厂宿主「数据管理」页面展示契约的权威说明。 实现上的 DDL 与默认中文元数据种子见 scripts/db/display_metadata.pyscripts/db/connection.pyinit_db() 会一并初始化。

职责边界

技能负责

  • 定义真实 SQLite 表结构
  • 使用英文 snake_case 表名和字段名
  • 使用 _jiangchang_tables 提供表级展示元数据
  • 使用 _jiangchang_columns 提供字段级展示元数据
  • 定义哪些字段可见、可搜索、可创建、可编辑
  • 定义字段展示类型和枚举选项
  • 保证元数据与真实数据库结构一致
  • 保证元数据初始化幂等

宿主负责

  • 读取 _jiangchang_* 元数据
  • 读取 SQLite PRAGMA table_info
  • 根据 schema 渲染数据管理页面
  • 根据 editable 推导字段是否可创建或编辑
  • 执行通用查询、新增、编辑、删除、导入、导出
  • 对字段名、表名、类型和权限进行最终校验
  • 不猜测技能业务含义
  • 不根据具体技能名称写特殊逻辑

SQLite 没有可靠的 COMMENT ON TABLE/COLUMNSQL 文件里的 -- 注释 也不会成为可查询结构。 因此中文展示名称必须写入元数据表;字段顺序必须写在 CREATE TABLE 的列定义顺序中(PRAGMA table_infocid)。

禁止新技能使用宿主的旧版 _schema_meta 表。仅 _jiangchang_tables / _jiangchang_columns 为正式契约。

技能名称SKILL.md

---
name: 中文业务名称
description: 中文技能说明
metadata:
  openclaw:
    slug: english-kebab-slug
---
  • name:用户可见的中文业务名称。
  • description:能力说明,不是技能名称。
  • metadata.openclaw.slug机器标识kebab-case 英文;目录名与默认数据库文件名使用 slug。slug 语义规范见 ../development/NAMING.md
  • 复制模板后不得将 your-skill-slugMy Skill 等占位内容作为正式技能发布名称。

数据库路径与命名

{JIANGCHANG_DATA_ROOT}/{JIANGCHANG_USER_ID}/{skill-slug}/{skill-slug}.db
  • 数据库文件:英文 slug例如 your-skill-slug.db(默认 {skill-slug}.db)。
  • 业务表、字段:英文 snake_case 物理名,例如 demo_itemstask_logscreated_at
  • 禁止用中文作为 SQLite 真实表名或字段名。

外部来源只读与本地数据可写

某些技能会从外部系统读取数据并写入技能自己的本地数据库。需要区分:

外部来源数据库:
  可能只读,不应被修改

技能本地数据库:
  可以根据业务语义允许新增、编辑或删除

不要把「外部来源只读」错误理解成「技能本地数据库一定只读」。技能通过 _jiangchang_tables.readonly_jiangchang_columns.editable 声明本地表的实际写权限。

元数据表(宿主兼容)

宿主读取字段(与匠厂 metadata-resolver 一致):

_jiangchang_tables 表级标准

字段 必填 说明
table_name 真实英文表名
display_name 用户可见的中文业务名称
description 表说明
sort_order 兼容保留字段,不用于替代物理字段顺序
visible 1 在宿主数据管理中展示 / 0 隐藏
readonly 1 整张表只读;0 允许宿主根据字段权限执行写操作

禁止:

  • 使用中文真实表名
  • 使用 _schema_meta 作为新的正式契约
  • 只写英文物理名而不写中文 display_name
  • 让元数据引用不存在的表

_jiangchang_columns 字段级标准

字段 必填 说明
table_name 所属物理表
column_name 真实英文字段名
display_name 用户可见的中文字段名
description 字段说明
display_order 兼容字段,默认保持 NULL
visible 是否在数据管理表格中展示
searchable 是否参与搜索
editable 1 业务字段允许宿主用于新增和编辑;0 系统/只读字段
display_type 字段渲染类型(如 texttextareadatetime_unix_seconds
options_json 枚举选项 JSON

这两张表是数据字典,不是业务数据;宿主不会把它们当作普通业务表展示。

editable 与 createable 的关系

技能模板只维护 editable 这一份字段权限声明:

skill metadata:
  editable

host runtime:
  createable / editable

宿主根据以下因素计算字段是否能够出现在新增或编辑表单中:

  • 表级 readonly
  • 字段 editable
  • 主键
  • generated 字段
  • sensitive 字段
  • BLOB 类型
  • not-null 约束
  • default value

通用规则:

新增表单:
  editable = 1
  且不是主键
  且不是 generated
  且不是敏感字段
  且不是 BLOB

编辑表单:
  editable = 1
  且不是主键
  且不是 generated
  且不是敏感字段
  且不是 BLOB

若一张表:

readonly = 0
但所有字段 editable = 0

则:

  • 这张表技术上可写,但不适合手工新增或编辑
  • 宿主不应打开空白新增表单
  • 技能应通过自身业务流程产生数据
  • 这不是宿主猜测业务,而是技能通过元数据声明的结果

系统字段标准

通常保持 editable = 0 的字段类型包括:

  • 自增主键
  • generated 字段
  • created_at / updated_at
  • 同步时间戳
  • 内容哈希
  • 计算结果
  • 内部状态
  • 删除标记
  • 审计字段
  • 敏感凭据或密钥字段

不要把具体业务字段名写死;按字段类型和职责设置 editable

表结构标准

  • 业务表和字段使用英文 snake_case
  • 可写表优先使用单字段主键
  • 自增主键优先放在 CREATE TABLE 第一列
  • created_atupdated_at 放在业务字段末尾
  • 字段展示顺序以 CREATE TABLEPRAGMA table_info(cid) 为准
  • 不依赖 display_order 重新排列字段
  • 不要让宿主通过英文字段名推断展示顺序
  • 复杂字段需要通过 display_typevisible 明确处理

推荐顺序(可按业务裁剪,但已有字段应遵守相对位置):

1. 主键 id
2. 关联标识与核心业务字段
3. 普通业务字段
4. 状态、结果、错误等辅助字段
5. created_at如有
6. updated_at如有

标准时间字段(新技能统一规范)

新技能业务表若需要时间审计字段,统一采用 Unix 秒级时间戳不要使用毫秒时间戳、ISO 字符串或 SQLite 文本 datetime。

项目 规范
物理字段 created_at INTEGERupdated_at INTEGER
存储格式 Unix 秒级整数
展示元数据 display_type datetime_unix_seconds
中文名 created_at创建时间updated_at更新时间
用户编辑 editable = 0(系统字段,宿主只展示格式化结果)
字段顺序 位于业务字段末尾:… → created_at → updated_at
排序 不使用 display_order;顺序仅来自 PRAGMA table_info

自动生成与维护

  • created_atINTEGER NOT NULL DEFAULT (unixepoch());插入时可省略,由数据库生成。
  • updated_atINTEGER NOT NULL DEFAULT (unixepoch());插入时同样可省略。
  • updated_at 更新:模板默认表 task_logs 通过 SQLite trigger {table}_set_updated_at 在业务字段更新后自动刷新。

实现参考:

  • DDL 与 triggerscripts/db/connection.pyscripts/db/timestamp_columns.py
  • 中文元数据种子:scripts/db/display_metadata.py
  • 自检:scripts/db/display_metadata_validator.py

宿主只负责把 display_type = datetime_unix_seconds 格式化为 YYYY-MM-DD HH:mm:ss 展示,不修改数据库原始值。

模板默认:task_logs

task_logs 是技能内部系统自动写入的任务审计日志(由 task_logs_repository INSERT/UPDATE不是供用户在宿主数据管理中手工维护的业务表。

权限设计:

层级 说明
表级 readonly 1 宿主数据管理只读展示,不提供新增/编辑/删除表单
所有业务字段 editable 0 task_typestatuserror_msgresult_summary
技能内部 repository 正常 INSERT/UPDATE 元数据权限只约束宿主 UI不限制技能代码写库

物理定义(权威顺序):

CREATE TABLE IF NOT EXISTS task_logs (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    task_type TEXT NOT NULL,
    target_id TEXT,
    input_id TEXT,
    input_title TEXT,
    status TEXT NOT NULL,
    error_msg TEXT,
    result_summary TEXT,
    created_at INTEGER NOT NULL DEFAULT (unixepoch()),
    updated_at INTEGER NOT NULL DEFAULT (unixepoch())
);

对应中文展示(由 init_db() 写入元数据表):

物理字段 中文名 editable
id 编号 0
task_type 任务类型 0
target_id 目标编号 0
input_id 输入编号 0
input_title 输入标题 0
status 状态 0
error_msg 错误信息 0
result_summary 结果摘要 0
created_at 创建时间 0
updated_at 更新时间 0

表中文名:任务日志;表级 readonly = 1(宿主只读;技能 repository 仍可写入)。

通用示例:demo_items(测试 fixture 参考)

以下表结构用于模板测试演示数据管理契约,不是模板默认业务表:

CREATE TABLE demo_items (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    title TEXT NOT NULL,
    description TEXT,
    status TEXT NOT NULL DEFAULT 'draft',
    generated_value TEXT,
    created_at INTEGER NOT NULL DEFAULT (unixepoch()),
    updated_at INTEGER NOT NULL DEFAULT (unixepoch())
);

对应元数据要点:

物理字段 editable 说明
id 0 自增主键
title 1 业务字段
description 1 业务字段
status 1 枚举,options_json 演示
generated_value 0 系统/计算字段
created_at 0 系统时间
updated_at 0 系统时间

枚举字段 options_json

options_json 为 JSON 数组,每项包含 valuelabel

[
  {"value": "draft", "label": "草稿"},
  {"value": "published", "label": "已发布"},
  {"value": "archived", "label": "已归档"}
]

宿主据此渲染选择框;技能侧负责保证 JSON 合法且与物理字段取值一致。

幂等初始化标准

  • _jiangchang_tables_jiangchang_columns 必须通过 upsert 初始化
  • 多次执行初始化不能重复记录
  • 重新初始化可以更新展示名、描述、visiblesearchableeditabledisplay_typeoptions_json
  • 元数据不能残留旧字段(删除或重命名字段时必须同步清理)
  • 元数据不能引用不存在的表或字段
  • 新增字段或表时必须同步增加元数据
  • 不要直接修改用户真实数据库作为迁移方式

实现 helper

  • upsert_table_metadata / upsert_column_metadata — 幂等写入
  • seed_column_metadata_batch — 批量 upsert可选传入 valid_column_names 时在 upsert 后自动 prune
  • prune_stale_column_metadata — 清理已删除物理字段的元数据

新增业务表时如何初始化

  1. connection.py(或迁移脚本)中 CREATE TABLE 真实物理表。
  2. PRAGMA table_infolist_physical_column_names 取得真实物理字段列表(顺序以 cid 为准)。
  3. 调用 upsert_table_metadata 写入表级元数据。
  4. 调用 seed_column_metadata_batch,并传入 valid_column_names=physical_column_names
from db.display_metadata import list_physical_column_names, seed_column_metadata_batch, upsert_table_metadata

physical = list_physical_column_names(cursor, "demo_items")
upsert_table_metadata(cursor, table_name="demo_items", display_name="示例记录", readonly=0)
seed_column_metadata_batch(
    cursor,
    table_name="demo_items",
    columns=DEMO_ITEMS_COLUMN_METADATA,
    valid_column_names=physical,
)

传入 valid_column_names 时:先 upsert再删除不在物理表中的陈旧 _jiangchang_columns 行。

字段删除或重命名时如何清理

  1. 在 DDL / 迁移中完成物理字段删除或重命名。
  2. 重新执行 seed_column_metadata_batch(..., valid_column_names=当前物理字段列表),自动 prune 旧字段元数据。
  3. 或单独调用 prune_stale_column_metadata(cursor, table_name, valid_column_names)

不传入 valid_column_names 时不会自动 prune——模板不会猜测真实物理字段,避免误删;文档与实现均以此为准。

不要直接修改用户真实数据库作为迁移方式;元数据初始化必须使用真实物理字段列表。

中文命名质量

  • 使用普通用户能理解的业务用语:task_logs任务日志error_msg错误信息
  • 禁止:Task Logsinput编号errormsg、直接把物理名当 display_name
  • 不宜向用户暴露的实现细节(如 payload_jsonselector)应设 visible = 0 或给出可理解中文名。

元数据同步规则

  1. 新增用户可见业务表 → 同步 _jiangchang_tables
  2. 新增用户可见字段 → 同步 _jiangchang_columns
  3. 删除/重命名表或字段 → 同步清理或迁移元数据。
  4. 初始化脚本必须幂等。
  5. 元数据中的表、字段必须在库中真实存在;可见项不得缺少中文 display_name

不同业务的 task_logs 映射

业务场景 task_type target_id input_id
发布类 publish 账号 ID 内容 ID
工资代发 disburse 付款账户 批次 ID
对账 reconcile 平台标识 对账批次 ID
发票验真 verify 税务地区 发票批次 ID

业务特有字段优先放入 result_summaryJSON 字符串),避免随意加列;若确需新列,必须同步元数据并保持合理物理顺序。

开发自检

  • init_db() 后运行 tests/test_display_metadata.pytests/test_data_management_contract.py
  • 若提供宿主 Action 入口,运行 tests/test_actions_manifest.py
  • 复制为真实技能后,使用 scripts/db/display_metadata_validator.py 中的校验函数检查 SKILL 名称、slug 与元数据完整性。

模板原则

  • 模板不做复杂历史迁移框架;新 skill 从当前 schema 起步。
  • 元数据 DDL 与 task_logs 中文种子以 scripts/db/display_metadata.py 为单一权威来源。
  • 模板定义「如何声明能力」;具体技能定义「声明什么业务能力」;宿主负责「如何通用渲染和执行」。