Skip to content

feat: replace sqlite with .confession storage and add cli mvp - #16

Merged
BlackishGreen33 merged 5 commits into
mainfrom
codex/file-store-cli-migration
Mar 8, 2026
Merged

feat: replace sqlite with .confession storage and add cli mvp#16
BlackishGreen33 merged 5 commits into
mainfrom
codex/file-store-cli-migration

Conversation

@BlackishGreen33

@BlackishGreen33 BlackishGreen33 commented Mar 8, 2026

Copy link
Copy Markdown
Owner

摘要

  • 全面下線 Prisma/SQLite,改用專案根目錄 .confession/*.json 檔案儲存
  • Extension 忽略規則改為 .confession/config.json(不再使用 .confessionignore
  • 新增 confession-cliinit/scan/list/status
  • 同步更新 steering / AGENTS / README
  • CI 移除 DATABASE_URL,並將 .confession/ 納入 .gitignore

主要變更

  • web/src/server/db.ts:FileStore + 檔鎖 + 原子寫入 + SQLite 一次性遷移
  • 移除 web/prisma/* 與 Prisma 依賴/腳本
  • extension/src/ignore-file.ts:改為讀寫 .confession/config.json
  • extension/src/extension.ts / extension/src/file-watcher.ts / extension/src/webview.ts:設定同步與 root-aware ignore
  • 新增 confession-cli/bin/confession.js

驗證

  • pnpm lint
  • pnpm build
  • pnpm test

Copilot AI review requested due to automatic review settings March 8, 2026 06:11
@BlackishGreen33
BlackishGreen33 merged commit 5bb00f0 into main Mar 8, 2026
4 checks passed
@BlackishGreen33
BlackishGreen33 deleted the codex/file-store-cli-migration branch March 8, 2026 06:13

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR migrates the web backend’s persistence layer from Prisma/SQLite to a project-local FileStore (.confession/*.json), and introduces an MVP CLI plus extension-side config/ignore syncing to align all clients on the new storage contract.

Changes:

  • Replaced Prisma/SQLite with a FileStore-backed “prisma-like” facade (file lock + atomic writes + one-time SQLite migration).
  • Updated web routes/serializers and health/advice logic to tolerate FileStore record shapes (date normalization + typed casts).
  • Updated extension to use root-aware ignore/config from .confession/config.json, added watcher-based sync, and added a confession-cli MVP.

Reviewed changes

Copilot reviewed 36 out of 39 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
web/src/server/db.ts Implements FileStore snapshot persistence, locking, and legacy SQLite migration behind a prisma-like API.
web/src/server/db.test.ts Updates persistence tests to exercise FileStore idempotency and transaction consistency.
web/src/server/routes/vulnerabilities.ts Adjusts dedupe + serialization to handle FileStore records and date parsing.
web/src/server/routes/scan.ts Hardens scan progress event normalization for FileStore/legacy shapes.
web/src/server/routes/export.ts Adapts export serialization typing for new store shapes.
web/src/server/health-score.ts Casts query results to expected health-score input shapes under FileStore.
web/src/server/advice-gate.ts Aligns vulnerability dedupe typing under FileStore.
extension/src/ignore-file.ts Adds .confession/config.json read/write + root-aware ignore resolution utilities.
extension/src/extension.ts Merges project config over VS Code settings and watches .confession/config.json for sync.
extension/src/file-watcher.ts Applies root-aware ignore rules for onSave incremental scans.
extension/src/webview.ts Normalizes ignore config before persisting and syncs to .confession/config.json.
extension/src/ignore-file.test.ts Adds unit tests for config normalization and root-aware ignore behavior.
extension/src/webview.test.ts Extends message-dispatch tests to cover project-config sync calls.
confession-cli/bin/confession.js Adds CLI commands for init/scan/list/status against the new FileStore.
confession-cli/package.json Declares the CLI package and bin entry.
web/package.json Removes Prisma scripts/deps in favor of FileStore.
turbo.json Removes Prisma-related global env configuration.
pnpm-workspace.yaml Adds confession-cli to the workspace.
.gitignore Ignores .confession/ and keeps legacy SQLite artifacts ignored.
.github/workflows/ci.yml Removes SQLite env requirement from CI.
README.md / AGENTS.md / .kiro/steering/* Updates documentation/steering to reflect FileStore + CLI usage and contracts.
web/prisma/* Removes Prisma schema and migrations from the repo.
Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 1066 to +1070
const fallbackUsed = Boolean(task.fallbackUsed);
const createdAt = toDateOrNow(task.createdAt);
const updatedAt = toDateOrNow(task.updatedAt);
return {
id: task.id,
id: typeof task.id === 'string' ? task.id : '',

Copilot AI Mar 8, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fallbackUsed is normalized via Boolean(...), which treats any non-empty string (including 'false') as true. If legacy/corrupted storage contains string values, this will incorrectly set fallbackUsed and expose fallback fields in the API. Prefer strict parsing: accept only boolean true (or the string 'true') as true, otherwise false.

Copilot uses AI. Check for mistakes.
return { exists: false, config: cloneDefaultConfig() }
}

return { exists: true, config: cloneDefaultConfig() }

Copilot AI Mar 8, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On JSON parse errors, readConfigFileByPath() returns exists: true with the default config. That makes callers treat the project config as present and override VS Code settings with defaults (and may later sync those defaults back to disk). Consider treating parse errors as exists: false (fallback to settings) or returning an explicit error flag so the UI can prompt the user to fix/remove the corrupted file.

Suggested change
return { exists: true, config: cloneDefaultConfig() }
// On JSON parse or other read errors, treat the config as non-existent so
// callers can fall back to settings or prompt the user to fix the file.
return { exists: false, config: cloneDefaultConfig() }

Copilot uses AI. Check for mistakes.
Comment on lines +356 to +360
const parsed = new Date(value);
if (!Number.isNaN(parsed.getTime())) return parsed.toISOString();
}
return new Date(0).toISOString();
}

Copilot AI Mar 8, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

toIsoString() falls back to new Date(0).toISOString() when the value is missing/invalid. This can silently turn bad storage data into a misleading 1970-01-01... timestamp in API responses. Prefer returning null for invalid values (and making the field nullable) or throwing/logging at least for required fields like createdAt/updatedAt so corruption is visible.

Copilot uses AI. Check for mistakes.
Comment thread web/src/server/db.ts
Comment on lines +272 to +274
} catch {
return fallback
}

Copilot AI Mar 8, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

readJsonFile() returns the fallback on any error (including JSON parse errors and permission errors). If a storage file is corrupted/unreadable, a subsequent write can load empty defaults and then persist them, effectively wiping data. Consider only falling back on ENOENT/ENOTDIR and surfacing parse errors (throw or quarantine/backup + log) to avoid silent data loss.

Copilot uses AI. Check for mistakes.
Comment thread web/src/server/db.ts
Comment on lines +1025 to +1029
async function withReadClient<T>(callback: (client: ReturnType<typeof buildScopedClient>) => Promise<T>): Promise<T> {
const projectRoot = resolveProjectRoot()
await ensureBootstrapped(projectRoot)
const snapshot = await loadSnapshot(projectRoot)
return callback(buildScopedClient(snapshot))

Copilot AI Mar 8, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reads (withReadClient) don’t take the file lock, while writes update multiple files in a snapshot. Even with atomic per-file renames, a reader can observe a mixed snapshot (some files old, some new). Consider locking reads too, or implementing a manifest/generation scheme so readers can detect and retry inconsistent versions.

Copilot uses AI. Check for mistakes.
Comment thread web/src/server/db.ts
Comment on lines +543 to +547
throw error
}
if (Date.now() >= deadline) {
throw new Error('Confession storage lock timeout')
}

Copilot AI Mar 8, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the process crashes while holding the lock, the .lock file will remain and all future writes will time out. Consider writing lock metadata (pid + timestamp) and treating old locks as stale (or using an OS-level advisory lock) so the store can recover after crashes.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants