fix: harden skill template contracts and scaffolding
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:
2026-07-11 11:55:46 +08:00
parent 35c20fc0f2
commit 766d162245
17 changed files with 536 additions and 72 deletions

View File

@@ -4,6 +4,17 @@
**边界:** 用户市场说明见根目录 [`README.md`](../README.md)CLI 参数与错误码细节见 [`CLI.md`](CLI.md)。
## 模板严格规范 vs 宿主向后兼容
| 角色 | 定位 |
|------|------|
| **skill-template** | 新技能推荐的**严格规范**:必填字段、明确类型、`additionalProperties: false` |
| **宿主运行时** | **向后兼容**的历史 manifest 解析器:可能接受部分缺省字段(如 `description``placements``entrypoint.args`、input property `type` |
**新技能不应依赖宿主缺省行为。** 复制模板后按 [`assets/schemas/skill-actions.schema.json`](../assets/schemas/skill-actions.schema.json) 与本文完整填写 manifest。
JSON Schema 使用 `additionalProperties: false` 表达模板严格边界;这与宿主共享类型中的可选字段并不矛盾——宿主为兼容旧技能可能更宽松,模板为防漂移要求更严。
## Action 是否必需
Action 是**可选能力**
@@ -33,6 +44,29 @@ Action 是**可选能力**
模板最小示例见 [`assets/actions.json`](../assets/actions.json)JSON Schema 见 [`assets/schemas/skill-actions.schema.json`](../assets/schemas/skill-actions.schema.json)。
## Phase 1 当前稳定支持(新技能应只使用这些)
| 层级 | 稳定字段 / 能力 |
|------|----------------|
| 顶层 | `schemaVersion``skill``actions` |
| action | `id``label``description``placements``entrypoint`;可选 `inputSchema``confirmation` |
| entrypoint | `type = cli``command``args`**必填** JSON 数组,项为 `string` / `number` / `boolean` |
| placements | `toolbar``skill-detail``agent``cron` |
| inputSchema | 根 `type: object`properties 使用 `string` / `number` / `boolean`;支持 `enum``default``required``sensitive` |
| confirmation | `{ "message": "..." }` |
## 预留能力(当前不可当作已实现能力)
以下在宿主共享类型或 Schema 枚举中可能出现,但 **Phase 1 Runtime 尚未完整支持****新模板示例不得使用**
| 类别 | 预留项 | 说明 |
|------|--------|------|
| placements | `row``batch` | 行内 / 批量入口,未来扩展 |
| action 字段 | `bind``concurrency``locks` | 绑定、并发与锁控制,宿主后续实现 |
| inputSchema | `array`、nested `object``oneOf` 等 | 复杂表单与联合类型,不属于当前推荐模板范围 |
可以在文档或 Schema 枚举中保留这些值供未来对齐,但不要让技能作者误以为它们已经可执行。
## 执行模型
宿主将 action 转为 argv再执行

View File

@@ -14,7 +14,7 @@
| [`SKILL.md`](../SKILL.md) | LLM / OpenClaw 平台技能入口 |
| [`CLI.md`](CLI.md) | 命令行入口、参数与调用契约 |
| [`SCHEMA.md`](SCHEMA.md) | 本地 SQLite 与数据管理展示契约 |
| [`ACTIONS.md`](ACTIONS.md) | Skill Action manifest 与宿主入口契约 |
| [`ACTIONS.md`](ACTIONS.md) | Skill Action manifest**可选**能力)与宿主入口契约 |
| [`../development/`](../development/) | 开发、测试、技术规范(给开发者 / AI 编程代理) |
按需阅读:编排任务时优先 `CLI.md`;解析结构化字段时读 `SCHEMA.md`;配置宿主按钮/定时任务/Agent 入口时读 `ACTIONS.md`;运行时环境细节见 `../development/RUNTIME.md`

View File

@@ -236,6 +236,16 @@ readonly = 0
### 模板默认:`task_logs`
`task_logs` 是技能内部**系统自动写入**的任务审计日志(由 `task_logs_repository` INSERT/UPDATE不是供用户在宿主数据管理中手工维护的业务表。
权限设计:
| 层级 | 值 | 说明 |
|------|-----|------|
| 表级 `readonly` | `1` | 宿主数据管理只读展示,不提供新增/编辑/删除表单 |
| 所有业务字段 `editable` | `0` | 含 `task_type``status``error_msg``result_summary` 等 |
| 技能内部 repository | 正常 INSERT/UPDATE | 元数据权限只约束宿主 UI不限制技能代码写库 |
物理定义(权威顺序):
```sql
@@ -258,17 +268,17 @@ CREATE TABLE IF NOT EXISTS task_logs (
| 物理字段 | 中文名 | editable |
|----------|--------|----------|
| id | 编号 | 0 |
| task_type | 任务类型 | 1 |
| target_id | 目标编号 | 1 |
| input_id | 输入编号 | 1 |
| input_title | 输入标题 | 1 |
| status | 状态 | 1 |
| error_msg | 错误信息 | 1 |
| result_summary | 结果摘要 | 1 |
| 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 = 0`
表中文名:**任务日志**;表级 `readonly = 1`(宿主只读;技能 repository 仍可写入)
### 通用示例:`demo_items`(测试 fixture 参考)
@@ -325,8 +335,41 @@ CREATE TABLE demo_items (
实现 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_info``list_physical_column_names` 取得**真实物理字段列表**(顺序以 `cid` 为准)。
3. 调用 `upsert_table_metadata` 写入表级元数据。
4. 调用 `seed_column_metadata_batch`,并传入 `valid_column_names=physical_column_names`
```python
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`**错误信息**