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,54 +0,0 @@
|
|
|
1
|
-
-- ⚠️ 自动生成,请勿手改。
|
|
2
|
-
-- 来源(权威):docs/schema-2026-11-14.sql
|
|
3
|
-
-- 权威文件 sha256:91590bcaaeb9612f800a39297d4effe9ddcb419dbb822de1a3eb268be6969ec7
|
|
4
|
-
-- 生成命令:node scripts/split-schema.mjs
|
|
5
|
-
-- 内容:v2 知识 4 表 / 0 索引 / 0 触发器 / 0 种子语句
|
|
6
|
-
-- 分期依据:权威 DDL 里 [P2] 且表名属于知识子集的表;v1 已建的 41 张 P1 表不重复。
|
|
7
|
-
-- ───────────────────────── 2. 知识 ─────────────────────────
|
|
8
|
-
CREATE TABLE kb_table ( -- [P2]
|
|
9
|
-
id TEXT PRIMARY KEY,
|
|
10
|
-
name TEXT NOT NULL,
|
|
11
|
-
source_kind TEXT NOT NULL CHECK (source_kind IN ('hive','lark_base','local','webhook')),
|
|
12
|
-
source_ref TEXT,
|
|
13
|
-
schema_json TEXT NOT NULL,
|
|
14
|
-
field_map_status TEXT NOT NULL DEFAULT 'ok' CHECK (field_map_status IN ('ok','missing')),
|
|
15
|
-
updated_at INTEGER NOT NULL
|
|
16
|
-
);
|
|
17
|
-
|
|
18
|
-
CREATE TABLE kb_doc ( -- [P2]
|
|
19
|
-
id TEXT PRIMARY KEY,
|
|
20
|
-
title TEXT NOT NULL,
|
|
21
|
-
body_ref TEXT NOT NULL, -- 绝对路径或 URL;面板内撰写的落在 hams/knowledge/docs
|
|
22
|
-
revision INTEGER NOT NULL DEFAULT 1,
|
|
23
|
-
owner_id TEXT NOT NULL REFERENCES member(id),
|
|
24
|
-
source_kind TEXT NOT NULL DEFAULT 'hams'
|
|
25
|
-
CHECK (source_kind IN ('hams','file')), -- hams=面板内撰写;file=导入的本地文件
|
|
26
|
-
content_hash TEXT NOT NULL DEFAULT '', -- sha256(原文),用于判断磁盘上的文件是否被外部改动
|
|
27
|
-
bytes INTEGER NOT NULL DEFAULT 0,
|
|
28
|
-
status TEXT NOT NULL DEFAULT 'draft'
|
|
29
|
-
CHECK (status IN ('draft','published','archived')),
|
|
30
|
-
updated_at INTEGER NOT NULL
|
|
31
|
-
);
|
|
32
|
-
|
|
33
|
-
CREATE TABLE kb_skill ( -- [P2]
|
|
34
|
-
id TEXT PRIMARY KEY,
|
|
35
|
-
name TEXT NOT NULL UNIQUE,
|
|
36
|
-
domain TEXT,
|
|
37
|
-
published_version_id TEXT REFERENCES kb_skill_version(id), -- NULL = 尚无已发布版本
|
|
38
|
-
usage_count INTEGER NOT NULL DEFAULT 0,
|
|
39
|
-
created_at INTEGER NOT NULL
|
|
40
|
-
);
|
|
41
|
-
|
|
42
|
-
CREATE TABLE kb_skill_version ( -- [P2] 发布走 approval(subject_kind='skill_publish')
|
|
43
|
-
id TEXT PRIMARY KEY,
|
|
44
|
-
skill_id TEXT NOT NULL REFERENCES kb_skill(id),
|
|
45
|
-
version TEXT NOT NULL,
|
|
46
|
-
manifest_json TEXT NOT NULL,
|
|
47
|
-
content_hash TEXT NOT NULL,
|
|
48
|
-
status TEXT NOT NULL DEFAULT 'draft'
|
|
49
|
-
CHECK (status IN ('draft','in_review','published','retired')),
|
|
50
|
-
authored_by TEXT NOT NULL REFERENCES member(id),
|
|
51
|
-
published_by TEXT REFERENCES member(id),
|
|
52
|
-
published_at INTEGER,
|
|
53
|
-
UNIQUE (skill_id, version)
|
|
54
|
-
);
|
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
// ⚠️ 自动生成,请勿手改。生成命令:node scripts/split-schema.mjs
|
|
2
|
-
// 来源:docs/schema-2026-11-14.sql(sha256 91590bcaaeb9612f800a39297d4effe9ddcb419dbb822de1a3eb268be6969ec7)
|
|
3
|
-
// v2 迁移:只含知识四张表;其余 P2/壳表仍按分期后置。
|
|
4
|
-
|
|
5
|
-
export const KNOWLEDGE_SCHEMA_SQL = `-- ⚠️ 自动生成,请勿手改。
|
|
6
|
-
-- 来源(权威):docs/schema-2026-11-14.sql
|
|
7
|
-
-- 权威文件 sha256:91590bcaaeb9612f800a39297d4effe9ddcb419dbb822de1a3eb268be6969ec7
|
|
8
|
-
-- 生成命令:node scripts/split-schema.mjs
|
|
9
|
-
-- 内容:v2 知识 4 表 / 0 索引 / 0 触发器 / 0 种子语句
|
|
10
|
-
-- 分期依据:权威 DDL 里 [P2] 且表名属于知识子集的表;v1 已建的 41 张 P1 表不重复。
|
|
11
|
-
-- ───────────────────────── 2. 知识 ─────────────────────────
|
|
12
|
-
CREATE TABLE kb_table ( -- [P2]
|
|
13
|
-
id TEXT PRIMARY KEY,
|
|
14
|
-
name TEXT NOT NULL,
|
|
15
|
-
source_kind TEXT NOT NULL CHECK (source_kind IN ('hive','lark_base','local','webhook')),
|
|
16
|
-
source_ref TEXT,
|
|
17
|
-
schema_json TEXT NOT NULL,
|
|
18
|
-
field_map_status TEXT NOT NULL DEFAULT 'ok' CHECK (field_map_status IN ('ok','missing')),
|
|
19
|
-
updated_at INTEGER NOT NULL
|
|
20
|
-
);
|
|
21
|
-
|
|
22
|
-
CREATE TABLE kb_doc ( -- [P2]
|
|
23
|
-
id TEXT PRIMARY KEY,
|
|
24
|
-
title TEXT NOT NULL,
|
|
25
|
-
body_ref TEXT NOT NULL, -- 绝对路径或 URL;面板内撰写的落在 hams/knowledge/docs
|
|
26
|
-
revision INTEGER NOT NULL DEFAULT 1,
|
|
27
|
-
owner_id TEXT NOT NULL REFERENCES member(id),
|
|
28
|
-
source_kind TEXT NOT NULL DEFAULT 'hams'
|
|
29
|
-
CHECK (source_kind IN ('hams','file')), -- hams=面板内撰写;file=导入的本地文件
|
|
30
|
-
content_hash TEXT NOT NULL DEFAULT '', -- sha256(原文),用于判断磁盘上的文件是否被外部改动
|
|
31
|
-
bytes INTEGER NOT NULL DEFAULT 0,
|
|
32
|
-
status TEXT NOT NULL DEFAULT 'draft'
|
|
33
|
-
CHECK (status IN ('draft','published','archived')),
|
|
34
|
-
updated_at INTEGER NOT NULL
|
|
35
|
-
);
|
|
36
|
-
|
|
37
|
-
CREATE TABLE kb_skill ( -- [P2]
|
|
38
|
-
id TEXT PRIMARY KEY,
|
|
39
|
-
name TEXT NOT NULL UNIQUE,
|
|
40
|
-
domain TEXT,
|
|
41
|
-
published_version_id TEXT REFERENCES kb_skill_version(id), -- NULL = 尚无已发布版本
|
|
42
|
-
usage_count INTEGER NOT NULL DEFAULT 0,
|
|
43
|
-
created_at INTEGER NOT NULL
|
|
44
|
-
);
|
|
45
|
-
|
|
46
|
-
CREATE TABLE kb_skill_version ( -- [P2] 发布走 approval(subject_kind='skill_publish')
|
|
47
|
-
id TEXT PRIMARY KEY,
|
|
48
|
-
skill_id TEXT NOT NULL REFERENCES kb_skill(id),
|
|
49
|
-
version TEXT NOT NULL,
|
|
50
|
-
manifest_json TEXT NOT NULL,
|
|
51
|
-
content_hash TEXT NOT NULL,
|
|
52
|
-
status TEXT NOT NULL DEFAULT 'draft'
|
|
53
|
-
CHECK (status IN ('draft','in_review','published','retired')),
|
|
54
|
-
authored_by TEXT NOT NULL REFERENCES member(id),
|
|
55
|
-
published_by TEXT REFERENCES member(id),
|
|
56
|
-
published_at INTEGER,
|
|
57
|
-
UNIQUE (skill_id, version)
|
|
58
|
-
);
|
|
59
|
-
`
|
|
60
|
-
|
|
61
|
-
export const KNOWLEDGE_SCHEMA_META = {
|
|
62
|
-
authority: "docs/schema-2026-11-14.sql",
|
|
63
|
-
authoritySha256: "91590bcaaeb9612f800a39297d4effe9ddcb419dbb822de1a3eb268be6969ec7",
|
|
64
|
-
tables: 4,
|
|
65
|
-
tableNames: ["kb_table","kb_doc","kb_skill","kb_skill_version"] as const,
|
|
66
|
-
} as const
|
|
67
|
-
|
|
68
|
-
export type KnowledgeSchemaMeta = typeof KNOWLEDGE_SCHEMA_META
|
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
// Generated by node src/store/schema/generate-projects.mjs; source docs/schema-2026-11-14.sql
|
|
2
|
-
export const PROJECTS_SCHEMA_META = {"authority":"docs/schema-2026-11-14.sql","authoritySha256":"91590bcaaeb9612f800a39297d4effe9ddcb419dbb822de1a3eb268be6969ec7","tableNames":["project_phase","project_phase_target","project_phase_check","project_review","project_review_metric","activity_daily","member_version_skill"]} as const
|
|
3
|
-
export const PROJECTS_SCHEMA_SQL = "CREATE TABLE member_version_skill ( -- [P2] 技能绑定在不可变的配置版本上\n member_version_id TEXT NOT NULL REFERENCES member_version(id),\n skill_id TEXT NOT NULL REFERENCES kb_skill(id),\n skill_version_id TEXT NOT NULL REFERENCES kb_skill_version(id),\n PRIMARY KEY (member_version_id, skill_id)\n);\n\nCREATE TABLE project_phase ( -- [P2]\n id TEXT PRIMARY KEY,\n project_id TEXT NOT NULL REFERENCES project(id),\n seq INTEGER NOT NULL,\n name TEXT NOT NULL,\n starts_at INTEGER NOT NULL,\n ends_at INTEGER NOT NULL,\n status TEXT NOT NULL DEFAULT 'planned'\n CHECK (status IN ('planned','active','awaiting_confirm','done')),\n confirmer_id TEXT REFERENCES member(id),\n confirmed_by TEXT REFERENCES member(id),\n confirmed_at INTEGER,\n UNIQUE (project_id, seq),\n CHECK ((status = 'done') = (confirmed_by IS NOT NULL))\n);\n\nCREATE TABLE project_phase_target ( -- [P2]\n id TEXT PRIMARY KEY,\n phase_id TEXT NOT NULL REFERENCES project_phase(id),\n metric_id TEXT REFERENCES project_metric(id),\n description TEXT NOT NULL,\n target_value REAL,\n verifier_human_id TEXT NOT NULL REFERENCES member(id)\n);\n\nCREATE TABLE project_phase_check ( -- [P2] 勾选只是记录\n id TEXT PRIMARY KEY,\n phase_id TEXT NOT NULL REFERENCES project_phase(id),\n label TEXT NOT NULL,\n checked INTEGER NOT NULL DEFAULT 0,\n checked_by TEXT REFERENCES member(id),\n checked_at INTEGER\n);\n\nCREATE TABLE project_review ( -- [P2]\n id TEXT PRIMARY KEY,\n project_id TEXT NOT NULL REFERENCES project(id),\n kind TEXT NOT NULL CHECK (kind IN ('weekly','monthly','quarterly','phase')),\n period_start INTEGER NOT NULL,\n period_end INTEGER NOT NULL,\n target_version_id TEXT NOT NULL REFERENCES project_target_version(id),\n status TEXT NOT NULL DEFAULT 'draft'\n CHECK (status IN ('draft','awaiting_confirm','confirmed')),\n conclusion_text TEXT,\n confirmer_id TEXT REFERENCES member(id),\n confirmed_by TEXT REFERENCES member(id),\n confirmed_at INTEGER,\n UNIQUE (project_id, kind, period_start),\n CHECK ((status = 'confirmed') = (confirmed_by IS NOT NULL))\n);\n\nCREATE TABLE project_review_metric ( -- [P2] 复盘冻结快照;缺数只能是 unknown\n review_id TEXT NOT NULL REFERENCES project_review(id),\n metric_id TEXT NOT NULL REFERENCES project_metric(id),\n target_value REAL,\n actual_value REAL,\n verdict TEXT NOT NULL CHECK (verdict IN ('met','missed','unknown')),\n evidence_ref TEXT,\n PRIMARY KEY (review_id, metric_id),\n CHECK (actual_value IS NOT NULL OR verdict = 'unknown')\n);\n\nCREATE TABLE activity_daily ( -- [P2] 热力图日聚合,只由 rollup 作业幂等重算\n member_id TEXT NOT NULL REFERENCES member(id),\n date TEXT NOT NULL,\n runs INTEGER NOT NULL DEFAULT 0,\n succeeded INTEGER NOT NULL DEFAULT 0,\n failed INTEGER NOT NULL DEFAULT 0,\n PRIMARY KEY (member_id, date)\n);\n\nCREATE TRIGGER trg_review_metric_unknown BEFORE INSERT ON project_review_metric\nWHEN NEW.verdict <> 'unknown' AND EXISTS (\n SELECT 1 FROM project_metric m WHERE m.id = NEW.metric_id\n AND (m.calculator_key IS NULL OR m.source_kind IS NULL))\nBEGIN SELECT RAISE(ABORT, 'DATA_STALE: metric without calculator or source can only be unknown'); END;\n"
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
// 手工编写的增量迁移(v4):项目群的多 bot 协作。
|
|
2
|
-
//
|
|
3
|
-
// 1. `member.scope_project_id`:临时 bot 只属于一个项目群,成员列表默认不显示;
|
|
4
|
-
// 2. `project_participant`:手动拉进群 / 自动创建的临时 bot,移除用软删(removed_at);
|
|
5
|
-
// 3. `project_message`:群聊合并记录。每个参与者仍各有自己的宿主会话,
|
|
6
|
-
// 这里只存「谁在什么时候说了什么」,不把整份会话日志抄一遍;
|
|
7
|
-
// 4. `chat_cursor`:每个会话已合并到哪条事件 seq,避免重复。
|
|
8
|
-
export const TEAM_SCHEMA_META = {
|
|
9
|
-
tableNames: ['project_participant', 'project_message', 'chat_cursor'],
|
|
10
|
-
note: 'v4 项目群团队:participants / merged messages / session cursor',
|
|
11
|
-
} as const
|
|
12
|
-
|
|
13
|
-
export const TEAM_SCHEMA_SQL = `-- [P4] 临时 bot 的归属项目;NULL = 常规成员
|
|
14
|
-
ALTER TABLE member ADD COLUMN scope_project_id TEXT;
|
|
15
|
-
|
|
16
|
-
CREATE TABLE project_participant ( -- [P4] 项目群成员(不包含推进 bot,它在 project.coordinator_id)
|
|
17
|
-
project_id TEXT NOT NULL REFERENCES project(id),
|
|
18
|
-
member_id TEXT NOT NULL REFERENCES member(id),
|
|
19
|
-
kind TEXT NOT NULL CHECK (kind IN ('member','temp')),
|
|
20
|
-
brief TEXT NOT NULL DEFAULT '',
|
|
21
|
-
added_by TEXT NOT NULL REFERENCES member(id),
|
|
22
|
-
added_at INTEGER NOT NULL,
|
|
23
|
-
removed_by TEXT REFERENCES member(id),
|
|
24
|
-
removed_at INTEGER,
|
|
25
|
-
PRIMARY KEY (project_id, member_id)
|
|
26
|
-
);
|
|
27
|
-
|
|
28
|
-
CREATE INDEX idx_project_participant_active ON project_participant(project_id) WHERE removed_at IS NULL;
|
|
29
|
-
|
|
30
|
-
CREATE TABLE project_message ( -- [P4] 群聊合并记录
|
|
31
|
-
id TEXT PRIMARY KEY,
|
|
32
|
-
project_id TEXT NOT NULL REFERENCES project(id),
|
|
33
|
-
author_kind TEXT NOT NULL CHECK (author_kind IN ('human','bot','system')),
|
|
34
|
-
author_id TEXT NOT NULL,
|
|
35
|
-
author_name TEXT NOT NULL,
|
|
36
|
-
text TEXT NOT NULL,
|
|
37
|
-
at INTEGER NOT NULL,
|
|
38
|
-
kind TEXT NOT NULL DEFAULT 'chat' CHECK (kind IN ('chat','mention','system')),
|
|
39
|
-
to_member_id TEXT,
|
|
40
|
-
session_id TEXT
|
|
41
|
-
);
|
|
42
|
-
|
|
43
|
-
CREATE INDEX idx_project_message_project ON project_message(project_id, at, id);
|
|
44
|
-
|
|
45
|
-
CREATE TABLE chat_cursor ( -- [P4] 会话事件已合并到哪一条
|
|
46
|
-
session_id TEXT PRIMARY KEY,
|
|
47
|
-
project_id TEXT NOT NULL REFERENCES project(id),
|
|
48
|
-
last_seq INTEGER NOT NULL DEFAULT 0
|
|
49
|
-
);
|
|
50
|
-
`
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
// 增量迁移(v5):阶段目标的「提交检验」记录。
|
|
2
|
-
//
|
|
3
|
-
// 一个阶段目标可以被反复检验(项目经理提交),每次留下:谁、什么时候、结果、报告链接、说明。
|
|
4
|
-
// 原来的 project_phase_check 只是勾选清单,没有提交人与报告,无法承载这条业务记录。
|
|
5
|
-
export const TARGET_CHECK_SCHEMA_META = {
|
|
6
|
-
tableNames: ['project_phase_target_check'],
|
|
7
|
-
note: 'v5 阶段目标检验记录(submitted_by / result / report_url)',
|
|
8
|
-
} as const
|
|
9
|
-
|
|
10
|
-
export const TARGET_CHECK_SCHEMA_SQL = `CREATE TABLE project_phase_target_check ( -- [P5] 阶段目标的检验提交
|
|
11
|
-
id TEXT PRIMARY KEY,
|
|
12
|
-
project_id TEXT NOT NULL REFERENCES project(id),
|
|
13
|
-
target_id TEXT NOT NULL REFERENCES project_phase_target(id),
|
|
14
|
-
submitted_by TEXT NOT NULL REFERENCES member(id),
|
|
15
|
-
submitted_at INTEGER NOT NULL,
|
|
16
|
-
result TEXT NOT NULL CHECK (result IN ('met','missed','unknown')),
|
|
17
|
-
report_url TEXT,
|
|
18
|
-
note TEXT NOT NULL DEFAULT ''
|
|
19
|
-
);
|
|
20
|
-
|
|
21
|
-
CREATE INDEX idx_phase_target_check_target ON project_phase_target_check(target_id, submitted_at);
|
|
22
|
-
`
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
// 增量迁移(v6):阶段目标的「说明 / 检验提交与审批」字段。
|
|
2
|
-
//
|
|
3
|
-
// - `project_phase_target.detail`:目标详情(计算方式与验收要求);
|
|
4
|
-
// - `project_phase_target_check`:补上审批人、状态、审批决定与摘要。提交检验 = 申请审批,
|
|
5
|
-
// 审批通过后目标才算达成(审批本身复用 approval 表,subject_kind = 'target_change')。
|
|
6
|
-
export const TARGET_FLOW_SCHEMA_META = {
|
|
7
|
-
tableNames: [],
|
|
8
|
-
note: 'v6 阶段目标说明 + 检验提交的审批字段(ALTER TABLE)',
|
|
9
|
-
} as const
|
|
10
|
-
|
|
11
|
-
export const TARGET_FLOW_SCHEMA_SQL = `ALTER TABLE project_phase_target ADD COLUMN detail TEXT NOT NULL DEFAULT '';
|
|
12
|
-
|
|
13
|
-
ALTER TABLE project_phase_target_check ADD COLUMN approver_id TEXT REFERENCES member(id);
|
|
14
|
-
ALTER TABLE project_phase_target_check ADD COLUMN summary TEXT NOT NULL DEFAULT '';
|
|
15
|
-
ALTER TABLE project_phase_target_check ADD COLUMN status TEXT NOT NULL DEFAULT 'pending';
|
|
16
|
-
ALTER TABLE project_phase_target_check ADD COLUMN decided_by TEXT REFERENCES member(id);
|
|
17
|
-
ALTER TABLE project_phase_target_check ADD COLUMN decided_at INTEGER;
|
|
18
|
-
ALTER TABLE project_phase_target_check ADD COLUMN decision_note TEXT NOT NULL DEFAULT '';
|
|
19
|
-
`
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
// 手工编写的增量迁移(v7):归档项目可以「删除」。
|
|
2
|
-
//
|
|
3
|
-
// 设计取舍:这里不是物理删除。HAMS 的证据和审计是追加式的,硬删会绕开
|
|
4
|
-
// `trg_evidence_no_delete` 与审计留痕;因此「删除」= 在项目上打 removed 标记,
|
|
5
|
-
// 列表/详情/群聊不再出现,同时把该项目的工作项解绑为「无项目」继续留在任务里。
|
|
6
|
-
// 需要物理清库时另行走运维工具,不在界面里提供。
|
|
7
|
-
export const PROJECT_DELETE_SCHEMA_META = {
|
|
8
|
-
tableNames: [] as const,
|
|
9
|
-
alteredTables: ['project'] as const,
|
|
10
|
-
note: 'v7 项目软删除:project.deleted_at / deleted_by;工作项解绑保留',
|
|
11
|
-
} as const
|
|
12
|
-
|
|
13
|
-
export const PROJECT_DELETE_SCHEMA_SQL = `ALTER TABLE project ADD COLUMN deleted_at INTEGER;
|
|
14
|
-
ALTER TABLE project ADD COLUMN deleted_by TEXT REFERENCES member(id);
|
|
15
|
-
-- 项目名有 UNIQUE 约束:软删时把名字改成墓碑,原值留在这里,名字可复用。
|
|
16
|
-
ALTER TABLE project ADD COLUMN deleted_name TEXT;
|
|
17
|
-
`
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
// 手工编写的增量迁移(v8):项目记忆 + bot 记忆。
|
|
2
|
-
//
|
|
3
|
-
// 项目记忆是「推进过程中值得留下来的话」:决策、偏好、教训、事实,以及 bot 能力类
|
|
4
|
-
// 经验。它和知识库(kb_doc)不同:知识库是成篇文档,记忆是短句 + 来源,默认按项目
|
|
5
|
-
// 隔离;能力类记忆会镜像一份到 `bot_memory`,在对应 bot 的会话上下文里注入。
|
|
6
|
-
//
|
|
7
|
-
// 只追加、可归档;不删外键数据,项目软删时记忆随项目一起隐藏。
|
|
8
|
-
export const MEMORY_SCHEMA_META = {
|
|
9
|
-
tableNames: ['project_memory', 'bot_memory'] as const,
|
|
10
|
-
alteredTables: [] as const,
|
|
11
|
-
note: 'v8 项目记忆:project_memory + bot 镜像 bot_memory',
|
|
12
|
-
} as const
|
|
13
|
-
|
|
14
|
-
export const MEMORY_SCHEMA_SQL = `CREATE TABLE project_memory ( -- [P4] 项目推进过程中收集的记忆
|
|
15
|
-
id TEXT PRIMARY KEY,
|
|
16
|
-
project_id TEXT NOT NULL REFERENCES project(id),
|
|
17
|
-
kind TEXT NOT NULL CHECK (kind IN ('fact','decision','preference','lesson','capability')),
|
|
18
|
-
title TEXT NOT NULL,
|
|
19
|
-
body TEXT NOT NULL,
|
|
20
|
-
origin TEXT NOT NULL CHECK (origin IN ('auto','manual','tool')),
|
|
21
|
-
status TEXT NOT NULL DEFAULT 'active' CHECK (status IN ('active','archived')),
|
|
22
|
-
member_id TEXT REFERENCES member(id), -- 能力类记忆归属的 bot;其它类型为 NULL
|
|
23
|
-
source_ref TEXT, -- message:<id> / manual / tool
|
|
24
|
-
dedupe_key TEXT NOT NULL,
|
|
25
|
-
created_by TEXT NOT NULL REFERENCES member(id),
|
|
26
|
-
created_at INTEGER NOT NULL,
|
|
27
|
-
updated_at INTEGER NOT NULL,
|
|
28
|
-
revision INTEGER NOT NULL DEFAULT 1,
|
|
29
|
-
UNIQUE (project_id, dedupe_key)
|
|
30
|
-
);
|
|
31
|
-
|
|
32
|
-
CREATE INDEX idx_project_memory_project ON project_memory(project_id, status, created_at);
|
|
33
|
-
|
|
34
|
-
CREATE TABLE bot_memory ( -- [P4] 能力类记忆同步给 bot 的快照
|
|
35
|
-
id TEXT PRIMARY KEY,
|
|
36
|
-
member_id TEXT NOT NULL REFERENCES member(id),
|
|
37
|
-
project_id TEXT NOT NULL REFERENCES project(id),
|
|
38
|
-
memory_id TEXT NOT NULL REFERENCES project_memory(id),
|
|
39
|
-
kind TEXT NOT NULL,
|
|
40
|
-
title TEXT NOT NULL,
|
|
41
|
-
body TEXT NOT NULL,
|
|
42
|
-
created_at INTEGER NOT NULL,
|
|
43
|
-
UNIQUE (member_id, memory_id)
|
|
44
|
-
);
|
|
45
|
-
|
|
46
|
-
CREATE INDEX idx_bot_memory_member ON bot_memory(member_id, created_at);
|
|
47
|
-
`
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
// Generate migration 3 from the authoritative DDL; no hand-copied table definitions.
|
|
2
|
-
// Run: node src/store/schema/generate-projects.mjs [--check]
|
|
3
|
-
import { readFileSync, writeFileSync } from 'node:fs'
|
|
4
|
-
import { createHash } from 'node:crypto'
|
|
5
|
-
import { parseSchema } from '../../../scripts/split-schema.mjs'
|
|
6
|
-
const authority = new URL('../../../docs/schema-2026-11-14.sql', import.meta.url)
|
|
7
|
-
const text = readFileSync(authority, 'utf8')
|
|
8
|
-
const tables = ['project_phase', 'project_phase_target', 'project_phase_check', 'project_review', 'project_review_metric', 'activity_daily', 'member_version_skill']
|
|
9
|
-
const statements = parseSchema(text).filter(s => tables.some(t => new RegExp(`^(?:CREATE TABLE ${t}\\b|CREATE (?:UNIQUE )?INDEX \\w+ ON ${t}\\b|CREATE TRIGGER \\w+[\\s\\S]*?\\bON ${t}\\b)`).test(s.text)))
|
|
10
|
-
const sql = statements.map(s => s.text).join('\n\n') + '\n'
|
|
11
|
-
const sha = createHash('sha256').update(text).digest('hex')
|
|
12
|
-
const content = `// Generated by node src/store/schema/generate-projects.mjs; source docs/schema-2026-11-14.sql\nexport const PROJECTS_SCHEMA_META = ${JSON.stringify({authority: 'docs/schema-2026-11-14.sql', authoritySha256: sha, tableNames: tables})} as const\nexport const PROJECTS_SCHEMA_SQL = ${JSON.stringify(sql)}\n`
|
|
13
|
-
const target = new URL('./003_projects.sql.ts', import.meta.url)
|
|
14
|
-
if (process.argv.includes('--check')) {
|
|
15
|
-
if (readFileSync(target, 'utf8') !== content) throw new Error('Migration 3 differs from authoritative DDL')
|
|
16
|
-
} else writeFileSync(target, content)
|
package/src/store/seed.ts
DELETED
|
@@ -1,114 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 种子与元数据。
|
|
3
|
-
*
|
|
4
|
-
* v1 的 DDL 已经带了两条基线种子:唯一的系统主体(member.id='system')与 36 条状态转换
|
|
5
|
-
* 白名单。本文件负责其余「必须有、但可以缺省补写」的元数据,原则只有一条:
|
|
6
|
-
* **只补缺失,不覆盖已有值**——重启不能把 runtime_epoch 或工作区指针重置。
|
|
7
|
-
*
|
|
8
|
-
* 岗位能力矩阵属于业务待确认项(status §5),不在这里造数据。
|
|
9
|
-
*/
|
|
10
|
-
import type { DatabaseSync } from 'node:sqlite'
|
|
11
|
-
import { transaction, userVersion, StoreError } from './db.ts'
|
|
12
|
-
|
|
13
|
-
export const META_KEYS = [
|
|
14
|
-
'schema_version',
|
|
15
|
-
'runtime_epoch',
|
|
16
|
-
'primary_workspace_id',
|
|
17
|
-
'last_tick_at',
|
|
18
|
-
'bootstrapped_at',
|
|
19
|
-
] as const
|
|
20
|
-
|
|
21
|
-
export type MetaKey = (typeof META_KEYS)[number]
|
|
22
|
-
|
|
23
|
-
export class SeedError extends StoreError {
|
|
24
|
-
constructor(message: string, code = 'SEED_INVALID') {
|
|
25
|
-
super(code, message)
|
|
26
|
-
this.name = 'SeedError'
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export function readMeta(db: DatabaseSync): Record<string, string> {
|
|
31
|
-
const rows = db.prepare('SELECT key, value FROM hams_meta').all() as { key: string; value: string }[]
|
|
32
|
-
const meta: Record<string, string> = {}
|
|
33
|
-
for (const row of rows) meta[row.key] = row.value
|
|
34
|
-
return meta
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
export interface SeedOptions {
|
|
38
|
-
now?: number
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* 补齐 hams_meta 的缺省键(幂等)。schema_version 永远与 PRAGMA user_version 对齐。
|
|
43
|
-
*/
|
|
44
|
-
export function ensureMetaDefaults(db: DatabaseSync, options: SeedOptions = {}): Record<string, string> {
|
|
45
|
-
const now = options.now ?? Date.now()
|
|
46
|
-
const defaults: Record<MetaKey, string> = {
|
|
47
|
-
schema_version: String(userVersion(db)),
|
|
48
|
-
runtime_epoch: '0',
|
|
49
|
-
primary_workspace_id: '',
|
|
50
|
-
last_tick_at: '0',
|
|
51
|
-
bootstrapped_at: String(now),
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
return transaction(db, () => {
|
|
55
|
-
const insert = db.prepare('INSERT OR IGNORE INTO hams_meta (key, value) VALUES (?, ?)')
|
|
56
|
-
for (const key of META_KEYS) insert.run(key, defaults[key])
|
|
57
|
-
const update = db.prepare('UPDATE hams_meta SET value = ? WHERE key = ?')
|
|
58
|
-
update.run(String(userVersion(db)), 'schema_version')
|
|
59
|
-
return readMeta(db)
|
|
60
|
-
})
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/** 宿主启动代次 +1:与业务代次无关,只用于识别上个进程留下的运行。 */
|
|
64
|
-
export function bumpRuntimeEpoch(db: DatabaseSync): number {
|
|
65
|
-
return transaction(db, () => {
|
|
66
|
-
const row = db.prepare("SELECT value FROM hams_meta WHERE key = 'runtime_epoch'").get() as
|
|
67
|
-
| { value: string }
|
|
68
|
-
| undefined
|
|
69
|
-
const current = Number(row?.value ?? 0)
|
|
70
|
-
const next = (Number.isFinite(current) && current >= 0 ? current : 0) + 1
|
|
71
|
-
db.prepare(
|
|
72
|
-
`INSERT INTO hams_meta (key, value) VALUES ('runtime_epoch', ?)
|
|
73
|
-
ON CONFLICT(key) DO UPDATE SET value = excluded.value`,
|
|
74
|
-
).run(String(next))
|
|
75
|
-
return next
|
|
76
|
-
})
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
export function readRuntimeEpoch(db: DatabaseSync): number {
|
|
80
|
-
const row = db.prepare("SELECT value FROM hams_meta WHERE key = 'runtime_epoch'").get() as
|
|
81
|
-
| { value: string }
|
|
82
|
-
| undefined
|
|
83
|
-
return Number(row?.value ?? 0)
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
export interface SeedSummary {
|
|
87
|
-
systemMember: boolean
|
|
88
|
-
transitions: number
|
|
89
|
-
tables: number
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
/**
|
|
93
|
-
* 基线种子核对:系统主体必须唯一存在,状态白名单必须正好 36 条。
|
|
94
|
-
* 迁移完成后立刻调用;不满足说明 DDL 被改坏,拒绝接流量。
|
|
95
|
-
*/
|
|
96
|
-
export function assertBaselineSeed(db: DatabaseSync): SeedSummary {
|
|
97
|
-
const system = db
|
|
98
|
-
.prepare("SELECT COUNT(*) AS c FROM member WHERE kind = 'system' AND id = 'system'")
|
|
99
|
-
.get() as { c: number }
|
|
100
|
-
const transitions = (db.prepare('SELECT COUNT(*) AS c FROM work_item_transition').get() as { c: number }).c
|
|
101
|
-
const tables = (
|
|
102
|
-
db
|
|
103
|
-
.prepare("SELECT COUNT(*) AS c FROM sqlite_master WHERE type = 'table' AND name NOT LIKE 'sqlite_%'")
|
|
104
|
-
.get() as { c: number }
|
|
105
|
-
).c
|
|
106
|
-
|
|
107
|
-
if (Number(system.c) !== 1) {
|
|
108
|
-
throw new SeedError(`系统主体应唯一存在一行,实际 ${system.c} 行;拒绝接流量`)
|
|
109
|
-
}
|
|
110
|
-
if (Number(transitions) !== 36) {
|
|
111
|
-
throw new SeedError(`状态转换白名单应有 36 条,实际 ${transitions} 条;拒绝接流量`)
|
|
112
|
-
}
|
|
113
|
-
return { systemMember: true, transitions: Number(transitions), tables: Number(tables) }
|
|
114
|
-
}
|