dsh-hams 0.1.3 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +3 -3
- package/README.md +4 -0
- package/dist/index.js +11335 -0
- package/package.json +9 -5
- package/src/domain/check.ts +0 -193
- package/src/domain/errors.ts +0 -59
- package/src/domain/evidence.ts +0 -134
- package/src/domain/ids.ts +0 -31
- package/src/host/chat-runtime.ts +0 -489
- package/src/host/dsh-adapter.ts +0 -867
- package/src/host/session-adapter.ts +0 -276
- package/src/http/handler.ts +0 -1132
- package/src/index.js +0 -53
- package/src/service/audit.ts +0 -92
- package/src/service/chat.ts +0 -1100
- package/src/service/contracts.ts +0 -193
- package/src/service/defaults.ts +0 -182
- package/src/service/deliverables.ts +0 -240
- package/src/service/hams.ts +0 -3103
- package/src/service/knowledge.ts +0 -940
- package/src/service/member-config.ts +0 -39
- package/src/service/members.ts +0 -506
- package/src/service/memory.ts +0 -375
- package/src/service/permissions.ts +0 -118
- package/src/service/project-lifecycle.ts +0 -233
- package/src/service/projects.ts +0 -840
- package/src/service/receipts.ts +0 -63
- package/src/service/templates.ts +0 -140
- package/src/service/tick.ts +0 -751
- package/src/service/tools.ts +0 -711
- package/src/service/types.ts +0 -219
- package/src/service/work-item-create.ts +0 -159
- package/src/service/workspace-profile.ts +0 -443
- package/src/store/backup.ts +0 -239
- package/src/store/bootstrap.ts +0 -225
- package/src/store/db.ts +0 -179
- package/src/store/lock.ts +0 -194
- package/src/store/migrations.ts +0 -211
- package/src/store/schema/001_p1.sql +0 -846
- package/src/store/schema/001_p1.sql.ts +0 -865
- package/src/store/schema/002_knowledge.sql +0 -54
- package/src/store/schema/002_knowledge.sql.ts +0 -68
- package/src/store/schema/003_projects.sql.ts +0 -3
- package/src/store/schema/004_team.sql.ts +0 -50
- package/src/store/schema/005_target_check.sql.ts +0 -22
- package/src/store/schema/006_target_flow.sql.ts +0 -19
- package/src/store/schema/007_project_delete.sql.ts +0 -17
- package/src/store/schema/008_memory.sql.ts +0 -47
- package/src/store/schema/generate-projects.mjs +0 -16
- package/src/store/seed.ts +0 -114
|
@@ -1,865 +0,0 @@
|
|
|
1
|
-
// ⚠️ 自动生成,请勿手改。生成命令:node scripts/split-schema.mjs
|
|
2
|
-
// 来源:docs/schema-2026-11-14.sql(sha256 91590bcaaeb9612f800a39297d4effe9ddcb419dbb822de1a3eb268be6969ec7)
|
|
3
|
-
// 迁移代码从这里读 DDL,避免运行时依赖 docs/ 目录(打包后 docs 不进产物)。
|
|
4
|
-
|
|
5
|
-
export const P1_SCHEMA_SQL = `-- ⚠️ 自动生成,请勿手改。
|
|
6
|
-
-- 来源(权威):docs/schema-2026-11-14.sql
|
|
7
|
-
-- 权威文件 sha256:91590bcaaeb9612f800a39297d4effe9ddcb419dbb822de1a3eb268be6969ec7
|
|
8
|
-
-- 生成命令:node scripts/split-schema.mjs
|
|
9
|
-
-- 内容:P1 41 表 / 30 索引 / 31 触发器 / 2 种子语句
|
|
10
|
-
-- 分期依据:表注释里的 [P1] / [P2] / [壳] 标记;触发器按它引用的表取最晚一档。
|
|
11
|
-
CREATE TABLE hams_meta ( -- [P1] schema_version / runtime_epoch / primary_workspace_id / last_tick_at
|
|
12
|
-
key TEXT PRIMARY KEY,
|
|
13
|
-
value TEXT NOT NULL
|
|
14
|
-
);
|
|
15
|
-
|
|
16
|
-
-- ───────────────────────── 1. 成员、岗位与授权 ─────────────────────────
|
|
17
|
-
CREATE TABLE member ( -- [P1] 人、bot 与唯一的系统主体共用主键空间
|
|
18
|
-
id TEXT PRIMARY KEY,
|
|
19
|
-
kind TEXT NOT NULL CHECK (kind IN ('human','bot','system')),
|
|
20
|
-
name TEXT NOT NULL,
|
|
21
|
-
domain TEXT,
|
|
22
|
-
responsibility TEXT NOT NULL DEFAULT '',
|
|
23
|
-
role TEXT NOT NULL DEFAULT 'executor'
|
|
24
|
-
CHECK (role IN ('coordinator','executor','observer')),
|
|
25
|
-
status TEXT NOT NULL DEFAULT 'active'
|
|
26
|
-
CHECK (status IN ('active','paused','retired')),
|
|
27
|
-
current_version INTEGER NOT NULL DEFAULT 0,
|
|
28
|
-
owner_human_id TEXT REFERENCES member(id), -- bot 的责任人
|
|
29
|
-
created_at INTEGER NOT NULL,
|
|
30
|
-
updated_at INTEGER NOT NULL,
|
|
31
|
-
CHECK (kind <> 'bot' OR owner_human_id IS NOT NULL)
|
|
32
|
-
);
|
|
33
|
-
|
|
34
|
-
CREATE INDEX idx_member_kind ON member(kind, status);
|
|
35
|
-
|
|
36
|
-
CREATE UNIQUE INDEX uq_member_system ON member(kind) WHERE kind = 'system';
|
|
37
|
-
|
|
38
|
-
INSERT INTO member (id, kind, name, created_at, updated_at) VALUES ('system', 'system', '系统', 0, 0);
|
|
39
|
-
|
|
40
|
-
CREATE TABLE member_version ( -- [P1] bot 配置快照:发布即不可变
|
|
41
|
-
id TEXT PRIMARY KEY,
|
|
42
|
-
member_id TEXT NOT NULL REFERENCES member(id),
|
|
43
|
-
version INTEGER NOT NULL,
|
|
44
|
-
agent_preset TEXT, -- 宿主 agentPreset(不是 permissionPresets)
|
|
45
|
-
model TEXT,
|
|
46
|
-
memory_scope_json TEXT NOT NULL DEFAULT '{}',
|
|
47
|
-
prompt_digest TEXT NOT NULL,
|
|
48
|
-
change_class TEXT NOT NULL DEFAULT 'minor' CHECK (change_class IN ('minor','major')),
|
|
49
|
-
published_by TEXT NOT NULL REFERENCES member(id),
|
|
50
|
-
published_at INTEGER NOT NULL,
|
|
51
|
-
UNIQUE (member_id, version)
|
|
52
|
-
);
|
|
53
|
-
|
|
54
|
-
CREATE TABLE member_permission ( -- [P1] 只用于 bot;人的权限走岗位
|
|
55
|
-
member_id TEXT NOT NULL REFERENCES member(id),
|
|
56
|
-
capability TEXT NOT NULL,
|
|
57
|
-
scope TEXT NOT NULL, -- domain:<域> | project:<id> | global
|
|
58
|
-
granted_by TEXT NOT NULL REFERENCES member(id),
|
|
59
|
-
granted_at INTEGER NOT NULL,
|
|
60
|
-
PRIMARY KEY (member_id, capability, scope)
|
|
61
|
-
);
|
|
62
|
-
|
|
63
|
-
CREATE TABLE member_tool_policy ( -- [P1] 无记录 = 不可用
|
|
64
|
-
member_id TEXT NOT NULL REFERENCES member(id),
|
|
65
|
-
tool_name TEXT NOT NULL,
|
|
66
|
-
enabled INTEGER NOT NULL DEFAULT 0,
|
|
67
|
-
PRIMARY KEY (member_id, tool_name)
|
|
68
|
-
);
|
|
69
|
-
|
|
70
|
-
CREATE TABLE hams_user ( -- [P1] 登录身份 → human 成员;HAMS 不存口令、不发 token
|
|
71
|
-
id TEXT PRIMARY KEY,
|
|
72
|
-
member_id TEXT NOT NULL UNIQUE REFERENCES member(id),
|
|
73
|
-
login TEXT NOT NULL UNIQUE,
|
|
74
|
-
display_name TEXT NOT NULL,
|
|
75
|
-
auth_source TEXT NOT NULL CHECK (auth_source IN ('local_host','sso','gateway_local')),
|
|
76
|
-
external_id TEXT NOT NULL, -- 宿主账号 id / OIDC sub / 网关本地账号名
|
|
77
|
-
expires_at INTEGER, -- gateway_local 兜底账号必须限期
|
|
78
|
-
is_admin INTEGER NOT NULL DEFAULT 0,
|
|
79
|
-
status TEXT NOT NULL DEFAULT 'active' CHECK (status IN ('active','disabled')),
|
|
80
|
-
last_login_at INTEGER,
|
|
81
|
-
UNIQUE (auth_source, external_id),
|
|
82
|
-
CHECK (auth_source <> 'gateway_local' OR expires_at IS NOT NULL)
|
|
83
|
-
);
|
|
84
|
-
|
|
85
|
-
CREATE TABLE position ( -- [P1] 权限挂岗位
|
|
86
|
-
id TEXT PRIMARY KEY,
|
|
87
|
-
name TEXT NOT NULL UNIQUE,
|
|
88
|
-
domain TEXT,
|
|
89
|
-
description TEXT NOT NULL DEFAULT '',
|
|
90
|
-
status TEXT NOT NULL DEFAULT 'active' CHECK (status IN ('active','retired'))
|
|
91
|
-
);
|
|
92
|
-
|
|
93
|
-
CREATE TABLE position_permission ( -- [P1]
|
|
94
|
-
position_id TEXT NOT NULL REFERENCES position(id),
|
|
95
|
-
capability TEXT NOT NULL,
|
|
96
|
-
scope TEXT NOT NULL,
|
|
97
|
-
PRIMARY KEY (position_id, capability, scope)
|
|
98
|
-
);
|
|
99
|
-
|
|
100
|
-
CREATE TABLE member_position ( -- [P1] 在岗区间 [starts_at, ends_at)
|
|
101
|
-
member_id TEXT NOT NULL REFERENCES member(id),
|
|
102
|
-
position_id TEXT NOT NULL REFERENCES position(id),
|
|
103
|
-
starts_at INTEGER NOT NULL,
|
|
104
|
-
ends_at INTEGER,
|
|
105
|
-
assigned_by TEXT NOT NULL REFERENCES member(id),
|
|
106
|
-
PRIMARY KEY (member_id, position_id, starts_at),
|
|
107
|
-
CHECK (ends_at IS NULL OR ends_at > starts_at)
|
|
108
|
-
);
|
|
109
|
-
|
|
110
|
-
CREATE INDEX idx_mp_active ON member_position(member_id, ends_at);
|
|
111
|
-
|
|
112
|
-
-- ───────────────────────── 3. 项目与指标 ─────────────────────────
|
|
113
|
-
CREATE TABLE project ( -- [P1]
|
|
114
|
-
id TEXT PRIMARY KEY,
|
|
115
|
-
name TEXT NOT NULL UNIQUE,
|
|
116
|
-
type TEXT NOT NULL CHECK (type IN ('one_off','continuous')),
|
|
117
|
-
status TEXT NOT NULL DEFAULT 'draft'
|
|
118
|
-
CHECK (status IN ('draft','active','paused','archived')),
|
|
119
|
-
owner_human_id TEXT NOT NULL REFERENCES member(id),
|
|
120
|
-
coordinator_id TEXT REFERENCES member(id),
|
|
121
|
-
goal_text TEXT NOT NULL,
|
|
122
|
-
current_target_version INTEGER NOT NULL DEFAULT 0,
|
|
123
|
-
no_progress_streak INTEGER NOT NULL DEFAULT 0,
|
|
124
|
-
revision INTEGER NOT NULL DEFAULT 1,
|
|
125
|
-
created_at INTEGER NOT NULL,
|
|
126
|
-
updated_at INTEGER NOT NULL
|
|
127
|
-
);
|
|
128
|
-
|
|
129
|
-
CREATE TABLE project_target_version ( -- [P1] 口径版本:发布即冻结
|
|
130
|
-
id TEXT PRIMARY KEY,
|
|
131
|
-
project_id TEXT NOT NULL REFERENCES project(id),
|
|
132
|
-
version INTEGER NOT NULL,
|
|
133
|
-
goal_text TEXT NOT NULL,
|
|
134
|
-
published_by TEXT NOT NULL REFERENCES member(id),
|
|
135
|
-
published_at INTEGER NOT NULL,
|
|
136
|
-
UNIQUE (project_id, version)
|
|
137
|
-
);
|
|
138
|
-
|
|
139
|
-
CREATE TABLE project_metric ( -- [P1] 口径 = 预置计算器 + 参数,不存自由表达式
|
|
140
|
-
id TEXT PRIMARY KEY,
|
|
141
|
-
project_id TEXT NOT NULL REFERENCES project(id),
|
|
142
|
-
version_id TEXT NOT NULL REFERENCES project_target_version(id),
|
|
143
|
-
name TEXT NOT NULL,
|
|
144
|
-
direction TEXT NOT NULL CHECK (direction IN ('higher','lower','range')),
|
|
145
|
-
target_value REAL,
|
|
146
|
-
target_max REAL, -- direction='range' 时的上界
|
|
147
|
-
unit TEXT,
|
|
148
|
-
definition_text TEXT NOT NULL DEFAULT '', -- 给人看的口径说明
|
|
149
|
-
calculator_key TEXT, -- 例:ratio_of_counts;NULL = 未定义口径
|
|
150
|
-
calculator_params_json TEXT NOT NULL DEFAULT '{}',
|
|
151
|
-
window_kind TEXT NOT NULL CHECK (window_kind IN ('day','week','month','snapshot')),
|
|
152
|
-
source_kind TEXT CHECK (source_kind IS NULL OR source_kind IN ('ledger','work_item','webhook','manual')),
|
|
153
|
-
source_ref TEXT,
|
|
154
|
-
trial_value REAL, -- 发布前试算结果
|
|
155
|
-
trial_at INTEGER,
|
|
156
|
-
UNIQUE (version_id, name)
|
|
157
|
-
);
|
|
158
|
-
|
|
159
|
-
CREATE TABLE project_budget ( -- [P1] 额度按「周期类型 + 周期起点 + 计量项」记账
|
|
160
|
-
project_id TEXT NOT NULL REFERENCES project(id),
|
|
161
|
-
period_kind TEXT NOT NULL CHECK (period_kind IN ('day','week')),
|
|
162
|
-
period_start TEXT NOT NULL, -- YYYY-MM-DD(项目时区)
|
|
163
|
-
metric TEXT NOT NULL CHECK (metric IN ('new_tasks','proposals')),
|
|
164
|
-
used INTEGER NOT NULL DEFAULT 0,
|
|
165
|
-
limit_value INTEGER NOT NULL,
|
|
166
|
-
PRIMARY KEY (project_id, period_kind, period_start, metric)
|
|
167
|
-
);
|
|
168
|
-
|
|
169
|
-
CREATE TABLE metric_observation ( -- [P1] value NULL = 缺数
|
|
170
|
-
id TEXT PRIMARY KEY,
|
|
171
|
-
metric_id TEXT NOT NULL REFERENCES project_metric(id),
|
|
172
|
-
observed_at INTEGER NOT NULL,
|
|
173
|
-
window_start INTEGER NOT NULL,
|
|
174
|
-
window_end INTEGER NOT NULL,
|
|
175
|
-
value REAL,
|
|
176
|
-
source_ref TEXT,
|
|
177
|
-
ingested_at INTEGER NOT NULL
|
|
178
|
-
);
|
|
179
|
-
|
|
180
|
-
CREATE INDEX idx_obs_metric ON metric_observation(metric_id, window_end DESC);
|
|
181
|
-
|
|
182
|
-
-- ───────────────────────── 4. 资产、模板与入站事件 ─────────────────────────
|
|
183
|
-
CREATE TABLE asset ( -- [P1] 外部资产的稳定映射
|
|
184
|
-
id TEXT PRIMARY KEY,
|
|
185
|
-
external_system TEXT NOT NULL,
|
|
186
|
-
external_id TEXT NOT NULL,
|
|
187
|
-
kind TEXT NOT NULL DEFAULT 'battery',
|
|
188
|
-
display_name TEXT NOT NULL,
|
|
189
|
-
last_snapshot_date TEXT,
|
|
190
|
-
UNIQUE (external_system, external_id)
|
|
191
|
-
);
|
|
192
|
-
|
|
193
|
-
CREATE TABLE ledger_snapshot ( -- [P1] Hive 台账镜像,页面只查本地
|
|
194
|
-
snapshot_date TEXT NOT NULL,
|
|
195
|
-
asset_id TEXT NOT NULL REFERENCES asset(id),
|
|
196
|
-
status TEXT,
|
|
197
|
-
attrs_json TEXT NOT NULL DEFAULT '{}',
|
|
198
|
-
source_ref TEXT NOT NULL,
|
|
199
|
-
ingested_at INTEGER NOT NULL,
|
|
200
|
-
PRIMARY KEY (snapshot_date, asset_id)
|
|
201
|
-
);
|
|
202
|
-
|
|
203
|
-
CREATE INDEX idx_ledger_asset ON ledger_snapshot(asset_id, snapshot_date DESC);
|
|
204
|
-
|
|
205
|
-
CREATE TABLE work_template ( -- [P1]
|
|
206
|
-
id TEXT PRIMARY KEY,
|
|
207
|
-
key TEXT NOT NULL UNIQUE, -- 例:battery_alert_tracking
|
|
208
|
-
name TEXT NOT NULL,
|
|
209
|
-
domain TEXT NOT NULL,
|
|
210
|
-
category TEXT NOT NULL,
|
|
211
|
-
published_version_id TEXT REFERENCES work_template_version(id)
|
|
212
|
-
);
|
|
213
|
-
|
|
214
|
-
CREATE TABLE work_template_version ( -- [P1] 关闭判据的出处:证据要求、复查策略、验收策略
|
|
215
|
-
id TEXT PRIMARY KEY,
|
|
216
|
-
template_id TEXT NOT NULL REFERENCES work_template(id),
|
|
217
|
-
version INTEGER NOT NULL,
|
|
218
|
-
required_evidence_json TEXT NOT NULL DEFAULT '[]', -- [{kind,min_count,max_age_hours}]
|
|
219
|
-
validator_key TEXT, -- 代码内置的具名验证器;NULL = 只做证据清单核对
|
|
220
|
-
validator_params_json TEXT NOT NULL DEFAULT '{}',
|
|
221
|
-
check_policy_json TEXT NOT NULL DEFAULT '{}', -- {mode:'next_day_at',time:'09:00',tz,max_rechecks}
|
|
222
|
-
review_policy_json TEXT NOT NULL DEFAULT '{}', -- {approver:'owner'|'position:<id>',expires_hours,auto_close}
|
|
223
|
-
misfire_policy TEXT NOT NULL DEFAULT 'each' CHECK (misfire_policy IN ('each','catch_up_latest','skip')),
|
|
224
|
-
status TEXT NOT NULL DEFAULT 'draft' CHECK (status IN ('draft','published','retired')),
|
|
225
|
-
published_by TEXT REFERENCES member(id),
|
|
226
|
-
published_at INTEGER,
|
|
227
|
-
UNIQUE (template_id, version)
|
|
228
|
-
);
|
|
229
|
-
|
|
230
|
-
CREATE TABLE event_inbox ( -- [P1] 入站事件幂等:同一外部事件只产生一次业务效果
|
|
231
|
-
id TEXT PRIMARY KEY,
|
|
232
|
-
connector TEXT NOT NULL,
|
|
233
|
-
external_event_id TEXT NOT NULL,
|
|
234
|
-
asset_id TEXT REFERENCES asset(id),
|
|
235
|
-
event_type TEXT NOT NULL,
|
|
236
|
-
severity INTEGER NOT NULL DEFAULT 0,
|
|
237
|
-
payload_ref TEXT NOT NULL,
|
|
238
|
-
payload_hash TEXT NOT NULL,
|
|
239
|
-
occurred_at INTEGER NOT NULL,
|
|
240
|
-
received_at INTEGER NOT NULL,
|
|
241
|
-
work_item_id TEXT REFERENCES work_item(id), -- 处置后回填
|
|
242
|
-
disposition TEXT NOT NULL DEFAULT 'received'
|
|
243
|
-
CHECK (disposition IN ('received','created','attached','escalated','ignored')),
|
|
244
|
-
UNIQUE (connector, external_event_id)
|
|
245
|
-
);
|
|
246
|
-
|
|
247
|
-
CREATE INDEX idx_inbox_asset ON event_inbox(asset_id, occurred_at DESC);
|
|
248
|
-
|
|
249
|
-
-- ───────────────────────── 5. 工作项、运行与证据 ─────────────────────────
|
|
250
|
-
CREATE TABLE work_item_transition ( -- [P1] 状态白名单:域代码与触发器共用这一份
|
|
251
|
-
from_status TEXT NOT NULL,
|
|
252
|
-
to_status TEXT NOT NULL,
|
|
253
|
-
command TEXT NOT NULL,
|
|
254
|
-
PRIMARY KEY (from_status, to_status)
|
|
255
|
-
);
|
|
256
|
-
|
|
257
|
-
INSERT INTO work_item_transition (from_status, to_status, command) VALUES
|
|
258
|
-
('ready','active','claim|accept'),
|
|
259
|
-
('ready','blocked','reportBlocker'),
|
|
260
|
-
('ready','paused','pause'),
|
|
261
|
-
('ready','cancelled','cancel'),
|
|
262
|
-
('active','ready','release|takeOver|handoff/accept'),
|
|
263
|
-
('active','waiting_check','submitResult'),
|
|
264
|
-
('active','waiting_review','requestClosure'),
|
|
265
|
-
('active','blocked','reportBlocker'),
|
|
266
|
-
('active','paused','pause'),
|
|
267
|
-
('active','failed','fail'),
|
|
268
|
-
('active','cancelled','cancel'),
|
|
269
|
-
('waiting_check','active','check failed'),
|
|
270
|
-
('waiting_check','waiting_review','requestClosure'),
|
|
271
|
-
('waiting_check','blocked','check unknown|retries exhausted|reportBlocker'),
|
|
272
|
-
('waiting_check','paused','pause'),
|
|
273
|
-
('waiting_check','cancelled','cancel'),
|
|
274
|
-
('waiting_review','completed','approval approve|auto close'),
|
|
275
|
-
('waiting_review','active','approval reject|approval authorize'),
|
|
276
|
-
('waiting_review','blocked','approval return'),
|
|
277
|
-
('waiting_review','paused','pause'),
|
|
278
|
-
('waiting_review','cancelled','cancel'),
|
|
279
|
-
('blocked','ready','unblock'),
|
|
280
|
-
('blocked','active','unblock'),
|
|
281
|
-
('blocked','waiting_check','scheduleCheck'),
|
|
282
|
-
('blocked','waiting_review','requestClosure'),
|
|
283
|
-
('blocked','paused','pause'),
|
|
284
|
-
('blocked','cancelled','cancel'),
|
|
285
|
-
('paused','ready','resume'),
|
|
286
|
-
('paused','active','resume'),
|
|
287
|
-
('paused','waiting_check','resume'),
|
|
288
|
-
('paused','waiting_review','resume'),
|
|
289
|
-
('paused','blocked','resume'),
|
|
290
|
-
('paused','cancelled','cancel'),
|
|
291
|
-
('completed','ready','reopen'),
|
|
292
|
-
('failed','ready','retry'),
|
|
293
|
-
('failed','cancelled','cancel');
|
|
294
|
-
|
|
295
|
-
CREATE TABLE work_item ( -- [P1]
|
|
296
|
-
id TEXT PRIMARY KEY,
|
|
297
|
-
project_id TEXT REFERENCES project(id),
|
|
298
|
-
parent_id TEXT REFERENCES work_item(id),
|
|
299
|
-
template_version_id TEXT REFERENCES work_template_version(id),
|
|
300
|
-
asset_id TEXT REFERENCES asset(id),
|
|
301
|
-
domain TEXT NOT NULL,
|
|
302
|
-
category TEXT NOT NULL,
|
|
303
|
-
title TEXT NOT NULL,
|
|
304
|
-
priority INTEGER NOT NULL DEFAULT 0,
|
|
305
|
-
status TEXT NOT NULL DEFAULT 'ready'
|
|
306
|
-
CHECK (status IN ('ready','active','waiting_check','waiting_review','blocked',
|
|
307
|
-
'paused','completed','failed','cancelled')),
|
|
308
|
-
resume_state TEXT CHECK (resume_state IS NULL OR resume_state IN
|
|
309
|
-
('ready','active','waiting_check','waiting_review','blocked')),
|
|
310
|
-
blocked_reason TEXT CHECK (blocked_reason IS NULL OR blocked_reason IN
|
|
311
|
-
('needs_evidence','external','dependency','permission','human_input','data_source')),
|
|
312
|
-
owner_human_id TEXT NOT NULL REFERENCES member(id), -- 对结果负责的人
|
|
313
|
-
assignee_id TEXT REFERENCES member(id), -- 当前执行者:人或 bot
|
|
314
|
-
due_at INTEGER,
|
|
315
|
-
next_action_at INTEGER,
|
|
316
|
-
check_generation INTEGER NOT NULL DEFAULT 0,
|
|
317
|
-
close_requires_approval INTEGER NOT NULL DEFAULT 1,
|
|
318
|
-
dedupe_key TEXT, -- 例:alert:BT-20871:overdischarge
|
|
319
|
-
revision INTEGER NOT NULL DEFAULT 1, -- 对象修订号:行或证据集每变一次 +1
|
|
320
|
-
execution_epoch INTEGER NOT NULL DEFAULT 1, -- 执行代次:暂停/恢复/取消/重开/换执行者 +1
|
|
321
|
-
fence_seq INTEGER NOT NULL DEFAULT 0, -- 租约栅栏号发号器
|
|
322
|
-
evidence_set_hash TEXT NOT NULL DEFAULT '', -- 有效证据集的哈希
|
|
323
|
-
created_by TEXT NOT NULL REFERENCES member(id),
|
|
324
|
-
created_at INTEGER NOT NULL,
|
|
325
|
-
updated_at INTEGER NOT NULL,
|
|
326
|
-
completed_at INTEGER,
|
|
327
|
-
CHECK ((status = 'paused') = (resume_state IS NOT NULL)),
|
|
328
|
-
CHECK (status = 'blocked' OR resume_state IS 'blocked' OR blocked_reason IS NULL)
|
|
329
|
-
);
|
|
330
|
-
|
|
331
|
-
-- 去重只约束「仍在处理」的单:关单后同一电池再次告警可以新建
|
|
332
|
-
CREATE UNIQUE INDEX uq_wi_dedupe ON work_item(dedupe_key)
|
|
333
|
-
WHERE dedupe_key IS NOT NULL AND status NOT IN ('completed','cancelled');
|
|
334
|
-
|
|
335
|
-
CREATE INDEX idx_wi_status_due ON work_item(status, next_action_at);
|
|
336
|
-
|
|
337
|
-
CREATE INDEX idx_wi_assignee ON work_item(assignee_id, status);
|
|
338
|
-
|
|
339
|
-
CREATE INDEX idx_wi_owner ON work_item(owner_human_id, status);
|
|
340
|
-
|
|
341
|
-
CREATE INDEX idx_wi_asset ON work_item(asset_id, created_at DESC);
|
|
342
|
-
|
|
343
|
-
CREATE INDEX idx_wi_project ON work_item(project_id, domain, status);
|
|
344
|
-
|
|
345
|
-
CREATE TABLE work_item_relation ( -- [P1]
|
|
346
|
-
from_id TEXT NOT NULL REFERENCES work_item(id),
|
|
347
|
-
to_id TEXT NOT NULL REFERENCES work_item(id),
|
|
348
|
-
kind TEXT NOT NULL CHECK (kind IN ('blocks','duplicate_of','caused_by')),
|
|
349
|
-
PRIMARY KEY (from_id, to_id, kind),
|
|
350
|
-
CHECK (from_id <> to_id)
|
|
351
|
-
);
|
|
352
|
-
|
|
353
|
-
CREATE TABLE work_item_event ( -- [P1] 追加式时间线
|
|
354
|
-
id TEXT PRIMARY KEY,
|
|
355
|
-
work_item_id TEXT NOT NULL REFERENCES work_item(id),
|
|
356
|
-
kind TEXT NOT NULL,
|
|
357
|
-
actor_id TEXT NOT NULL REFERENCES member(id), -- 系统动作用 'system'
|
|
358
|
-
actor_kind TEXT NOT NULL CHECK (actor_kind IN ('human','bot','system')),
|
|
359
|
-
payload_json TEXT NOT NULL DEFAULT '{}',
|
|
360
|
-
revision INTEGER NOT NULL,
|
|
361
|
-
execution_epoch INTEGER NOT NULL,
|
|
362
|
-
created_at INTEGER NOT NULL
|
|
363
|
-
);
|
|
364
|
-
|
|
365
|
-
CREATE INDEX idx_event_wi ON work_item_event(work_item_id, created_at);
|
|
366
|
-
|
|
367
|
-
CREATE TABLE work_item_thread ( -- [P1] 工作项的会话代次:历史全保留,同时只有一条在用
|
|
368
|
-
id TEXT PRIMARY KEY,
|
|
369
|
-
work_item_id TEXT NOT NULL REFERENCES work_item(id),
|
|
370
|
-
generation INTEGER NOT NULL,
|
|
371
|
-
session_id TEXT NOT NULL UNIQUE, -- 宿主会话 id;不得跨工作项复用
|
|
372
|
-
member_id TEXT NOT NULL REFERENCES member(id),
|
|
373
|
-
member_version INTEGER NOT NULL, -- 开会话时固化的配置版本
|
|
374
|
-
status TEXT NOT NULL DEFAULT 'active'
|
|
375
|
-
CHECK (status IN ('active','pending_rotate','rotated','closed')),
|
|
376
|
-
turn_count INTEGER NOT NULL DEFAULT 0,
|
|
377
|
-
rotate_after_turns INTEGER NOT NULL DEFAULT 200,
|
|
378
|
-
rotate_after_days INTEGER NOT NULL DEFAULT 30,
|
|
379
|
-
opened_at INTEGER NOT NULL,
|
|
380
|
-
closed_at INTEGER,
|
|
381
|
-
close_reason TEXT,
|
|
382
|
-
UNIQUE (work_item_id, generation)
|
|
383
|
-
);
|
|
384
|
-
|
|
385
|
-
CREATE UNIQUE INDEX uq_thread_current ON work_item_thread(work_item_id)
|
|
386
|
-
WHERE status IN ('active','pending_rotate');
|
|
387
|
-
|
|
388
|
-
CREATE TABLE work_item_check ( -- [P1] 复查:每个代次一条,按模板版本判定
|
|
389
|
-
id TEXT PRIMARY KEY,
|
|
390
|
-
work_item_id TEXT NOT NULL REFERENCES work_item(id),
|
|
391
|
-
generation INTEGER NOT NULL,
|
|
392
|
-
execution_epoch INTEGER NOT NULL,
|
|
393
|
-
template_version_id TEXT REFERENCES work_template_version(id),
|
|
394
|
-
planned_due_at INTEGER NOT NULL, -- 及时率按它算,不按实际执行时间
|
|
395
|
-
status TEXT NOT NULL DEFAULT 'scheduled'
|
|
396
|
-
CHECK (status IN ('scheduled','running','passed','failed','unknown','cancelled')),
|
|
397
|
-
evidence_set_hash TEXT NOT NULL DEFAULT '', -- 判定时的证据集
|
|
398
|
-
result_json TEXT NOT NULL DEFAULT '{}',
|
|
399
|
-
evaluated_at INTEGER,
|
|
400
|
-
created_at INTEGER NOT NULL,
|
|
401
|
-
UNIQUE (work_item_id, generation)
|
|
402
|
-
);
|
|
403
|
-
|
|
404
|
-
CREATE INDEX idx_check_due ON work_item_check(status, planned_due_at);
|
|
405
|
-
|
|
406
|
-
CREATE TABLE task_run ( -- [P1] bot 的一次执行尝试
|
|
407
|
-
id TEXT PRIMARY KEY,
|
|
408
|
-
work_item_id TEXT NOT NULL REFERENCES work_item(id),
|
|
409
|
-
thread_id TEXT NOT NULL REFERENCES work_item_thread(id),
|
|
410
|
-
check_id TEXT REFERENCES work_item_check(id),
|
|
411
|
-
member_id TEXT NOT NULL REFERENCES member(id),
|
|
412
|
-
member_version INTEGER NOT NULL,
|
|
413
|
-
skill_versions_json TEXT NOT NULL DEFAULT '{}',
|
|
414
|
-
execution_epoch INTEGER NOT NULL, -- 创建时工作项的执行代次
|
|
415
|
-
runtime_epoch INTEGER NOT NULL, -- 创建时的宿主启动代次
|
|
416
|
-
fence INTEGER NOT NULL,
|
|
417
|
-
trigger TEXT NOT NULL CHECK (trigger IN ('claim','check','schedule','manual','retry','instruction')),
|
|
418
|
-
attempt INTEGER NOT NULL DEFAULT 1,
|
|
419
|
-
status TEXT NOT NULL DEFAULT 'queued'
|
|
420
|
-
CHECK (status IN ('queued','running','succeeded','failed','cancelled','orphaned','unknown')),
|
|
421
|
-
result_summary TEXT,
|
|
422
|
-
error_kind TEXT,
|
|
423
|
-
created_at INTEGER NOT NULL,
|
|
424
|
-
started_at INTEGER,
|
|
425
|
-
finished_at INTEGER,
|
|
426
|
-
UNIQUE (work_item_id, fence)
|
|
427
|
-
);
|
|
428
|
-
|
|
429
|
-
CREATE UNIQUE INDEX uq_wi_active_run ON task_run(work_item_id) WHERE status IN ('queued','running');
|
|
430
|
-
|
|
431
|
-
CREATE UNIQUE INDEX uq_member_active_run ON task_run(member_id) WHERE status = 'running';
|
|
432
|
-
|
|
433
|
-
CREATE INDEX idx_run_runtime ON task_run(runtime_epoch, status);
|
|
434
|
-
|
|
435
|
-
CREATE TABLE run_step ( -- [P1] 粗粒度步骤摘要;不存 token 级明细
|
|
436
|
-
id TEXT PRIMARY KEY,
|
|
437
|
-
run_id TEXT NOT NULL REFERENCES task_run(id),
|
|
438
|
-
seq INTEGER NOT NULL,
|
|
439
|
-
kind TEXT NOT NULL CHECK (kind IN ('reason','tool','message','wait_human','error')),
|
|
440
|
-
tool_name TEXT,
|
|
441
|
-
summary TEXT NOT NULL,
|
|
442
|
-
payload_ref TEXT,
|
|
443
|
-
created_at INTEGER NOT NULL,
|
|
444
|
-
UNIQUE (run_id, seq)
|
|
445
|
-
);
|
|
446
|
-
|
|
447
|
-
CREATE TABLE lease ( -- [P1] 只管 bot 的活跃运行;人的认领不靠租约
|
|
448
|
-
work_item_id TEXT PRIMARY KEY REFERENCES work_item(id),
|
|
449
|
-
holder_member_id TEXT NOT NULL REFERENCES member(id),
|
|
450
|
-
run_id TEXT NOT NULL REFERENCES task_run(id),
|
|
451
|
-
fence INTEGER NOT NULL,
|
|
452
|
-
runtime_epoch INTEGER NOT NULL,
|
|
453
|
-
lease_until INTEGER NOT NULL,
|
|
454
|
-
renewed_at INTEGER NOT NULL
|
|
455
|
-
);
|
|
456
|
-
|
|
457
|
-
CREATE INDEX idx_lease_expiry ON lease(lease_until);
|
|
458
|
-
|
|
459
|
-
CREATE TABLE evidence ( -- [P1] 追加式;更正另起一行并指向被替代的那行
|
|
460
|
-
id TEXT PRIMARY KEY,
|
|
461
|
-
work_item_id TEXT NOT NULL REFERENCES work_item(id),
|
|
462
|
-
run_id TEXT REFERENCES task_run(id),
|
|
463
|
-
kind TEXT NOT NULL, -- reading / photo / receipt / log / doc
|
|
464
|
-
ref TEXT NOT NULL,
|
|
465
|
-
content_hash TEXT NOT NULL,
|
|
466
|
-
source TEXT NOT NULL DEFAULT '',
|
|
467
|
-
observed_at INTEGER NOT NULL, -- 事实发生时间
|
|
468
|
-
captured_at INTEGER NOT NULL,
|
|
469
|
-
captured_by TEXT NOT NULL REFERENCES member(id),
|
|
470
|
-
on_behalf_of TEXT REFERENCES member(id), -- 代录时填实际反馈人
|
|
471
|
-
supersedes_id TEXT REFERENCES evidence(id),
|
|
472
|
-
status TEXT NOT NULL DEFAULT 'active' CHECK (status IN ('active','superseded','stale')),
|
|
473
|
-
verified_by TEXT REFERENCES member(id),
|
|
474
|
-
verified_at INTEGER,
|
|
475
|
-
purged_at INTEGER -- 保留期满只删文件,行与哈希保留
|
|
476
|
-
);
|
|
477
|
-
|
|
478
|
-
CREATE INDEX idx_ev_wi ON evidence(work_item_id, kind, status);
|
|
479
|
-
|
|
480
|
-
CREATE TABLE handoff ( -- [P1]
|
|
481
|
-
id TEXT PRIMARY KEY,
|
|
482
|
-
work_item_id TEXT NOT NULL REFERENCES work_item(id),
|
|
483
|
-
from_member_id TEXT NOT NULL REFERENCES member(id),
|
|
484
|
-
to_member_id TEXT NOT NULL REFERENCES member(id),
|
|
485
|
-
reason_kind TEXT NOT NULL CHECK (reason_kind IN
|
|
486
|
-
('need_physical_action','need_authority','out_of_domain','capability_missing','takeover')),
|
|
487
|
-
note TEXT NOT NULL,
|
|
488
|
-
status TEXT NOT NULL DEFAULT 'pending' CHECK (status IN ('pending','accepted','declined','cancelled')),
|
|
489
|
-
created_at INTEGER NOT NULL,
|
|
490
|
-
resolved_at INTEGER,
|
|
491
|
-
CHECK (from_member_id <> to_member_id)
|
|
492
|
-
);
|
|
493
|
-
|
|
494
|
-
CREATE UNIQUE INDEX uq_handoff_pending ON handoff(work_item_id) WHERE status = 'pending';
|
|
495
|
-
|
|
496
|
-
CREATE TABLE attention_item ( -- [P1] 需人关注事项:孤儿运行、重试耗尽、连续无进展、告警加重、外发结果未知、漏跑。与审批超时无关
|
|
497
|
-
id TEXT PRIMARY KEY,
|
|
498
|
-
work_item_id TEXT REFERENCES work_item(id),
|
|
499
|
-
project_id TEXT REFERENCES project(id),
|
|
500
|
-
reason TEXT NOT NULL CHECK (reason IN
|
|
501
|
-
('orphaned_run','retries_exhausted','no_progress','severity_raised','outbox_unknown','missed_check')),
|
|
502
|
-
detail TEXT NOT NULL DEFAULT '',
|
|
503
|
-
to_member_id TEXT NOT NULL REFERENCES member(id),
|
|
504
|
-
created_at INTEGER NOT NULL,
|
|
505
|
-
resolved_at INTEGER,
|
|
506
|
-
resolved_by TEXT REFERENCES member(id)
|
|
507
|
-
);
|
|
508
|
-
|
|
509
|
-
CREATE INDEX idx_attention_open ON attention_item(to_member_id, resolved_at);
|
|
510
|
-
|
|
511
|
-
-- ───────────────────────── 6. 提议与审批 ─────────────────────────
|
|
512
|
-
CREATE TABLE proposal ( -- [P1] bot 的「我想做这件事」
|
|
513
|
-
id TEXT PRIMARY KEY,
|
|
514
|
-
subject_key TEXT NOT NULL, -- project:<id> | work_item:<id> | global(不用 NULL)
|
|
515
|
-
project_id TEXT REFERENCES project(id),
|
|
516
|
-
work_item_id TEXT REFERENCES work_item(id),
|
|
517
|
-
kind TEXT NOT NULL CHECK (kind IN
|
|
518
|
-
('create_task','upgrade_task','permission_request','scope_change','knowledge_change')),
|
|
519
|
-
dedupe_key TEXT NOT NULL, -- 规范化后的意图键,例:recount:offline-12
|
|
520
|
-
proposed_by TEXT NOT NULL REFERENCES member(id),
|
|
521
|
-
decider_id TEXT NOT NULL REFERENCES member(id), -- 由谁决定(默认项目负责人)
|
|
522
|
-
title TEXT NOT NULL,
|
|
523
|
-
body TEXT NOT NULL,
|
|
524
|
-
rationale_json TEXT NOT NULL, -- {evidence:[...],expected_effect,cost};evidence 为空不落库
|
|
525
|
-
impact_json TEXT NOT NULL DEFAULT '{}',
|
|
526
|
-
status TEXT NOT NULL DEFAULT 'open'
|
|
527
|
-
CHECK (status IN ('open','accepted','rejected','expired','superseded')),
|
|
528
|
-
duplicate_count INTEGER NOT NULL DEFAULT 0,
|
|
529
|
-
snoozed_until INTEGER, -- 「稍后再说」只改这里,不产生决定
|
|
530
|
-
expires_at INTEGER,
|
|
531
|
-
result_work_item_id TEXT REFERENCES work_item(id),
|
|
532
|
-
revision INTEGER NOT NULL DEFAULT 1,
|
|
533
|
-
created_at INTEGER NOT NULL
|
|
534
|
-
);
|
|
535
|
-
|
|
536
|
-
CREATE UNIQUE INDEX uq_proposal_open ON proposal(subject_key, kind, dedupe_key) WHERE status = 'open';
|
|
537
|
-
|
|
538
|
-
CREATE INDEX idx_proposal_decider ON proposal(decider_id, status);
|
|
539
|
-
|
|
540
|
-
CREATE TABLE decision ( -- [P1] 只记终态决定;一条提议至多一条
|
|
541
|
-
id TEXT PRIMARY KEY,
|
|
542
|
-
proposal_id TEXT NOT NULL UNIQUE REFERENCES proposal(id),
|
|
543
|
-
decided_by TEXT NOT NULL REFERENCES member(id),
|
|
544
|
-
decision TEXT NOT NULL CHECK (decision IN ('accept','reject')),
|
|
545
|
-
channel TEXT NOT NULL CHECK (channel = 'ui'),
|
|
546
|
-
note TEXT NOT NULL DEFAULT '',
|
|
547
|
-
created_at INTEGER NOT NULL,
|
|
548
|
-
CHECK (decision <> 'reject' OR length(note) > 0)
|
|
549
|
-
);
|
|
550
|
-
|
|
551
|
-
CREATE TABLE approval ( -- [P1] 人的一次把关
|
|
552
|
-
id TEXT PRIMARY KEY,
|
|
553
|
-
subject_kind TEXT NOT NULL CHECK (subject_kind IN
|
|
554
|
-
('work_item_close','evidence_override','asset_disposal','skill_publish',
|
|
555
|
-
'target_change','permission_grant','project_archive')),
|
|
556
|
-
subject_id TEXT NOT NULL, -- 前三类指向 work_item.id
|
|
557
|
-
subject_revision INTEGER NOT NULL, -- 申请时对象的修订号
|
|
558
|
-
execution_epoch INTEGER NOT NULL DEFAULT 0, -- 仅工作项类审批使用
|
|
559
|
-
evidence_set_hash TEXT NOT NULL DEFAULT '', -- 申请时锁定的证据集 / 内容哈希
|
|
560
|
-
criteria_ref TEXT NOT NULL DEFAULT '', -- 判据版本:work_template_version.id 等
|
|
561
|
-
criteria_json TEXT NOT NULL DEFAULT '{}', -- 判据逐条结果快照,供界面展示
|
|
562
|
-
reason_text TEXT NOT NULL,
|
|
563
|
-
requested_by TEXT NOT NULL REFERENCES member(id),
|
|
564
|
-
requested_channel TEXT NOT NULL CHECK (requested_channel IN ('tool','ui','system')),
|
|
565
|
-
approver_id TEXT NOT NULL REFERENCES member(id), -- 申请时按模板验收策略解析,不由申请方挑选
|
|
566
|
-
status TEXT NOT NULL DEFAULT 'pending'
|
|
567
|
-
CHECK (status IN ('pending','approved','rejected','returned',
|
|
568
|
-
'expired','invalidated','withdrawn')),
|
|
569
|
-
decided_by TEXT REFERENCES member(id),
|
|
570
|
-
decided_channel TEXT CHECK (decided_channel IS NULL OR decided_channel = 'ui'),
|
|
571
|
-
decision_note TEXT NOT NULL DEFAULT '',
|
|
572
|
-
decided_at INTEGER,
|
|
573
|
-
invalidated_reason TEXT,
|
|
574
|
-
expires_at INTEGER NOT NULL,
|
|
575
|
-
created_at INTEGER NOT NULL,
|
|
576
|
-
CHECK (status NOT IN ('rejected','returned') OR length(decision_note) > 0)
|
|
577
|
-
);
|
|
578
|
-
|
|
579
|
-
CREATE UNIQUE INDEX uq_approval_pending ON approval(subject_kind, subject_id) WHERE status = 'pending';
|
|
580
|
-
|
|
581
|
-
CREATE INDEX idx_approval_approver ON approval(approver_id, status, expires_at);
|
|
582
|
-
|
|
583
|
-
-- ───────────────────────── 7. 调度、出站、幂等与审计 ─────────────────────────
|
|
584
|
-
CREATE TABLE schedule ( -- [P1] 周期计划;逐单复查走 work_item_check
|
|
585
|
-
id TEXT PRIMARY KEY,
|
|
586
|
-
project_id TEXT REFERENCES project(id),
|
|
587
|
-
template_version_id TEXT REFERENCES work_template_version(id),
|
|
588
|
-
member_id TEXT REFERENCES member(id),
|
|
589
|
-
owner_human_id TEXT NOT NULL REFERENCES member(id),
|
|
590
|
-
kind TEXT NOT NULL CHECK (kind IN ('at','every','cron')),
|
|
591
|
-
spec TEXT NOT NULL,
|
|
592
|
-
timezone TEXT NOT NULL, -- IANA 时区
|
|
593
|
-
misfire_policy TEXT NOT NULL DEFAULT 'catch_up_latest' CHECK (misfire_policy IN ('each','catch_up_latest','skip')),
|
|
594
|
-
concurrency TEXT NOT NULL DEFAULT 'skip' CHECK (concurrency IN ('skip','queue','replace')),
|
|
595
|
-
max_fires INTEGER,
|
|
596
|
-
fired_count INTEGER NOT NULL DEFAULT 0,
|
|
597
|
-
next_fire_at INTEGER NOT NULL,
|
|
598
|
-
enabled INTEGER NOT NULL DEFAULT 1,
|
|
599
|
-
revision INTEGER NOT NULL DEFAULT 1,
|
|
600
|
-
created_at INTEGER NOT NULL
|
|
601
|
-
);
|
|
602
|
-
|
|
603
|
-
CREATE INDEX idx_sched_next ON schedule(enabled, next_fire_at);
|
|
604
|
-
|
|
605
|
-
CREATE TABLE schedule_occurrence ( -- [P1] 双触发防重靠主键,不靠时间比较
|
|
606
|
-
schedule_id TEXT NOT NULL REFERENCES schedule(id),
|
|
607
|
-
planned_at INTEGER NOT NULL,
|
|
608
|
-
status TEXT NOT NULL DEFAULT 'fired' CHECK (status IN ('fired','skipped','missed')),
|
|
609
|
-
work_item_id TEXT REFERENCES work_item(id),
|
|
610
|
-
fired_at INTEGER NOT NULL, -- 实际执行时间
|
|
611
|
-
PRIMARY KEY (schedule_id, planned_at)
|
|
612
|
-
);
|
|
613
|
-
|
|
614
|
-
CREATE TABLE outbox ( -- [P1] 只放对外动作;HTTP 命令幂等在 command_receipt
|
|
615
|
-
id TEXT PRIMARY KEY,
|
|
616
|
-
action TEXT NOT NULL, -- 例:lark.notify / ticket.create
|
|
617
|
-
payload_json TEXT NOT NULL,
|
|
618
|
-
idempotency_key TEXT NOT NULL UNIQUE,
|
|
619
|
-
work_item_id TEXT REFERENCES work_item(id),
|
|
620
|
-
execution_epoch INTEGER, -- 发送前与工作项当前代次比对
|
|
621
|
-
approval_id TEXT REFERENCES approval(id),
|
|
622
|
-
requested_by TEXT NOT NULL REFERENCES member(id),
|
|
623
|
-
status TEXT NOT NULL DEFAULT 'pending'
|
|
624
|
-
CHECK (status IN ('pending','sending','sent','confirmed','unknown','cancelled','failed')),
|
|
625
|
-
not_after INTEGER, -- 过了就不再发
|
|
626
|
-
available_at INTEGER NOT NULL,
|
|
627
|
-
attempts INTEGER NOT NULL DEFAULT 0,
|
|
628
|
-
max_attempts INTEGER NOT NULL DEFAULT 5,
|
|
629
|
-
external_ref TEXT,
|
|
630
|
-
last_error TEXT,
|
|
631
|
-
created_at INTEGER NOT NULL,
|
|
632
|
-
sending_at INTEGER,
|
|
633
|
-
finished_at INTEGER
|
|
634
|
-
);
|
|
635
|
-
|
|
636
|
-
CREATE INDEX idx_outbox_due ON outbox(status, available_at);
|
|
637
|
-
|
|
638
|
-
CREATE TABLE command_receipt ( -- [P1] 写命令幂等:同键同载荷返回首次回执
|
|
639
|
-
scope_key TEXT PRIMARY KEY, -- hash(principal + action + idempotencyKey)
|
|
640
|
-
command_id TEXT NOT NULL,
|
|
641
|
-
principal_id TEXT NOT NULL REFERENCES member(id),
|
|
642
|
-
action TEXT NOT NULL,
|
|
643
|
-
payload_hash TEXT NOT NULL,
|
|
644
|
-
receipt_json TEXT NOT NULL,
|
|
645
|
-
created_at INTEGER NOT NULL
|
|
646
|
-
);
|
|
647
|
-
|
|
648
|
-
CREATE INDEX idx_receipt_age ON command_receipt(created_at);
|
|
649
|
-
|
|
650
|
-
CREATE TABLE change_log ( -- [P1] 界面「快照 + 游标」的游标来源
|
|
651
|
-
seq INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
652
|
-
object_kind TEXT NOT NULL,
|
|
653
|
-
object_id TEXT NOT NULL,
|
|
654
|
-
revision INTEGER NOT NULL,
|
|
655
|
-
change_kind TEXT NOT NULL,
|
|
656
|
-
created_at INTEGER NOT NULL
|
|
657
|
-
);
|
|
658
|
-
|
|
659
|
-
CREATE TABLE tick_log ( -- [P1]
|
|
660
|
-
tick_seq INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
661
|
-
runtime_epoch INTEGER NOT NULL,
|
|
662
|
-
started_at INTEGER NOT NULL,
|
|
663
|
-
finished_at INTEGER,
|
|
664
|
-
fired INTEGER NOT NULL DEFAULT 0,
|
|
665
|
-
recovered INTEGER NOT NULL DEFAULT 0,
|
|
666
|
-
error_text TEXT
|
|
667
|
-
);
|
|
668
|
-
|
|
669
|
-
CREATE TABLE audit_log ( -- [P1] 追加式审计
|
|
670
|
-
id TEXT PRIMARY KEY,
|
|
671
|
-
actor_id TEXT NOT NULL REFERENCES member(id),
|
|
672
|
-
actor_kind TEXT NOT NULL CHECK (actor_kind IN ('human','bot','system')),
|
|
673
|
-
on_behalf_of TEXT REFERENCES member(id), -- 代录
|
|
674
|
-
channel TEXT NOT NULL CHECK (channel IN ('ui','tool','system','connector')),
|
|
675
|
-
auth_subject TEXT, -- 宿主账号 / SSO 主体
|
|
676
|
-
host_preset TEXT, -- 当时的宿主权限预设名;读不到记 'unreadable'
|
|
677
|
-
action TEXT NOT NULL,
|
|
678
|
-
subject_kind TEXT NOT NULL,
|
|
679
|
-
subject_id TEXT NOT NULL,
|
|
680
|
-
before_json TEXT,
|
|
681
|
-
after_json TEXT,
|
|
682
|
-
correlation_id TEXT NOT NULL,
|
|
683
|
-
created_at INTEGER NOT NULL
|
|
684
|
-
);
|
|
685
|
-
|
|
686
|
-
CREATE INDEX idx_audit_subject ON audit_log(subject_kind, subject_id, created_at);
|
|
687
|
-
|
|
688
|
-
-- ───────────────────────── 9. 触发器:把最要紧的不变量落在库里 ─────────────────────────
|
|
689
|
-
|
|
690
|
-
-- 9.1 授权:审批权只能给人,且只能经岗位
|
|
691
|
-
CREATE TRIGGER trg_member_permission_bot_only BEFORE INSERT ON member_permission
|
|
692
|
-
WHEN (SELECT kind FROM member WHERE id = NEW.member_id) IS NOT 'bot'
|
|
693
|
-
BEGIN SELECT RAISE(ABORT, 'POLICY_DENIED: member_permission is for bots only'); END;
|
|
694
|
-
|
|
695
|
-
CREATE TRIGGER trg_bot_no_human_capability BEFORE INSERT ON member_permission
|
|
696
|
-
WHEN NEW.capability IN ('approval.decide','proposal.decide','work_item.close','skill.publish',
|
|
697
|
-
'target.publish','review.confirm','project.archive','member.manage','position.manage','permission.grant')
|
|
698
|
-
BEGIN SELECT RAISE(ABORT, 'POLICY_DENIED: capability reserved for humans'); END;
|
|
699
|
-
|
|
700
|
-
CREATE TRIGGER trg_member_position_human_only BEFORE INSERT ON member_position
|
|
701
|
-
WHEN (SELECT kind FROM member WHERE id = NEW.member_id) IS NOT 'human'
|
|
702
|
-
BEGIN SELECT RAISE(ABORT, 'POLICY_DENIED: positions are for humans only'); END;
|
|
703
|
-
|
|
704
|
-
CREATE TRIGGER trg_hams_user_human_only BEFORE INSERT ON hams_user
|
|
705
|
-
WHEN (SELECT kind FROM member WHERE id = NEW.member_id) IS NOT 'human'
|
|
706
|
-
BEGIN SELECT RAISE(ABORT, 'POLICY_DENIED: login identity must bind a human member'); END;
|
|
707
|
-
|
|
708
|
-
-- 9.2 配置版本不可变
|
|
709
|
-
CREATE TRIGGER trg_member_version_immutable BEFORE UPDATE ON member_version
|
|
710
|
-
BEGIN SELECT RAISE(ABORT, 'IMMUTABLE: member_version'); END;
|
|
711
|
-
|
|
712
|
-
CREATE TRIGGER trg_target_version_immutable BEFORE UPDATE ON project_target_version
|
|
713
|
-
BEGIN SELECT RAISE(ABORT, 'IMMUTABLE: project_target_version'); END;
|
|
714
|
-
|
|
715
|
-
-- 9.3 工作项:负责人必须是人;计数器只增;状态只走白名单;恢复只回到暂停前状态
|
|
716
|
-
CREATE TRIGGER trg_wi_owner_human_ins BEFORE INSERT ON work_item
|
|
717
|
-
WHEN (SELECT kind FROM member WHERE id = NEW.owner_human_id) IS NOT 'human'
|
|
718
|
-
BEGIN SELECT RAISE(ABORT, 'POLICY_DENIED: work item owner must be human'); END;
|
|
719
|
-
|
|
720
|
-
CREATE TRIGGER trg_wi_owner_human_upd BEFORE UPDATE OF owner_human_id ON work_item
|
|
721
|
-
WHEN (SELECT kind FROM member WHERE id = NEW.owner_human_id) IS NOT 'human'
|
|
722
|
-
BEGIN SELECT RAISE(ABORT, 'POLICY_DENIED: work item owner must be human'); END;
|
|
723
|
-
|
|
724
|
-
CREATE TRIGGER trg_wi_counters_monotonic BEFORE UPDATE ON work_item
|
|
725
|
-
WHEN NEW.revision <= OLD.revision OR NEW.execution_epoch < OLD.execution_epoch OR NEW.fence_seq < OLD.fence_seq
|
|
726
|
-
BEGIN SELECT RAISE(ABORT, 'VERSION_CONFLICT: every write must bump revision; counters never go back'); END;
|
|
727
|
-
|
|
728
|
-
CREATE TRIGGER trg_wi_transition_whitelist BEFORE UPDATE OF status ON work_item
|
|
729
|
-
WHEN NEW.status <> OLD.status AND NOT EXISTS (
|
|
730
|
-
SELECT 1 FROM work_item_transition t WHERE t.from_status = OLD.status AND t.to_status = NEW.status)
|
|
731
|
-
BEGIN SELECT RAISE(ABORT, 'STATE_INVALID: transition not in whitelist'); END;
|
|
732
|
-
|
|
733
|
-
CREATE TRIGGER trg_wi_resume_target BEFORE UPDATE OF status ON work_item
|
|
734
|
-
WHEN OLD.status = 'paused' AND NEW.status NOT IN ('paused','cancelled') AND NEW.status IS NOT OLD.resume_state
|
|
735
|
-
BEGIN SELECT RAISE(ABORT, 'STATE_INVALID: resume must return to the state before pause'); END;
|
|
736
|
-
|
|
737
|
-
CREATE TRIGGER trg_wi_epoch_on_pause BEFORE UPDATE OF status ON work_item
|
|
738
|
-
WHEN NEW.status <> OLD.status
|
|
739
|
-
AND (NEW.status IN ('paused','cancelled') OR OLD.status IN ('paused','completed','failed'))
|
|
740
|
-
AND NEW.execution_epoch = OLD.execution_epoch
|
|
741
|
-
BEGIN SELECT RAISE(ABORT, 'EPOCH_STALE: pause/resume/cancel/reopen/retry must bump execution_epoch'); END;
|
|
742
|
-
|
|
743
|
-
CREATE TRIGGER trg_wi_epoch_on_reassign BEFORE UPDATE OF assignee_id ON work_item
|
|
744
|
-
WHEN OLD.assignee_id IS NOT NULL AND NEW.assignee_id IS NOT OLD.assignee_id
|
|
745
|
-
AND NEW.execution_epoch = OLD.execution_epoch
|
|
746
|
-
BEGIN SELECT RAISE(ABORT, 'EPOCH_STALE: changing the executor must bump execution_epoch'); END;
|
|
747
|
-
|
|
748
|
-
-- 9.4 关单:有效审批(人工路径)或通过的复查(已授权自动关单),二者都锁定代次与证据集
|
|
749
|
-
CREATE TRIGGER trg_wi_complete_guard BEFORE UPDATE OF status ON work_item
|
|
750
|
-
WHEN NEW.status = 'completed' AND OLD.status <> 'completed' AND (
|
|
751
|
-
(NEW.close_requires_approval = 1 AND NOT EXISTS (
|
|
752
|
-
SELECT 1 FROM approval a
|
|
753
|
-
WHERE a.subject_kind = 'work_item_close' AND a.subject_id = OLD.id AND a.status = 'approved'
|
|
754
|
-
AND a.subject_revision = OLD.revision AND a.execution_epoch = OLD.execution_epoch
|
|
755
|
-
AND a.evidence_set_hash = OLD.evidence_set_hash))
|
|
756
|
-
OR
|
|
757
|
-
(NEW.close_requires_approval = 0 AND NOT EXISTS (
|
|
758
|
-
SELECT 1 FROM work_item_check c
|
|
759
|
-
WHERE c.work_item_id = OLD.id AND c.status = 'passed'
|
|
760
|
-
AND c.execution_epoch = OLD.execution_epoch AND c.evidence_set_hash = OLD.evidence_set_hash))
|
|
761
|
-
)
|
|
762
|
-
BEGIN SELECT RAISE(ABORT, 'APPROVAL_REQUIRED: close needs a valid approval or a passed check on the same evidence'); END;
|
|
763
|
-
|
|
764
|
-
-- 9.5 工作项或证据一变,待决的审批立即作废
|
|
765
|
-
CREATE TRIGGER trg_wi_change_invalidates_approval AFTER UPDATE OF revision ON work_item
|
|
766
|
-
WHEN NEW.revision <> OLD.revision
|
|
767
|
-
BEGIN
|
|
768
|
-
UPDATE approval SET status = 'invalidated', invalidated_reason = 'subject_changed'
|
|
769
|
-
WHERE subject_kind IN ('work_item_close','evidence_override','asset_disposal') AND subject_id = NEW.id
|
|
770
|
-
AND status = 'pending' AND subject_revision <> NEW.revision;
|
|
771
|
-
END;
|
|
772
|
-
|
|
773
|
-
-- 证据新增或状态变化都会递增工作项修订号:待决审批经上一条触发器作废,
|
|
774
|
-
-- 已批准但尚未关单的审批也因修订号不再匹配而无法用于关单(9.4)。证据集哈希由服务在同一事务重算。
|
|
775
|
-
CREATE TRIGGER trg_evidence_insert_bumps_revision AFTER INSERT ON evidence
|
|
776
|
-
BEGIN
|
|
777
|
-
UPDATE work_item SET revision = revision + 1, updated_at = NEW.captured_at WHERE id = NEW.work_item_id;
|
|
778
|
-
END;
|
|
779
|
-
|
|
780
|
-
CREATE TRIGGER trg_evidence_status_bumps_revision AFTER UPDATE OF status ON evidence
|
|
781
|
-
WHEN NEW.status <> OLD.status
|
|
782
|
-
BEGIN
|
|
783
|
-
UPDATE work_item SET revision = revision + 1 WHERE id = NEW.work_item_id;
|
|
784
|
-
END;
|
|
785
|
-
|
|
786
|
-
CREATE TRIGGER trg_evidence_immutable BEFORE UPDATE OF work_item_id, kind, ref, content_hash, observed_at, captured_by ON evidence
|
|
787
|
-
BEGIN SELECT RAISE(ABORT, 'IMMUTABLE: evidence facts are append-only'); END;
|
|
788
|
-
|
|
789
|
-
CREATE TRIGGER trg_evidence_no_delete BEFORE DELETE ON evidence
|
|
790
|
-
BEGIN SELECT RAISE(ABORT, 'IMMUTABLE: evidence rows are never deleted'); END;
|
|
791
|
-
|
|
792
|
-
-- 9.6 审批:审批人与决定人必须是人、走界面、是指定审批人、未过期;终态不可再改
|
|
793
|
-
CREATE TRIGGER trg_approval_approver_human_ins BEFORE INSERT ON approval
|
|
794
|
-
WHEN (SELECT kind FROM member WHERE id = NEW.approver_id) IS NOT 'human'
|
|
795
|
-
BEGIN SELECT RAISE(ABORT, 'POLICY_DENIED: approver must be human'); END;
|
|
796
|
-
|
|
797
|
-
CREATE TRIGGER trg_approval_approver_human_upd BEFORE UPDATE OF approver_id ON approval
|
|
798
|
-
WHEN (SELECT kind FROM member WHERE id = NEW.approver_id) IS NOT 'human' OR OLD.status <> 'pending'
|
|
799
|
-
BEGIN SELECT RAISE(ABORT, 'POLICY_DENIED: approver must be human and approval still pending'); END;
|
|
800
|
-
|
|
801
|
-
CREATE TRIGGER trg_approval_terminal BEFORE UPDATE OF status ON approval
|
|
802
|
-
WHEN OLD.status <> 'pending' AND NEW.status <> OLD.status
|
|
803
|
-
BEGIN SELECT RAISE(ABORT, 'STATE_INVALID: approval already decided'); END;
|
|
804
|
-
|
|
805
|
-
CREATE TRIGGER trg_approval_decision_guard BEFORE UPDATE OF status ON approval
|
|
806
|
-
WHEN NEW.status IN ('approved','rejected','returned') AND (
|
|
807
|
-
NEW.decided_by IS NULL
|
|
808
|
-
OR NEW.decided_by <> OLD.approver_id
|
|
809
|
-
OR NEW.decided_channel IS NOT 'ui'
|
|
810
|
-
OR NEW.decided_at IS NULL
|
|
811
|
-
OR NEW.decided_at >= OLD.expires_at
|
|
812
|
-
OR (SELECT kind FROM member WHERE id = NEW.decided_by) IS NOT 'human')
|
|
813
|
-
BEGIN SELECT RAISE(ABORT, 'POLICY_DENIED: decision must come from the designated human approver via UI before expiry'); END;
|
|
814
|
-
|
|
815
|
-
-- 9.7 提议只能由 bot 发,决定只能由人做
|
|
816
|
-
CREATE TRIGGER trg_proposal_by_bot BEFORE INSERT ON proposal
|
|
817
|
-
WHEN (SELECT kind FROM member WHERE id = NEW.proposed_by) IS NOT 'bot'
|
|
818
|
-
OR (SELECT kind FROM member WHERE id = NEW.decider_id) IS NOT 'human'
|
|
819
|
-
BEGIN SELECT RAISE(ABORT, 'POLICY_DENIED: proposals come from bots and are decided by humans'); END;
|
|
820
|
-
|
|
821
|
-
CREATE TRIGGER trg_decision_by_human BEFORE INSERT ON decision
|
|
822
|
-
WHEN (SELECT kind FROM member WHERE id = NEW.decided_by) IS NOT 'human'
|
|
823
|
-
OR NEW.decided_by IS NOT (SELECT decider_id FROM proposal WHERE id = NEW.proposal_id)
|
|
824
|
-
OR (SELECT status FROM proposal WHERE id = NEW.proposal_id) IS NOT 'open'
|
|
825
|
-
BEGIN SELECT RAISE(ABORT, 'POLICY_DENIED: decision must come from the designated human on an open proposal'); END;
|
|
826
|
-
|
|
827
|
-
-- 9.8 运行:必须落在同一工作项「在用」的会话代次上,且代次是当前的;租约只给 bot
|
|
828
|
-
CREATE TRIGGER trg_run_binds_current_thread BEFORE INSERT ON task_run
|
|
829
|
-
WHEN NOT EXISTS (SELECT 1 FROM work_item_thread t
|
|
830
|
-
WHERE t.id = NEW.thread_id AND t.work_item_id = NEW.work_item_id
|
|
831
|
-
AND t.status = 'active' AND t.member_id = NEW.member_id)
|
|
832
|
-
BEGIN SELECT RAISE(ABORT, 'STATE_INVALID: run must bind the active thread of the same work item and bot'); END;
|
|
833
|
-
|
|
834
|
-
CREATE TRIGGER trg_run_epoch_current BEFORE INSERT ON task_run
|
|
835
|
-
WHEN NEW.execution_epoch IS NOT (SELECT execution_epoch FROM work_item WHERE id = NEW.work_item_id)
|
|
836
|
-
BEGIN SELECT RAISE(ABORT, 'EPOCH_STALE: run must carry the current execution_epoch'); END;
|
|
837
|
-
|
|
838
|
-
CREATE TRIGGER trg_lease_bot_only BEFORE INSERT ON lease
|
|
839
|
-
WHEN (SELECT kind FROM member WHERE id = NEW.holder_member_id) IS NOT 'bot'
|
|
840
|
-
BEGIN SELECT RAISE(ABORT, 'POLICY_DENIED: leases are for bot runs only'); END;
|
|
841
|
-
|
|
842
|
-
-- 9.10 时间线与审计只追加(按月归档由迁移步骤在同一事务内临时解除后恢复)
|
|
843
|
-
CREATE TRIGGER trg_event_no_update BEFORE UPDATE ON work_item_event
|
|
844
|
-
BEGIN SELECT RAISE(ABORT, 'IMMUTABLE: work_item_event'); END;
|
|
845
|
-
|
|
846
|
-
CREATE TRIGGER trg_audit_no_update BEFORE UPDATE ON audit_log
|
|
847
|
-
BEGIN SELECT RAISE(ABORT, 'IMMUTABLE: audit_log'); END;
|
|
848
|
-
|
|
849
|
-
CREATE TRIGGER trg_audit_no_delete BEFORE DELETE ON audit_log
|
|
850
|
-
BEGIN SELECT RAISE(ABORT, 'IMMUTABLE: audit_log'); END;
|
|
851
|
-
`
|
|
852
|
-
|
|
853
|
-
export const P1_SCHEMA_META = {
|
|
854
|
-
authority: "docs/schema-2026-11-14.sql",
|
|
855
|
-
authoritySha256: "91590bcaaeb9612f800a39297d4effe9ddcb419dbb822de1a3eb268be6969ec7",
|
|
856
|
-
tables: 41,
|
|
857
|
-
indexes: 30,
|
|
858
|
-
triggers: 31,
|
|
859
|
-
inserts: 2,
|
|
860
|
-
p2Tables: 14,
|
|
861
|
-
shellTables: 2,
|
|
862
|
-
tableNames: ["hams_meta","member","member_version","member_permission","member_tool_policy","hams_user","position","position_permission","member_position","project","project_target_version","project_metric","project_budget","metric_observation","asset","ledger_snapshot","work_template","work_template_version","event_inbox","work_item_transition","work_item","work_item_relation","work_item_event","work_item_thread","work_item_check","task_run","run_step","lease","evidence","handoff","attention_item","proposal","decision","approval","schedule","schedule_occurrence","outbox","command_receipt","change_log","tick_log","audit_log"] as const,
|
|
863
|
-
} as const
|
|
864
|
-
|
|
865
|
-
export type P1SchemaMeta = typeof P1_SCHEMA_META
|