fix(scripts): add database-first legacy store guard

Adds a required database-first legacy-store guard and regression coverage for legacy runtime state write patterns.

The guard is wired into architecture/preflight/changed checks, narrows the documented guard contract to the implemented filesystem-write scope, and tightens extension migration exemptions to explicit owner APIs. Also includes a small memory-core lint unblocker after current CI flagged an unnecessary non-null assertion.

Verification:
- pnpm check:database-first-legacy-stores
- pnpm lint:scripts
- node scripts/run-vitest.mjs test/scripts/check-database-first-legacy-stores.test.ts -- --reporter=verbose
- node scripts/run-oxlint.mjs extensions/memory-core/src/memory/manager-embedding-ops.ts
- git diff --check
- .agents/skills/autoreview/scripts/autoreview --mode branch --base origin/main
- GitHub CI green for PR head 34dde2c620

Closes #91628.
This commit is contained in:
Galin Iliev 2026-06-14 20:08:06 -07:00 committed by GitHub
parent dc46a67e80
commit 50c82b3020
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 18270 additions and 17 deletions

View file

@ -1406,22 +1406,13 @@ create` validates the written archive by default; `--no-verify` is the
explicit `dbPath`.
- `check:database-first-legacy-stores` fails new runtime source that pairs
legacy store names with write-style filesystem APIs. It also fails runtime
source that reintroduces transcript bridge contracts such as
`transcriptLocator`, `sqlite-transcript://...`, `sessionFile`, or
`storePath`, and scans tests for those bridge-contract names too. It also
bans `SessionManager.open(...)` and the old static SessionManager facades so
runtime and tests cannot silently re-create a file-backed session opener or
file-era session discovery. It also bans the old session JSONL downloader
hook/class from export UI. It also bans sidecar-shaped plugin-state/task
SQLite helper names; tests should assert `databasePath` and the shared
`state/openclaw.sqlite` location instead of pretending those features own
separate SQLite files. It also bans the old generic memory index SQL table
names (`meta`, `files`, `chunks`, `chunks_vec`,
`chunks_fts`, `embedding_cache`) in runtime source so the agent database keeps
its explicit `memory_index_*` schema. It also bans embedding TEXT schemas and
embedding JSON-array writes so vectors stay compact SQLite BLOBs. Migration,
doctor, import, and explicit non-session export code remain allowed. The
guard now also covers runtime `cache/*.json` stores, generic
source that reintroduces the retired transcript bridge markers
`transcriptLocator` or `sqlite-transcript://...`. Migration, doctor, import,
and explicit non-session export code remain allowed. Broader legacy contract
names such as `sessionFile`, `storePath`, and old `SessionManager` file-era
facades still have current owners and need separate migration guard work
before they can become a required preflight check. The guard now also covers
runtime `cache/*.json` stores, generic
`thread-bindings.json` sidecars, cron state/run-log JSON, config health JSON,
restart and lock sidecars, Voice Wake settings, plugin binding approvals,
installed plugin index JSON, File Transfer audit JSONL, Memory Wiki activity

View file

@ -1453,11 +1453,12 @@
"canvas:a2ui:bundle": "node scripts/bundle-a2ui.mjs",
"changed:lanes": "node scripts/changed-lanes.mjs",
"check": "node scripts/check.mjs",
"check:architecture": "pnpm check:import-cycles && pnpm check:madge-import-cycles && pnpm check:deprecated-api-usage && pnpm check:deprecated-jsdoc && pnpm db:kysely:check && pnpm lint:kysely",
"check:architecture": "pnpm check:import-cycles && pnpm check:madge-import-cycles && pnpm check:deprecated-api-usage && pnpm check:deprecated-jsdoc && pnpm db:kysely:check && pnpm lint:kysely && pnpm check:database-first-legacy-stores",
"check:base-config-schema": "node --import tsx scripts/generate-base-config-schema.ts --check",
"check:bundled-channel-config-metadata": "node --import tsx scripts/generate-bundled-channel-config-metadata.ts --check",
"check:changed": "node scripts/check-changed.mjs",
"check:changelog-attributions": "node scripts/check-changelog-attributions.mjs",
"check:database-first-legacy-stores": "node scripts/check-database-first-legacy-stores.mjs",
"check:deprecated-api-usage": "node scripts/check-deprecated-api-usage.mjs",
"check:deprecated-jsdoc": "node scripts/check-deprecated-jsdoc.mjs",
"check:docs": "pnpm format:docs:check && pnpm lint:docs && pnpm docs:check-mdx && pnpm docs:check-i18n-glossary && pnpm docs:check-links",

View file

@ -257,6 +257,7 @@ export function createChangedCheckPlan(result, options = {}) {
}
if (runAll) {
add("database-first legacy-store guard", ["check:database-first-legacy-stores"]);
add("media download helper guard", ["check:media-download-helpers"]);
add("runtime sidecar loader guard", ["check:runtime-sidecar-loaders"]);
addTypecheck("typecheck all", ["tsgo:all"]);
@ -322,6 +323,7 @@ export function createChangedCheckPlan(result, options = {}) {
}
if (lanes.core || lanes.extensions) {
add("database-first legacy-store guard", ["check:database-first-legacy-stores"]);
add("media download helper guard", ["check:media-download-helpers"]);
add("runtime sidecar loader guard", ["check:runtime-sidecar-loaders"]);
add("runtime import cycles", ["check:import-cycles"]);

File diff suppressed because it is too large Load diff

View file

@ -88,6 +88,7 @@ export async function main(argv = process.argv.slice(2)) {
commands: [
{ name: "conflict markers", args: ["check:no-conflict-markers"] },
{ name: "changelog attributions", args: ["check:changelog-attributions"] },
{ name: "database-first legacy-store guard", args: ["check:database-first-legacy-stores"] },
{
name: "guarded extension wildcard re-exports",
args: ["lint:extensions:no-guarded-wildcard-reexports"],

View file

@ -466,6 +466,9 @@ describe("scripts/changed-lanes", () => {
core: true,
coreTests: true,
});
expect(plan.commands.map((command) => command.args[0])).toContain(
"check:database-first-legacy-stores",
);
expect(plan.commands.map((command) => command.args[0])).toContain("tsgo:core");
expect(plan.commands.map((command) => command.args[0])).toContain("tsgo:core:test");
expect(plan.commands.find((command) => command.args[0] === "tsgo:core")?.env).toEqual({

File diff suppressed because it is too large Load diff