Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions apps/desktop/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -891,6 +891,17 @@ function HomeView({
<Setting label={t("diagnostics.api")} value={`${localizeStatus(data.diagnostics.api.status, t)} ${data.diagnostics.api.bind}:${data.diagnostics.api.port}`} />
<Setting label={t("diagnostics.webui")} value={`${localizeStatus(data.diagnostics.webui.status, t)} ${data.diagnostics.webui.expected_url}`} />
<Setting label={t("diagnostics.storage")} value={`${data.diagnostics.storage.storage_mode} ${data.diagnostics.storage.storage_path || ""}`} />
<Setting
label={t("diagnostics.schema")}
value={`${data.diagnostics.storage.schema_version}/${data.diagnostics.storage.schema_target_version} / ${localizeStatus(data.diagnostics.storage.migration_status, t)}`}
/>
<Setting label={t("diagnostics.integrity")} value={localizeStatus(data.diagnostics.storage.integrity_status, t)} />
<Setting label={t("diagnostics.wal")} value={localizeStatus(data.diagnostics.storage.wal_status, t)} />
<Setting
label={t("diagnostics.migrationBackup")}
value={data.diagnostics.storage.migration_backup_created ? t("status.created") : t("status.notCreated")}
/>
<Setting label={t("diagnostics.backupCleanup")} value={localizeStatus(data.diagnostics.storage.backup_cleanup_status, t)} />
<Setting label={t("diagnostics.events")} value={data.diagnostics.storage.event_count.toString()} />
<Setting label={t("diagnostics.reviews")} value={data.diagnostics.storage.automation_review_count.toString()} />
<Setting
Expand Down Expand Up @@ -2296,6 +2307,10 @@ function localizeStatus(status: string, t: Translate): string {
ok: "status.ready",
ready: "status.ready",
passed: "status.passed",
current: "status.current",
migrated: "status.migrated",
warning: "status.warning",
not_applicable: "status.notApplicable",
available: "status.available",
free: "status.available",
enabled: "status.enabled",
Expand Down
11 changes: 11 additions & 0 deletions apps/desktop/src/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@
"status.noSensitiveCapture": "No keystrokes, screenshots, audio, or camera",
"status.ready": "Ready",
"status.passed": "Passed",
"status.current": "Current",
"status.migrated": "Updated",
"status.warning": "Warning",
"status.notApplicable": "Not applicable",
"status.created": "Created",
"status.notCreated": "Not created",
"status.available": "Available",
"status.disabled": "Disabled",
"status.unavailable": "Unavailable",
Expand Down Expand Up @@ -249,6 +255,11 @@
"diagnostics.api": "API",
"diagnostics.webui": "WebUI",
"diagnostics.storage": "Storage",
"diagnostics.schema": "Database schema",
"diagnostics.integrity": "Database integrity",
"diagnostics.wal": "WAL checkpoint",
"diagnostics.migrationBackup": "Pre-upgrade backup",
"diagnostics.backupCleanup": "Backup cleanup",
"diagnostics.events": "Events",
"diagnostics.reviews": "Reviews",
"diagnostics.recording": "Mac recording agent",
Expand Down
11 changes: 11 additions & 0 deletions apps/desktop/src/locales/ja.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@
"status.noSensitiveCapture": "キー入力・画面・音声・カメラは取得しない",
"status.ready": "準備完了",
"status.passed": "合格",
"status.current": "最新",
"status.migrated": "更新済み",
"status.warning": "要確認",
"status.notApplicable": "対象外",
"status.created": "作成済み",
"status.notCreated": "未作成",
"status.available": "利用可能",
"status.disabled": "無効",
"status.unavailable": "利用不可",
Expand Down Expand Up @@ -249,6 +255,11 @@
"diagnostics.api": "API",
"diagnostics.webui": "WebUI",
"diagnostics.storage": "保存先",
"diagnostics.schema": "データベーススキーマ",
"diagnostics.integrity": "データベース整合性",
"diagnostics.wal": "WAL チェックポイント",
"diagnostics.migrationBackup": "更新前バックアップ",
"diagnostics.backupCleanup": "バックアップ整理",
"diagnostics.events": "イベント",
"diagnostics.reviews": "レビュー",
"diagnostics.recording": "Mac記録エージェント",
Expand Down
7 changes: 7 additions & 0 deletions apps/desktop/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,13 @@ export type Diagnostics = {
manual_label_count: number;
import_history_count: number;
automation_review_count: number;
schema_version: number;
schema_target_version: number;
migration_status: string;
migration_backup_created: boolean;
integrity_status: string;
wal_status: string;
backup_cleanup_status: string;
};
dependencies: Record<string, DiagnosticItem>;
ports: Record<string, DiagnosticItem & { host: string; port: number }>;
Expand Down
8 changes: 8 additions & 0 deletions docs/architecture/DATA_MODEL.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,11 @@ OpsMineFlow uses a standard event record as the common contract between importer
## Storage

The product local workflow uses SQLite in the user's application data directory by default. Tests and explicit callers can still use an in-memory `EventStore`. Storage remains local-only and can be redirected with `OPSMINEFLOW_DATA_DIR`.

### Schema Evolution and Recovery

Persistent databases use `PRAGMA user_version` together with an append-only `schema_migrations` ledger. OpsMineFlow applies ordered migrations only at startup, in one SQLite transaction. A migration never rewrites an already-applied migration: a schema change requires a new, sequential migration entry and a matching registry checksum.

Before upgrading an existing recognized database, the app creates a SQLite online-backup snapshot in the local `backups/` directory. The backup directory is owner-only and the snapshot file is owner-read/write only. The app retains at most the three newest migration snapshots after an attempt that created a snapshot, whether that attempt commits or rolls back. The app verifies database integrity and foreign-key consistency before and after migration, then checkpoints WAL after a successful upgrade. A post-commit WAL checkpoint warning does not roll back a completed schema migration; diagnostics reports it separately for follow-up.

If a database was created by a newer app version, has an unknown migration ledger, or is not a recognized legacy schema, OpsMineFlow fails closed. It does not create tables, overwrite the database, seed sample data, or attempt an automatic restore. Keep the original database and use the pre-upgrade snapshot for manual recovery with a compatible build. **Delete Data** removes both active analysis records and migration snapshots; filesystem or Time Machine backups are outside the app's control.
8 changes: 8 additions & 0 deletions docs/operations/RUNBOOK.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,12 @@ The default SQLite database is stored under the user's macOS application data di

Exports are written only to the local path chosen by the user.

### Database Upgrades and Recovery

At startup, OpsMineFlow checks the local SQLite schema before loading records. When an upgrade is needed, it creates a private pre-upgrade snapshot under the local data directory's `backups/` folder and runs the ordered migration transaction. The three newest migration snapshots are retained. Diagnostics reports the schema version, migration status, integrity status, and whether a backup was created; it never exposes the backup path in the UI.

If startup reports that the database is from a newer app version, unknown, or failed to migrate, stop using that database. Do not delete or overwrite it. Preserve the database and its `backups/` folder, then open the snapshot only with a compatible OpsMineFlow build or follow the support/recovery procedure documented for that release. **Delete Data** removes both active analysis records and migration snapshots, but it cannot erase operating-system or Time Machine backups.

## Problem Resolution

Use [TROUBLESHOOTING.md](TROUBLESHOOTING.md) for startup, port, dependency, recording, import, ActivityWatch, export, and packaging problems.
Expand All @@ -117,6 +123,8 @@ Run all checks:
./scripts/check_no_external_network.sh
```

`./scripts/lint.sh` runs `./scripts/check_migrations.sh`. The migration registry check rejects gaps and checksum changes to applied migrations, so schema changes must be introduced as a new migration.

Start development servers:

```bash
Expand Down
21 changes: 21 additions & 0 deletions scripts/check_migrations.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#!/usr/bin/env bash
set -euo pipefail

ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
cd "$ROOT_DIR"

if [[ -x "$ROOT_DIR/.venv/bin/python" ]]; then
PYTHON_BIN="$ROOT_DIR/.venv/bin/python"
else
PYTHON_BIN="python3"
fi

export PYTHONPATH="$ROOT_DIR/services/mining-core/src:$ROOT_DIR/services/local-api/src:$ROOT_DIR/packages/drawio-exporter/src:${PYTHONPATH:-}"

echo "Checking SQLite migration registry..."
"$PYTHON_BIN" - <<'PY'
from opsmineflow_api.migrations import CURRENT_SCHEMA_VERSION, validate_migration_registry

validate_migration_registry()
print(f"Migration registry is valid through schema version {CURRENT_SCHEMA_VERSION}.")
PY
2 changes: 2 additions & 0 deletions scripts/lint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ cd "$ROOT_DIR"
echo "Compiling Python files..."
python3 -m compileall -q services packages/drawio-exporter/src

./scripts/check_migrations.sh

echo "Checking shell syntax..."
while IFS= read -r script; do
bash -n "$script"
Expand Down
Loading