Why: PR #209 is behind main by 14 commits (admin passwords, managed
interpreter path, U8 SQLite instance guard, EXT-20 regulations) and conflicted
in app wiring, host capabilities, migrations and the extension surface; the
user authorized merging the renderer line on 2026-10-18.
Evidence: content is the agent-exec transport 87a99ece (cursor worker, unit
backend-task10-sync-main-56a7f9f-r2, receipt
/tmp/cleanup-20261018/render/backend-sync-run2.json, SELF): gofmt, go build,
go vet and go test ./... (43 packages) passed. Host check: main migrations
1-36 unchanged, feature migrations moved from 36-47 to 37-48 in order with
identical SQL, surface.txt is the sorted union.
Impact: main's migration 36 instance_guard stays authoritative; databases that
ran unmerged feature builds with the old numbering must be recreated.
SurfaceVersion becomes 2.24.0-experimental.
Refs: PR #209, Backend main 56a7f9f
Co-authored-by: Cursor <cursoragent@cursor.com>
120 lines
5.0 KiB
Go
120 lines
5.0 KiB
Go
package store
|
||
|
||
import "fmt"
|
||
|
||
// migrationBreaksOldReaders 登记每个迁移版本落地后,schema.target 更低的旧制品
|
||
// 还能不能读这个库。新增迁移漏登记时 TestMigrationCompatRegistered 失败。
|
||
// true 会抬高 SchemaCompatFloor:候选若要把库迁过该版本,而旧制品的 target
|
||
// 低于该下限,切换必须在停旧服务前拒绝,除非走已确认的 forward-only 迁移。
|
||
var migrationBreaksOldReaders = map[int]bool{
|
||
1: false,
|
||
2: false,
|
||
3: false,
|
||
4: false,
|
||
5: false,
|
||
6: false,
|
||
7: false,
|
||
8: false,
|
||
9: false,
|
||
10: false,
|
||
11: false,
|
||
12: false,
|
||
13: false,
|
||
14: false,
|
||
15: false,
|
||
16: false,
|
||
17: false,
|
||
18: false,
|
||
19: false,
|
||
20: false,
|
||
21: false,
|
||
22: false,
|
||
23: false,
|
||
24: false,
|
||
25: false,
|
||
26: false,
|
||
27: false,
|
||
28: false, // auth_version_account_events:仅加列与新表,旧读者不受影响
|
||
29: false, // redeem_attempts:新表,旧读者不受影响
|
||
30: false, // project_event_publication_proofs:新表,旧读者不受影响
|
||
31: false, // account_audit 给 audit_events 增加带默认值的列,旧制品按列名读取
|
||
32: true, // 账号有效态/凭据来源是安全协议边界;旧认证写者必须排除
|
||
33: true, // 本人证明与 grant 绑定协议;旧恢复写者和兑换者必须排除
|
||
34: false, // 游标签名密钥只新增内部列;旧读者按列名读取既有字段
|
||
35: false, // exec_env 新表;旧读者不读这些表
|
||
36: false, // instance_guard 是新表,旧制品不读取它
|
||
37: false, // work_task_bindings:新增绑定表,旧后台任务读面不受影响
|
||
38: false, // trusted_context_manifests:独立可信回执表,旧读者不受影响
|
||
39: false, // workflow_step_sources:新增逐步骤来源绑定表,旧读者不受影响
|
||
40: false, // context_manifest_resolve_scope:可信回执增加范围标记,旧读者不受影响
|
||
41: false, // scrub_candidate_context_scope_text:候选原文列清理,旧读者不受影响
|
||
42: false, // command_audit_attribution_snapshot:仅加列/索引,旧读者不受影响
|
||
43: false, // project_audit_committed_links:新表,旧读者不受影响
|
||
44: false, // project_audit_committed_prefix:PG 审计 ID 默认函数,旧读者兼容
|
||
45: false, // project_audit_all_writers_guard:PG 在线审计写入门禁,旧读者兼容
|
||
46: false, // project_work_observation_outbox:独立私域投递表,旧读者不读取
|
||
47: false, // human_decision_proofs:独立真人确认表,旧读者不读取
|
||
48: false, // document_render_deliveries:新增普通工作区文档交付回执表
|
||
}
|
||
|
||
// SchemaTarget 是本二进制会迁移到的 schema 版本。
|
||
func SchemaTarget() int { return latestMigrationVersion() }
|
||
|
||
// MigrationName lets the offline copier reject an unpublished branch schema
|
||
// that reused a numeric version for a different migration before copying rows.
|
||
func MigrationName(version int) (string, bool) {
|
||
for _, migration := range migrations {
|
||
if migration.version == version {
|
||
return migration.name, true
|
||
}
|
||
}
|
||
return "", false
|
||
}
|
||
|
||
// legacyFeatureMigrationNames records the unpublished project-context/audit
|
||
// candidate numbering that collided with the adopted v35 exec_env migration.
|
||
// These identities are intentionally not aliases: accepting them would mark a
|
||
// different schema as applied. Operators must first move that candidate through
|
||
// an explicit, reviewed compatibility procedure.
|
||
var legacyFeatureMigrationNames = map[int]string{
|
||
35: "work_task_bindings",
|
||
36: "trusted_context_manifests",
|
||
37: "workflow_step_sources",
|
||
38: "context_manifest_resolve_scope",
|
||
39: "scrub_candidate_context_scope_text",
|
||
40: "command_audit_attribution_snapshot",
|
||
41: "project_audit_committed_links",
|
||
42: "project_audit_committed_prefix",
|
||
43: "project_audit_all_writers_guard",
|
||
44: "project_work_observation_outbox",
|
||
45: "human_decision_proofs",
|
||
}
|
||
|
||
// ValidateMigrationIdentity rejects any ledger row whose numeric version names
|
||
// a different migration. Known unpublished feature identities get a specific
|
||
// fail-closed error so they cannot be mistaken for the adopted exec_env history.
|
||
func ValidateMigrationIdentity(version int, actual string) error {
|
||
expected, known := MigrationName(version)
|
||
if !known {
|
||
return fmt.Errorf("migration identity mismatch at v%d; refuse to reinterpret another schema", version)
|
||
}
|
||
if actual == expected {
|
||
return nil
|
||
}
|
||
if legacyFeatureMigrationNames[version] == actual {
|
||
return fmt.Errorf("migration identity mismatch at v%d: legacy feature migration numbering %q conflicts with adopted history %q; refuse automatic reinterpretation", version, actual, expected)
|
||
}
|
||
return fmt.Errorf("migration identity mismatch at v%d; refuse to reinterpret another schema", version)
|
||
}
|
||
|
||
// SchemaCompatFloor 是旧制品仍能读取的最高破坏性版本。0 表示当前全部迁移都不破坏旧读。
|
||
func SchemaCompatFloor() int {
|
||
floor := 0
|
||
for version, breaks := range migrationBreaksOldReaders {
|
||
if breaks && version > floor {
|
||
floor = version
|
||
}
|
||
}
|
||
return floor
|
||
}
|