chore: align sqlite control plane architecture
- replace default Convex control-plane wording with Rust SQLite control-plane across architecture, AGENTS, Reasonix, and design docs - retire root Convex functions source and deploy script into recycle while keeping explicit cloud/compat/sync-replica boundaries - add control-plane migration guard/docs and keep CodeGraph refreshed after the SQLite control-plane cutover
This commit is contained in:
@@ -6,6 +6,7 @@ const fs = require("node:fs");
|
||||
const path = require("node:path");
|
||||
|
||||
const WAIVER = "local-first-allow-convex-main-storage";
|
||||
const MIGRATION_PERIOD_WAIVER = "sqlite-migration-waiver";
|
||||
|
||||
const RUNTIME_FORBIDDEN = [
|
||||
{
|
||||
@@ -49,6 +50,67 @@ const ACTIVE_CONVEX_FORBIDDEN = [
|
||||
},
|
||||
];
|
||||
|
||||
const RETIRED_ROOT_CONVEX_PATHS = [
|
||||
{
|
||||
name: "根 convex schema",
|
||||
pattern: /^convex\/schema\.ts$/,
|
||||
},
|
||||
{
|
||||
name: "根 Convex Auth 函数",
|
||||
pattern: /^convex\/auth(?:\.config)?\.ts$/,
|
||||
},
|
||||
{
|
||||
name: "根 Convex 用户函数",
|
||||
pattern: /^convex\/users\.ts$/,
|
||||
},
|
||||
{
|
||||
name: "根 Convex AI session 函数",
|
||||
pattern: /^convex\/aiSessions\.ts$/,
|
||||
},
|
||||
];
|
||||
|
||||
// ─── SQLite 迁移期 forbid ───────────────────────────────────
|
||||
// 在 SQLite 控制面迁移阶段(Phase 1-5),新增 Convex 控制面代码
|
||||
// (users/workspaces/sessions/policies/audit/outbox/share 等)
|
||||
// 应标记 sqlite-migration-waiver,否则提示应迁移到 SQLite。
|
||||
const MIGRATION_PERIOD_FORBIDDEN = [
|
||||
{
|
||||
name: "未标注迁移例外的 Convex 控制面 users 代码",
|
||||
pattern: /(?:[Uu]sers?\s*[:=.]|[a-z]+[A-Z][a-z]*[Uu]ser\b|auth_identities|auth_sessions).*(?:defineTable|mutation|query|schema)/,
|
||||
scope: "convex",
|
||||
},
|
||||
{
|
||||
name: "未标注迁移例外的 Convex 控制面 workspace 代码",
|
||||
pattern: /(?:[Ww]orkspaces?\s*[:=]|[Ww]orkspace[Mm]embers\b|[Ww]orkspace[Mm]ember\b).*(?:defineTable|mutation|query|schema)/,
|
||||
scope: "convex",
|
||||
},
|
||||
{
|
||||
name: "未标注迁移例外的 Convex 控制面 grants/access 代码",
|
||||
pattern: /\b(?:directory_grants|directoryGrants|access_policy|accessPolicy)\b/,
|
||||
scope: "convex",
|
||||
},
|
||||
{
|
||||
name: "未标注迁移例外的 Convex 控制面 share 代码",
|
||||
pattern: /\b(?:share_links|shareLinks)\b/,
|
||||
scope: "convex",
|
||||
},
|
||||
{
|
||||
name: "未标注迁移例外的 Convex 控制面 sync/audit/outbox 代码",
|
||||
pattern: /\b(?:sync_state|syncState|audit_log|auditLog|outbox_events|outboxEvents)\b/,
|
||||
scope: "convex",
|
||||
},
|
||||
{
|
||||
name: "未标注迁移例外的 Convex 控制面 ai_policies 代码",
|
||||
pattern: /\b(?:ai_policies|aiPolicies|ai_policy|aiPolicy)\b/,
|
||||
scope: "convex",
|
||||
},
|
||||
{
|
||||
name: "未标注迁移例外的 Convex 控制面 legacy_id_map 代码",
|
||||
pattern: /\b(?:legacy_id_map|legacyIdMap)\b/,
|
||||
scope: "convex",
|
||||
},
|
||||
];
|
||||
|
||||
function usage() {
|
||||
return [
|
||||
"用法:",
|
||||
@@ -59,6 +121,8 @@ function usage() {
|
||||
" node scripts/check-local-first-convex-guard.js --self-test",
|
||||
"",
|
||||
`如需保留明确的 cloud/compat 直连,请在同一新增行加入 ${WAIVER}。`,
|
||||
"根 convex/ functions 源码已退役;如确需新增显式 cloud/compat Convex functions,应先放到明确的辅助目录并更新 guard。",
|
||||
`SQLite 迁移期中新增 Convex 控制面代码(users/workspaces/grants/share 等)需加 ${MIGRATION_PERIOD_WAIVER} 标注迁移例外。`,
|
||||
].join("\n");
|
||||
}
|
||||
|
||||
@@ -111,6 +175,10 @@ function isActiveConvexPath(filePath) {
|
||||
return filePath.startsWith("convex/") && filePath.endsWith(".ts") && !filePath.startsWith("convex/_generated/");
|
||||
}
|
||||
|
||||
function isRetiredRootConvexPath(filePath) {
|
||||
return RETIRED_ROOT_CONVEX_PATHS.some((entry) => entry.pattern.test(filePath));
|
||||
}
|
||||
|
||||
function isAllowedRuntimeAdapter(filePath) {
|
||||
return (
|
||||
filePath === "rust/crates/mnote-web/src/transport/convex.rs" ||
|
||||
@@ -182,8 +250,12 @@ function collectLinesFromFiles(files) {
|
||||
return rows;
|
||||
}
|
||||
|
||||
function isMigrationWaived(row) {
|
||||
return row.text.includes(WAIVER) || row.text.includes(MIGRATION_PERIOD_WAIVER);
|
||||
}
|
||||
|
||||
function checkLine(row) {
|
||||
if (row.text.includes(WAIVER)) {
|
||||
if (isMigrationWaived(row)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
@@ -199,14 +271,32 @@ function checkLine(row) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (isRetiredRootConvexPath(row.filePath)) {
|
||||
return [
|
||||
{
|
||||
...row,
|
||||
reason: `${RETIRED_ROOT_CONVEX_PATHS.find((entry) => entry.pattern.test(row.filePath)).name} 已随根 convex/ 退役;不要重新新增为默认控制面,请迁到 Rust SQLite control-plane 或明确的 cloud/compat 辅助目录。`,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
if (!isRuntimePath(row.filePath) || isAllowedRuntimeAdapter(row.filePath)) {
|
||||
if (!isActiveConvexPath(row.filePath)) {
|
||||
return [];
|
||||
}
|
||||
return ACTIVE_CONVEX_FORBIDDEN.filter((entry) => entry.pattern.test(row.text)).map((entry) => ({
|
||||
const violations = ACTIVE_CONVEX_FORBIDDEN.filter((entry) => entry.pattern.test(row.text)).map((entry) => ({
|
||||
...row,
|
||||
reason: `${entry.name} 出现在 active convex 目录;新增 Convex 代码只能是 auth、membership、share grants、sync state、AI policy、cloud source、compat 或 sync replica,并需用 ${WAIVER} 标注例外。`,
|
||||
}));
|
||||
// 在迁移期内,新增控制面 Convex 代码也应标记例外
|
||||
if (violations.length === 0) {
|
||||
const migrationHit = MIGRATION_PERIOD_FORBIDDEN.filter((entry) => entry.pattern.test(row.text)).map((entry) => ({
|
||||
...row,
|
||||
reason: `${entry.name} — SQLite 迁移期内新增 Convex 控制面代码应标记 ${MIGRATION_PERIOD_WAIVER} 迁移例外,或将逻辑迁移到 Rust control-plane SQLite 实现。`,
|
||||
}));
|
||||
violations.push(...migrationHit);
|
||||
}
|
||||
return violations;
|
||||
}
|
||||
|
||||
return RUNTIME_FORBIDDEN.filter((entry) => entry.pattern.test(row.text)).map((entry) => ({
|
||||
@@ -222,6 +312,11 @@ function runSelfTest() {
|
||||
lineNumber: 1,
|
||||
text: " documents: defineTable({",
|
||||
},
|
||||
{
|
||||
filePath: "convex/aiSessions.ts",
|
||||
lineNumber: 1,
|
||||
text: "export const upsertRuntimeRun = mutation({",
|
||||
},
|
||||
{
|
||||
filePath: "convex/mediaAssets.ts",
|
||||
lineNumber: 1,
|
||||
@@ -242,12 +337,25 @@ function runSelfTest() {
|
||||
lineNumber: 1,
|
||||
text: '"documents:updateContent"',
|
||||
},
|
||||
{
|
||||
filePath: "convex/users.ts",
|
||||
lineNumber: 1,
|
||||
text: " users: defineTable({",
|
||||
},
|
||||
{
|
||||
filePath: "convex/workspaces_migration.ts",
|
||||
lineNumber: 1,
|
||||
text: " workspaces: defineTable({",
|
||||
},
|
||||
];
|
||||
const violations = rows.flatMap(checkLine);
|
||||
const labels = violations.map((item) => item.filePath);
|
||||
if (!labels.includes("convex/schema.ts")) {
|
||||
throw new Error("self-test 失败:未拦截 active convex documents 表");
|
||||
}
|
||||
if (!labels.includes("convex/aiSessions.ts")) {
|
||||
throw new Error("self-test 失败:未拦截退役根 convex AI session 函数");
|
||||
}
|
||||
if (!labels.includes("convex/mediaAssets.ts")) {
|
||||
throw new Error("self-test 失败:未拦截 active convex media 函数");
|
||||
}
|
||||
@@ -260,6 +368,12 @@ function runSelfTest() {
|
||||
if (labels.includes("rust/crates/mnote-web/src/transport/convex.rs")) {
|
||||
throw new Error("self-test 失败:误拦截 Convex adapter");
|
||||
}
|
||||
if (!labels.includes("convex/users.ts")) {
|
||||
throw new Error("self-test 失败:未拦截迁移期新增 Convex 控制面 users 代码");
|
||||
}
|
||||
if (!labels.includes("convex/workspaces_migration.ts")) {
|
||||
throw new Error("self-test 失败:未拦截迁移期新增 Convex 控制面 workspaces 代码");
|
||||
}
|
||||
console.log(JSON.stringify({ ok: true, guard: "local-first-convex", selfTest: true }, null, 2));
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user