feat(trace): add the project trace ledger and bounded impact walk

Keep item revisions and edge decisions as records, and refuse to treat a missing index as no impact.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
weichen
2026-09-20 15:35:07 +00:00
co-authored by Cursor
parent e3a5b2851e
commit 33ee59641f
11 changed files with 1808 additions and 0 deletions
@@ -0,0 +1,108 @@
-- +goose Up
-- 项目追踪与阶段回溯账本。留存类别为 record:随项目保留。
-- 条目修订与边决议只追加。源文档依法清除时把条目标成 tombstone,不级联删边和决议。
CREATE TABLE IF NOT EXISTS project_trace_items (
id BIGSERIAL PRIMARY KEY,
project_id BIGINT NOT NULL,
business_key TEXT NOT NULL,
kind TEXT NOT NULL,
lifecycle_state TEXT NOT NULL DEFAULT 'active',
review_state TEXT NOT NULL DEFAULT 'confirmed',
tombstone BOOLEAN NOT NULL DEFAULT false,
statement TEXT NOT NULL DEFAULT '',
source_doc_id TEXT NOT NULL DEFAULT '',
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
UNIQUE (project_id, kind, business_key)
);
CREATE TABLE IF NOT EXISTS project_trace_item_revisions (
id BIGSERIAL PRIMARY KEY,
item_id BIGINT NOT NULL REFERENCES project_trace_items (id),
revision_no INT NOT NULL,
source_doc_id TEXT NOT NULL DEFAULT '',
source_snapshot_id BIGINT NOT NULL DEFAULT 0,
content_hash TEXT NOT NULL DEFAULT '',
statement TEXT NOT NULL DEFAULT '',
extractor_version TEXT NOT NULL DEFAULT '',
tombstone BOOLEAN NOT NULL DEFAULT false,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
UNIQUE (item_id, revision_no),
UNIQUE (item_id, source_snapshot_id, content_hash, extractor_version)
);
CREATE TABLE IF NOT EXISTS project_trace_edges (
id BIGSERIAL PRIMARY KEY,
project_id BIGINT NOT NULL,
from_item_id BIGINT NOT NULL,
to_item_id BIGINT NOT NULL,
relation_type TEXT NOT NULL,
provenance TEXT NOT NULL,
review_state TEXT NOT NULL,
evidence_doc_id TEXT NOT NULL DEFAULT '',
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
UNIQUE (project_id, from_item_id, to_item_id, relation_type, provenance)
);
CREATE TABLE IF NOT EXISTS project_trace_edge_decisions (
id BIGSERIAL PRIMARY KEY,
project_id BIGINT NOT NULL,
edge_id BIGINT NOT NULL,
decision TEXT NOT NULL,
reason TEXT NOT NULL DEFAULT '',
decided_by BIGINT NOT NULL,
decided_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE TABLE IF NOT EXISTS project_trace_index_state (
project_id BIGINT NOT NULL,
doc_id TEXT NOT NULL,
source_snapshot_id BIGINT NOT NULL DEFAULT 0,
status TEXT NOT NULL,
extractor_version TEXT NOT NULL,
item_count INT NOT NULL DEFAULT 0,
error TEXT NOT NULL DEFAULT '',
indexed_at TIMESTAMPTZ NOT NULL DEFAULT now(),
PRIMARY KEY (project_id, doc_id, extractor_version)
);
CREATE TABLE IF NOT EXISTS stage_reopen_requests (
id BIGSERIAL PRIMARY KEY,
project_id BIGINT NOT NULL,
instance_id BIGINT NOT NULL,
stage_key TEXT NOT NULL,
doc_task_keys TEXT[] NOT NULL,
reason TEXT NOT NULL,
requested_by BIGINT NOT NULL,
status TEXT NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE TABLE IF NOT EXISTS stage_recheck_marks (
id BIGSERIAL PRIMARY KEY,
project_id BIGINT NOT NULL,
stage_key TEXT NOT NULL,
source_doc_id TEXT NOT NULL,
status TEXT NOT NULL,
reason TEXT NOT NULL DEFAULT '',
acked_by BIGINT,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE UNIQUE INDEX IF NOT EXISTS stage_recheck_marks_one_open
ON stage_recheck_marks (project_id, stage_key, source_doc_id)
WHERE status = 'open';
CREATE TABLE IF NOT EXISTS trace_revision_proposals (
id BIGSERIAL PRIMARY KEY,
project_id BIGINT NOT NULL,
author_id BIGINT NOT NULL,
body TEXT NOT NULL,
status TEXT NOT NULL DEFAULT 'pending_author',
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
-- +goose Down
-- 这些表是合规业务记录,不是可丢弃缓存。
-- Down 不删除 project_trace_edge_decisions,也不删除条目、修订、边、索引水位、再打开申请和待复核标记。
SELECT 1;