From 11f690ca356610ce82258d0e8c0042a2ddcc5f2e Mon Sep 17 00:00:00 2001 From: Accusys Date: Thu, 14 May 2026 17:57:00 +0800 Subject: [PATCH] =?UTF-8?q?docs:=20fix=20start/end=20=E2=86=92=20start=5Ff?= =?UTF-8?q?rame/end=5Fframe=20in=20API=20docs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs_v1.0/API_V1.0.0/API_DOCUMENTATION.md | 2 +- .../API_V1.0.0/API_DOCUMENTATION_V1.0.0.md | 2 +- .../API_V1.0.0/API_DOCUMENTATION_v1.0.0.md | 2 +- docs_v1.0/API_V1.0.0/DEMO_SEQUENCE_V1.0.0.md | 2 +- docs_v1.0/API_V1.0.0/DEMO_SEQUENCE_v1.0.0.md | 2 +- .../RELEASE/RELEASE_API_REFERENCE_V1.0.0.md | 4 +- .../RELEASE/RELEASE_API_REFERENCE_v1.0.0.md | 4 +- .../2026-05-11_copy_import_issue.md | 88 ++ .../2026-05-11_delivery_checklist.md | 31 + .../2026-05-11_delivery_checklist_response.md | 27 + .../2026-05-11_package_delivery_spec.md | 195 ++++ .../2026-05-11_portal_setup_fix.md | 52 + .../2026-05-11_release_packaging_v2.md | 32 + ...26-05-11_request_post_correction_backup.md | 49 + .../M4_workspace/2026-05-11_v1.0.3_issues.md | 45 + .../M4_workspace/2026-05-12_api_doc_fixes.md | 50 + .../2026-05-12_api_doc_verification.md | 112 +++ .../2026-05-12_docs_structure_proposal.md | 47 + .../2026-05-12_package_receive_checklist.md | 313 ++++++ .../2026-05-12_tkg_unified_spec.md | 177 ++++ .../2026-05-13_api_3002_test_report.md | 55 ++ .../2026-05-13_api_test_v2_compatibility.md | 33 + ...026-05-13_chunk_vectors_unique_conflict.md | 42 + .../2026-05-13_coordinate_registry_report.md | 64 ++ .../2026-05-13_deploy_required_files_fix.md | 35 + .../2026-05-13_deploy_script_fix.md | 68 ++ .../2026-05-13_deploy_sh_remaining.md | 38 + .../2026-05-13_landmark_coord_mismatch.md | 54 + .../2026-05-13_release_binary_owner.md | 31 + .../2026-05-13_release_decision_request.md | 19 + .../2026-05-13_release_fixes_required.md | 73 ++ .../M4_workspace/2026-05-13_release_notes.md | 111 +++ .../2026-05-13_scan_status_issues.md | 77 ++ .../2026-05-13_v2_package_issues.md | 110 +++ .../2026-05-14_deploy_force_flag.md | 35 + .../M4_workspace/2026-05-14_inference_down.md | 37 + .../2026-05-14_pipeline_v2_deploy_report.md | 34 + .../2026-05-14_schemafix_binary_still_dev.md | 29 + .../2026-05-14_trace_schema_hardcode.md | 42 + docs_v1.0/REFERENCE/MARKBASE_DESIGN_V2.0.md | 919 ++++++++++++++++++ docs_v1.0/REFERENCE/MARKBASE_DESIGN_v1.0.0.md | 730 ++++++++++++++ docs_v1.0/REFERENCE/RELEASE_NOTES_v1.0.0.md | 111 +++ docs_v1.0/REFERENCE/RELEASE_SOP_V2.0.md | 280 ++++++ docs_v1.0/REFERENCE/file_uuid_spec.md | 418 ++++++++ 44 files changed, 4672 insertions(+), 9 deletions(-) create mode 100644 docs_v1.0/M4_workspace/2026-05-11_copy_import_issue.md create mode 100644 docs_v1.0/M4_workspace/2026-05-11_delivery_checklist.md create mode 100644 docs_v1.0/M4_workspace/2026-05-11_delivery_checklist_response.md create mode 100644 docs_v1.0/M4_workspace/2026-05-11_package_delivery_spec.md create mode 100644 docs_v1.0/M4_workspace/2026-05-11_portal_setup_fix.md create mode 100644 docs_v1.0/M4_workspace/2026-05-11_release_packaging_v2.md create mode 100644 docs_v1.0/M4_workspace/2026-05-11_request_post_correction_backup.md create mode 100644 docs_v1.0/M4_workspace/2026-05-11_v1.0.3_issues.md create mode 100644 docs_v1.0/M4_workspace/2026-05-12_api_doc_fixes.md create mode 100644 docs_v1.0/M4_workspace/2026-05-12_api_doc_verification.md create mode 100644 docs_v1.0/M4_workspace/2026-05-12_docs_structure_proposal.md create mode 100644 docs_v1.0/M4_workspace/2026-05-12_package_receive_checklist.md create mode 100644 docs_v1.0/M4_workspace/2026-05-12_tkg_unified_spec.md create mode 100644 docs_v1.0/M4_workspace/2026-05-13_api_3002_test_report.md create mode 100644 docs_v1.0/M4_workspace/2026-05-13_api_test_v2_compatibility.md create mode 100644 docs_v1.0/M4_workspace/2026-05-13_chunk_vectors_unique_conflict.md create mode 100644 docs_v1.0/M4_workspace/2026-05-13_coordinate_registry_report.md create mode 100644 docs_v1.0/M4_workspace/2026-05-13_deploy_required_files_fix.md create mode 100644 docs_v1.0/M4_workspace/2026-05-13_deploy_script_fix.md create mode 100644 docs_v1.0/M4_workspace/2026-05-13_deploy_sh_remaining.md create mode 100644 docs_v1.0/M4_workspace/2026-05-13_landmark_coord_mismatch.md create mode 100644 docs_v1.0/M4_workspace/2026-05-13_release_binary_owner.md create mode 100644 docs_v1.0/M4_workspace/2026-05-13_release_decision_request.md create mode 100644 docs_v1.0/M4_workspace/2026-05-13_release_fixes_required.md create mode 100644 docs_v1.0/M4_workspace/2026-05-13_release_notes.md create mode 100644 docs_v1.0/M4_workspace/2026-05-13_scan_status_issues.md create mode 100644 docs_v1.0/M4_workspace/2026-05-13_v2_package_issues.md create mode 100644 docs_v1.0/M4_workspace/2026-05-14_deploy_force_flag.md create mode 100644 docs_v1.0/M4_workspace/2026-05-14_inference_down.md create mode 100644 docs_v1.0/M4_workspace/2026-05-14_pipeline_v2_deploy_report.md create mode 100644 docs_v1.0/M4_workspace/2026-05-14_schemafix_binary_still_dev.md create mode 100644 docs_v1.0/M4_workspace/2026-05-14_trace_schema_hardcode.md create mode 100644 docs_v1.0/REFERENCE/MARKBASE_DESIGN_V2.0.md create mode 100644 docs_v1.0/REFERENCE/MARKBASE_DESIGN_v1.0.0.md create mode 100644 docs_v1.0/REFERENCE/RELEASE_NOTES_v1.0.0.md create mode 100644 docs_v1.0/REFERENCE/RELEASE_SOP_V2.0.md create mode 100644 docs_v1.0/REFERENCE/file_uuid_spec.md diff --git a/docs_v1.0/API_V1.0.0/API_DOCUMENTATION.md b/docs_v1.0/API_V1.0.0/API_DOCUMENTATION.md index 747623a..06305a4 100644 --- a/docs_v1.0/API_V1.0.0/API_DOCUMENTATION.md +++ b/docs_v1.0/API_V1.0.0/API_DOCUMENTATION.md @@ -338,7 +338,7 @@ Returns video with face bounding boxes overlaid. ``` GET /api/v1/file/{file_uuid}/video/bbox -Query: ?start=0&end=300&face_uuid=xxx +Query: ?start_frame=0&end_frame=300&face_uuid=xxx ``` Returns `video/mp4` binary with red bboxes drawn at frame intervals. diff --git a/docs_v1.0/API_V1.0.0/API_DOCUMENTATION_V1.0.0.md b/docs_v1.0/API_V1.0.0/API_DOCUMENTATION_V1.0.0.md index 93c2f9e..f33af99 100644 --- a/docs_v1.0/API_V1.0.0/API_DOCUMENTATION_V1.0.0.md +++ b/docs_v1.0/API_V1.0.0/API_DOCUMENTATION_V1.0.0.md @@ -338,7 +338,7 @@ Returns video with face bounding boxes overlaid. ``` GET /api/v1/file/{file_uuid}/video/bbox -Query: ?start=0&end=300&face_uuid=xxx +Query: ?start_frame=0&end_frame=300&face_uuid=xxx ``` Returns `video/mp4` binary with red bboxes drawn at frame intervals. diff --git a/docs_v1.0/API_V1.0.0/API_DOCUMENTATION_v1.0.0.md b/docs_v1.0/API_V1.0.0/API_DOCUMENTATION_v1.0.0.md index 93c2f9e..f33af99 100644 --- a/docs_v1.0/API_V1.0.0/API_DOCUMENTATION_v1.0.0.md +++ b/docs_v1.0/API_V1.0.0/API_DOCUMENTATION_v1.0.0.md @@ -338,7 +338,7 @@ Returns video with face bounding boxes overlaid. ``` GET /api/v1/file/{file_uuid}/video/bbox -Query: ?start=0&end=300&face_uuid=xxx +Query: ?start_frame=0&end_frame=300&face_uuid=xxx ``` Returns `video/mp4` binary with red bboxes drawn at frame intervals. diff --git a/docs_v1.0/API_V1.0.0/DEMO_SEQUENCE_V1.0.0.md b/docs_v1.0/API_V1.0.0/DEMO_SEQUENCE_V1.0.0.md index f527867..8d10748 100644 --- a/docs_v1.0/API_V1.0.0/DEMO_SEQUENCE_V1.0.0.md +++ b/docs_v1.0/API_V1.0.0/DEMO_SEQUENCE_V1.0.0.md @@ -82,7 +82,7 @@ https://api.momentry.ddns.net/api/v1/file/3abeee81d94597629ed8cb943f182e94/trace Browser 開: ``` -https://api.momentry.ddns.net/api/v1/file/3abeee81d94597629ed8cb943f182e94/video/bbox?start=68000&end=69000 +https://api.momentry.ddns.net/api/v1/file/3abeee81d94597629ed8cb943f182e94/video/bbox?start_frame=68000&end_frame=69000 ``` **預期**: 該區間內所有臉部偵測的 bbox overlay 影片 diff --git a/docs_v1.0/API_V1.0.0/DEMO_SEQUENCE_v1.0.0.md b/docs_v1.0/API_V1.0.0/DEMO_SEQUENCE_v1.0.0.md index f527867..8d10748 100644 --- a/docs_v1.0/API_V1.0.0/DEMO_SEQUENCE_v1.0.0.md +++ b/docs_v1.0/API_V1.0.0/DEMO_SEQUENCE_v1.0.0.md @@ -82,7 +82,7 @@ https://api.momentry.ddns.net/api/v1/file/3abeee81d94597629ed8cb943f182e94/trace Browser 開: ``` -https://api.momentry.ddns.net/api/v1/file/3abeee81d94597629ed8cb943f182e94/video/bbox?start=68000&end=69000 +https://api.momentry.ddns.net/api/v1/file/3abeee81d94597629ed8cb943f182e94/video/bbox?start_frame=68000&end_frame=69000 ``` **預期**: 該區間內所有臉部偵測的 bbox overlay 影片 diff --git a/docs_v1.0/API_V1.0.0/RELEASE/RELEASE_API_REFERENCE_V1.0.0.md b/docs_v1.0/API_V1.0.0/RELEASE/RELEASE_API_REFERENCE_V1.0.0.md index f857758..749c801 100644 --- a/docs_v1.0/API_V1.0.0/RELEASE/RELEASE_API_REFERENCE_V1.0.0.md +++ b/docs_v1.0/API_V1.0.0/RELEASE/RELEASE_API_REFERENCE_V1.0.0.md @@ -133,8 +133,8 @@ curl "$BASE/api/v1/file/$FILE/trace/2/faces?limit=2&interpolate=true" -H "$KEY" | # | Method | Path | Description | |---|--------|------|-------------| | 29 | GET | `/api/v1/file/:file_uuid/thumbnail` | Frame JPEG (?frame=&x=&y=&w=&h=) | -| 30 | GET | `/api/v1/file/:file_uuid/video` | Raw video (?start=&end=) | -| 31 | GET | `/api/v1/file/:file_uuid/video/bbox` | Bbox overlay (?start=&end=&duration=) | +| 30 | GET | `/api/v1/file/:file_uuid/video` | Raw video (start_frame=&end_frame=) | +| 31 | GET | `/api/v1/file/:file_uuid/video/bbox` | Bbox overlay (start_frame=&end_frame=&duration=) | | 32 | GET | `/api/v1/file/:file_uuid/trace/:trace_id/video` | Trace clip (?padding=) | --- diff --git a/docs_v1.0/API_V1.0.0/RELEASE/RELEASE_API_REFERENCE_v1.0.0.md b/docs_v1.0/API_V1.0.0/RELEASE/RELEASE_API_REFERENCE_v1.0.0.md index f857758..749c801 100644 --- a/docs_v1.0/API_V1.0.0/RELEASE/RELEASE_API_REFERENCE_v1.0.0.md +++ b/docs_v1.0/API_V1.0.0/RELEASE/RELEASE_API_REFERENCE_v1.0.0.md @@ -133,8 +133,8 @@ curl "$BASE/api/v1/file/$FILE/trace/2/faces?limit=2&interpolate=true" -H "$KEY" | # | Method | Path | Description | |---|--------|------|-------------| | 29 | GET | `/api/v1/file/:file_uuid/thumbnail` | Frame JPEG (?frame=&x=&y=&w=&h=) | -| 30 | GET | `/api/v1/file/:file_uuid/video` | Raw video (?start=&end=) | -| 31 | GET | `/api/v1/file/:file_uuid/video/bbox` | Bbox overlay (?start=&end=&duration=) | +| 30 | GET | `/api/v1/file/:file_uuid/video` | Raw video (start_frame=&end_frame=) | +| 31 | GET | `/api/v1/file/:file_uuid/video/bbox` | Bbox overlay (start_frame=&end_frame=&duration=) | | 32 | GET | `/api/v1/file/:file_uuid/trace/:trace_id/video` | Trace clip (?padding=) | --- diff --git a/docs_v1.0/M4_workspace/2026-05-11_copy_import_issue.md b/docs_v1.0/M4_workspace/2026-05-11_copy_import_issue.md new file mode 100644 index 0000000..9fe2a54 --- /dev/null +++ b/docs_v1.0/M4_workspace/2026-05-11_copy_import_issue.md @@ -0,0 +1,88 @@ +# File Content Import Failure: COPY column mismatch + +**Date**: 2026-05-11 +**From**: M4 +**To**: M5 + +--- + +## Failure + +``` +psql:/tmp/.../data.sql:22054: ERROR: syntax error at or near "id" +LINE 1: id,file_uuid,face_id,frame_number,x,y,width,height,confidenc... + ^ +``` + +## Root Cause + +### Step 1: First error — COPY dev.chunk_vectors fails silently + +The real error occurs earlier in the file. psql suppresses it and continues, but then treats all subsequent lines as SQL commands. The face_detections CSV header `id,file_uuid,face_id,...` is the first line that triggers a visible syntax error. + +### Step 2: Why COPY fails — extra column + +M4 and M5 table schemas differ: + +```sql +-- M4 dev.face_detections (13 columns) +id, file_uuid, face_id, frame_number, +x, y, width, height, +confidence, embedding, identity_id, +created_at, trace_id + +-- M5 exported CSV (14 columns) +id, file_uuid, face_id, frame_number, +x, y, width, height, +confidence, embedding, identity_id, +created_at, trace_id, timestamp_secs ← extra +``` + +The `COPY` command uses an empty column list: + +```sql +COPY dev.face_detections () FROM STDIN WITH CSV HEADER; + ^^ empty = expects ALL columns +``` + +PostgreSQL requires the CSV column count to match the table column count. With 14 CSV columns vs 13 table columns, COPY rejects the entire data block. + +### Step 3: Additionally, first chunk_vectors COPY may also fail + +Same issue applies to `dev.chunk_vectors`: + +```sql +COPY dev.chunk_vectors () FROM STDIN WITH CSV HEADER; +``` + +If M5 added any columns to `chunk_vectors` that don't exist on M4, this COPY would also fail — which would explain why ALL three data tables (chunk, chunk_vectors, face_detections) show 0 rows after import. + +## Verification + +Check if M5's chunk_vectors has extra columns beyond: +``` +id, chunk_id, uuid, chunk_type, embedding, created_at +``` + +## Recommended Fix + +### Option A: Use column-explicit COPY (recommended) + +```sql +COPY dev.face_detections (id, file_uuid, face_id, frame_number, x, y, width, height, confidence, embedding, identity_id, created_at, trace_id) +FROM STDIN WITH CSV HEADER; +``` + +This explicitly names the columns, ignoring `timestamp_secs` if present in CSV but missing in M4 schema. + +### Option B: Deliver INSERT-format dump + +```sql +pg_dump --column-inserts --table=... +``` + +Same format as the working `dev_backup_post_correction.sql`. Larger file but portable across schema versions. + +### Option C: Deliver schema migration + +Provide `ALTER TABLE dev.face_detections ADD COLUMN timestamp_secs double precision;` as part of the dev upgrade package. diff --git a/docs_v1.0/M4_workspace/2026-05-11_delivery_checklist.md b/docs_v1.0/M4_workspace/2026-05-11_delivery_checklist.md new file mode 100644 index 0000000..1a26a5b --- /dev/null +++ b/docs_v1.0/M4_workspace/2026-05-11_delivery_checklist.md @@ -0,0 +1,31 @@ +# 交付確認 — v1.0.3 打包 + +**Date**: 2026-05-11 +**From**: M4 +**To**: M5 + +--- + +## 已收到 + +| 文件 | 大小 | 狀態 | +|------|------|:--:| +| `momentry_core_v1.0.3_source.tar.gz` | 378MB | ✅ | +| `dev_backup_post_correction.sql` | 86MB | ✅ | +| `aeed71342a899fe4b4c57b7d41bcb692.asr-1.json` | 1.3MB | ✅ | +| `api_test.sh` | 9KB | ✅ | +| `RELEASE_PACKAGING_README.md` | 2.6KB | ✅ | +| `HANDOVER_v1.0.3.md` | 9.8KB | ✅ | + +## 未收到 + +| 文件 | 預計大小 | 狀態 | +|------|---------|:--:| +| `release/dev_upgrade_v1.0.3.tar.gz` | ~400MB | ❌ | +| `release/file_aeed71342a899fe4b4c57b7d41bcb692_v1.0.tar.gz` | ? | ❌ | + +## 問題 + +1. 兩個包未出現在 `/Volumes/docs_v1.0/M4_HANDOVER/`。是否還在打包? +2. `momentry_core_v1.0.3_source.tar.gz` 裡面的 `release/` 仍是 v1.0.0 的舊檔。v1.0.3 的 `release/system/dev/` 和 `release/files/` 目錄結構是否應該包在 source tarball 內? +3. 檔案內容包的命名格式是 `file_{uuid}_v{major}.{minor}.tar.gz` 嗎? diff --git a/docs_v1.0/M4_workspace/2026-05-11_delivery_checklist_response.md b/docs_v1.0/M4_workspace/2026-05-11_delivery_checklist_response.md new file mode 100644 index 0000000..94c92d1 --- /dev/null +++ b/docs_v1.0/M4_workspace/2026-05-11_delivery_checklist_response.md @@ -0,0 +1,27 @@ +# M5 回覆 — 交付確認 + +## Q1: 兩包已放共享目錄 + +`/Volumes/docs_v1.0/M4_HANDOVER/` 現在有: + +| 檔案 | 大小 | +|------|:----:| +| `dev_upgrade_v1.0.3.tar.gz` | 400MB | +| `file_aeed71342a899fe4b4c57b7d41bcb692_v1.0.tar.gz` | 53MB | +| `momentry_core_v1.0.3_source.tar.gz` | 205MB(已清理 release/) | + +## Q2: source tarball 已清理 + +重新打包,排除 `release/`、`storage/`、`model_checkpoints/`、`output/`。從 378MB → **205MB**。 + +`release/system/dev/` 和 `release/files/` 是**交付時產生的目錄**,不是 source code 的一部分,不會包進 source tarball。M4 收到後解壓 `dev_upgrade_*.tar.gz` 執行升級即可。 + +## Q3: 命名規範確認 + +| 包類型 | 格式 | 範例 | +|--------|------|------| +| 開發系統升級包 | `dev_upgrade_{version}.tar.gz` | `dev_upgrade_v1.0.3.tar.gz` | +| 生產系統升級包 | `prod_upgrade_{version}.tar.gz` | `prod_upgrade_v1.0.3.tar.gz` | +| 檔案內容包 | `file_{file_uuid}_{version}.tar.gz` | `file_aeed71342a...b692_v1.0.tar.gz` | + +目前只有一個 Charade 檔案內容包。後續其他影片會用同一格式。 diff --git a/docs_v1.0/M4_workspace/2026-05-11_package_delivery_spec.md b/docs_v1.0/M4_workspace/2026-05-11_package_delivery_spec.md new file mode 100644 index 0000000..9d35220 --- /dev/null +++ b/docs_v1.0/M4_workspace/2026-05-11_package_delivery_spec.md @@ -0,0 +1,195 @@ +# M5 Package Delivery Spec — Three-Tier Packaging System + +**Date**: 2026-05-11 +**From**: M4 +**To**: M5 +**Priority**: High + +--- + +## 1. 三層打包架構 + +``` +├── 開發系統升級包 (Dev System Upgrade) +│ └── Schema migration + binary upgrade for dev (port 3003) +│ +├── 生產系統升級包 (Production System Upgrade) +│ └── Schema migration + binary upgrade for prod (port 3002) +│ +└── 檔案內容包 (File Content Package) + └── One file_uuid per package, complete post-correction data +``` + +--- + +## 2. 開發系統升級包 + +**目的**:升級開發環境(schema=dev, port 3003)。 + +| 屬性 | 說明 | +|------|------| +| Schema | `dev` | +| 內容 | Schema migration SQL + 新版 binary + source code | +| 不含 | 任何檔案資料(無 file content data) | +| 頻率 | 每次系統升級 | + +### 內容物 + +``` +dev_upgrade_v1.0.2.tar.gz +├── schema/ +│ └── migration_v1.0.2.sql # ALTER TABLE, CREATE INDEX, etc. +├── bin/ +│ └── momentry_playground # Dev binary +├── src/ +│ └── momentry_core_v1.0.2.tar.gz # Full source (optional) +└── UPGRADE.md # 升級步驟 +``` + +### 升級步驟 + +```bash +# 1. Apply schema migration +psql -U accusys -d momentry -c "SET search_path TO dev;" +psql -U accusys -d momentry < schema/migration_v1.0.2.sql + +# 2. Replace binary +cp bin/momentry_playground /path/to/target/debug/ + +# 3. Restart server (port 3003) +DATABASE_SCHEMA=dev ./momentry_playground server --port 3003 +``` + +--- + +## 3. 生產系統升級包 + +**目的**:升級生產環境(schema=public, port 3002)。 + +| 屬性 | 說明 | +|------|------| +| Schema | `public` | +| 內容 | Schema migration SQL + release binary | +| 不含 | 任何檔案資料 | +| ⚠️ 注意 | 生產環境變更需要謹慎,必須先備份 | + +### 內容物 + +``` +prod_upgrade_v1.0.2.tar.gz +├── schema/ +│ └── migration_v1.0.2.sql # ALTER TABLE, CREATE INDEX (public schema) +├── bin/ +│ └── momentry # Release binary +└── UPGRADE.md # 升級步驟 + rollback 指令 +``` + +### 升級步驟 + +```bash +# 0. Backup current DB (mandatory) +pg_dump -U accusys -d momentry --schema=public > backup_v1.0.1.sql + +# 1. Apply schema migration +psql -U accusys -d momentry < schema/migration_v1.0.2.sql + +# 2. Replace binary +cp bin/momentry /path/to/target/release/ + +# 3. Restart server (port 3002) +./momentry server --port 3002 +``` + +--- + +## 4. 檔案內容包 + +**目的**:交付單一影片的完整已處理資料。一包一檔。 + +| 屬性 | 說明 | +|------|------| +| 範圍 | **一個 file_uuid**(不跨檔案) | +| 內容 | 已註冊 + 已處理 + post-correction | +| Schema | `dev` 或 `public`(標註清楚) | +| 狀態 | 匯入後該檔案即為「已就緒(ready)」 | + +### 內容物 + +``` +charade_1963_{file_uuid}.tar.gz +├── data.sql # PG dump: videos + chunk + chunk_vectors + face_detections +├── file_info.json # { file_uuid, file_name, fps, duration, resolution, ... } +├── checksums.md5 # Integrity check +└── README.md # Import instructions +``` + +### Data Scope(data.sql 內容) + +每個 table 只匯出該 file_uuid 的 rows: + +| Table | WHERE Clause | +|-------|-------------| +| `videos` | `WHERE file_uuid = '{uuid}'` | +| `chunk` | `WHERE file_uuid = '{uuid}'` | +| `chunk_vectors` | `WHERE uuid = '{uuid}'` | +| `face_detections` | `WHERE file_uuid = '{uuid}'` | +| `identities` (if related) | 與此 file_uuid 關聯 | + +### 匯出指令(M5 端) + +```bash +UUID="aeed71342a899fe4b4c57b7d41bcb692" + +pg_dump -U accusys -d momentry --schema=dev \ + --data-only --column-inserts \ + --table=dev.videos \ + --table=dev.chunk \ + --table=dev.chunk_vectors \ + --table=dev.face_detections \ + --where="file_uuid='${UUID}'" \ + > data.sql +``` + +### 匯入指令(M4 端) + +```bash +export UUID="aeed71342a899fe4b4c57b7d41bcb692" + +# 1. Delete existing data for this file_uuid (if any) +psql -U accusys -d momentry < release/phase1/backup_post_correction.sql +``` + +Both tables should reflect the state AFTER running `apply_asr_corrections.py`. + +--- + +## M5 Response + +**Status**: ✅ Backup ready + +| Check | Value | +|------|-------| +| dev.chunk rows (Charade) | 6,021 total (4,188 sentence + 1,130 cut + 423 trace + 280 story) | +| dev.chunk_vectors | 4,188 | +| Matched (chunk ↔ vectors) | 4,188 ✅ | +| chunk_id format | Short (`0-01`, `1446-01`, `story_240`, etc.) | + +**File**: `docs_v1.0/M4_HANDOVER/dev_backup_post_correction.sql` (86 MB) + +This backup reflects the state AFTER applying corrections, with all schema changes (RENAME, column drops, short chunk_ids) already applied. No further migration steps needed — just `psql -U accusys -d momentry < dev_backup_post_correction.sql`. diff --git a/docs_v1.0/M4_workspace/2026-05-11_v1.0.3_issues.md b/docs_v1.0/M4_workspace/2026-05-11_v1.0.3_issues.md new file mode 100644 index 0000000..701c7bc --- /dev/null +++ b/docs_v1.0/M4_workspace/2026-05-11_v1.0.3_issues.md @@ -0,0 +1,45 @@ +# v1.0.3 文件內容包 — COPY 格式確認 + +**Date**: 2026-05-11 +**From**: M4 +**To**: M5 + +--- + +## 結論:COPY 格式無問題 + +M4 再次以 clean state 測試後,`data.sql` 匯入成功: + +``` +COPY 1 (videos) +COPY 6021 (chunk) +COPY 4188 (chunk_vectors) +COPY 11820 (face_detections) +COMMIT +``` + +39/39 API tests pass. + +## 之前失敗的原因 + +M4 測試時先以 INSERT 格式的 `dev_backup_post_correction.sql` 匯入,再用 COPY 格式的 `data.sql` 匯入 → 兩套相同資料的 ID 衝突(duplicate key)。 + +## 正確的檔案內容包匯入流程 + +```bash +# 1. Apply schema migration first (dev upgrade) +psql -U accusys -d momentry < schema/migration_v1.0.3.sql + +# 2. Delete existing data for this UUID (if any) +psql -U accusys -d momentry < 1000 | +| chunk_vectors rows | > 1000 | +| face_detections rows | > 1000 | +| identities rows (**v2**) | > 0 | +| identity_bindings rows (**v2**) | > 0 | +| chunk vs vectors 數量 | 二者不應差太多 | + +--- + +## 4. Identity 完整性檢查(**v2 新版**) + +### 匯入後檢查 + +```sql +-- 1. 檢查 face_detections → identity_id 覆蓋率 +SELECT + count(*) as total_faces, + count(identity_id) as faces_with_id, + round(100.0 * count(identity_id) / count(*), 1) as pct +FROM dev.face_detections WHERE file_uuid = '{UUID}'; +``` + +| 覆蓋率 | 判定 | +|:---:|------| +| > 80% | ✅ 正常 | +| 20-80% | ⚠️ 偏低,確認 M5 pipeline | +| < 20% | ❌ 異常 | + +```sql +-- 2. 檢查 identity_id 是否指向存在的 identity +SELECT count(*) as orphans +FROM dev.face_detections f +LEFT JOIN dev.identities i ON f.identity_id = i.id +WHERE f.file_uuid = '{UUID}' + AND f.identity_id IS NOT NULL + AND i.id IS NULL; +``` + +> 0 → ❌ orphan identity reference,需回報 M5 + +```sql +-- 3. 檢查 identity_bindings 綁定數量 +SELECT count(*) as bindings +FROM dev.identity_bindings +WHERE identity_id IN ( + SELECT DISTINCT identity_id FROM dev.face_detections + WHERE file_uuid = '{UUID}' AND identity_id IS NOT NULL +); +``` + +**binding 數量應與 face_detections 中有 identity_id 的行數相近** + +```sql +-- 4. 列出此檔案綁定的人物 Top 10 +SELECT i.name, count(DISTINCT f.id) as unique_faces +FROM dev.face_detections f +JOIN dev.identities i ON f.identity_id = i.id +WHERE f.file_uuid = '{UUID}' +GROUP BY i.name ORDER BY unique_faces DESC LIMIT 10; +``` + +**應看到已知角色(如 Cary Grant, Audrey Hepburn)** + +```sql +-- 5. 檢查 trace-level identity binding +SELECT + COUNT(DISTINCT trace_id) FILTER (WHERE identity_id IS NOT NULL) as bound_traces, + COUNT(DISTINCT trace_id) FILTER (WHERE identity_id IS NULL) as unbound_traces +FROM dev.face_detections WHERE file_uuid = '{UUID}'; +``` + +| 判定 | bound / unbound | +|:--:|------| +| ✅ | bound traces 佔多數 | +| ⚠️ | unbound traces > 30% | + +--- + +## 5. 匯入測試 + +```bash +UUID="..." + +# 1. 備份現有資料 +pg_dump -U accusys -d momentry --schema=dev \ + --table=dev.chunk --table=dev.chunk_vectors \ + --table=dev.face_detections --table=dev.videos \ + --table=dev.identities --table=dev.identity_bindings \ + --where="file_uuid='${UUID}'" > backup_before_import.sql + +# 2. 刪除舊資料(含 identity tables) +psql -U accusys -d momentry <&1 | tail -5 +``` + +### 9.2 Portal 層 + +| 頁面 | URL | 驗證點 | +|------|-----|--------| +| FilesView | `http://localhost:1420/files` | ✅ Charade 顯示「已就绪」 | +| SearchView | `http://localhost:1420/search` | ✅ 搜尋檔案下拉有 Charade | +| Search Play | Search "Audrey Hepburn" → Play | ✅ 影片片段播放 | +| Trace View | 點 trace → faces | ✅ 人臉列表 + trace video | +| Identities | `http://localhost:1420/identities` | ✅ Cary Grant / Audrey Hepburn 顯示 | + +### 9.3 資料核對 + +| 檢查項 | 方法 | 標準 | +|--------|------|:--:| +| 影片可播放 | `GET /api/v1/file/{uuid}/video` | 200, content-type: video/mp4 | +| 縮圖可顯示 | `GET /api/v1/file/{uuid}/thumbnail?frame=1000` | 200, image/jpeg | +| chunk detail 存在 | `GET /api/v1/file/{uuid}/chunk/0` | 200 | +| face trace video | `GET /api/v1/file/{uuid}/trace/{id}/video` | 200 | +| face trace faces 3D | `GET /api/v1/file/{uuid}/trace/{id}/faces?dimension=3d` | 200, has z_rel | +| identity detail | `GET /api/v1/identity/{id}` | 200 | +| files/scan 標記 registered | `GET /api/v1/files/scan` | is_registered = true | + +### 9.4 上架通過標準 + +| 條件 | 狀態 | +|------|:--:| +| API test 39/39 | ✅ | +| Portal FilesView 顯示已就绪 | ✅ | +| Portal Search 可搜到 + 可 Play | ✅ | +| Portal Face Traces 可查看 | ✅ | +| API /files/scan is_registered = true | ✅ | + +--- + +## 10. 接受/退回標準 + +| 狀態 | 條件 | +|:--:|------| +| ✅ 接受 | 1-8 全過,identity 覆蓋 > 80%,無 orphan;9.1 全過 | +| ⚠️ 暫存 | 1-5 過但 identity 覆蓋偏低,或綁定數偏少 | +| ❌ 退回 | hash fail、table 缺漏、匯入失敗、identity 全 NULL、9.1 失敗 | + +退回時附 log 和失敗原因。 diff --git a/docs_v1.0/M4_workspace/2026-05-12_tkg_unified_spec.md b/docs_v1.0/M4_workspace/2026-05-12_tkg_unified_spec.md new file mode 100644 index 0000000..851068a --- /dev/null +++ b/docs_v1.0/M4_workspace/2026-05-12_tkg_unified_spec.md @@ -0,0 +1,177 @@ +# PG + SQLite TKG 統一分析與檔案內容包要求 + +**Date**: 2026-05-12 +**From**: M4 +**To**: M5 +**Priority**: High + +--- + +## 1. PG TKG 現狀 + +``` +dev.tkg_nodes dev.tkg_edges +────────────── ────────────── +id (PK, bigint) id (PK, bigint) +node_type (varchar) edge_type (varchar) +external_id (varchar) source_node_id (FK → tkg_nodes.id) +file_uuid (varchar) target_node_id (FK → tkg_nodes.id) +label (varchar) file_uuid (varchar) +properties (jsonb) properties (jsonb) +created_at (timestamptz) created_at (timestamptz) +``` + +- 資料量:2,414 nodes / 1,320 edges +- 建立方式:`tkg_builder.py` (Python),由 `job_worker.rs:824` 呼叫 +- **查詢方式**:目前 Rust 程式碼中**沒有** `WITH RECURSIVE` 圖查詢 + +--- + +## 2. SQLite TKG 設計 + +表結構與 PG 完全一致: + +```sql +CREATE TABLE tkg_nodes ( + id INTEGER PRIMARY KEY, + node_type TEXT NOT NULL, + external_id TEXT NOT NULL, + file_uuid TEXT NOT NULL, + label TEXT, + properties TEXT DEFAULT '{}', + created_at TEXT DEFAULT (datetime('now')), + UNIQUE(file_uuid, node_type, external_id) +); + +CREATE TABLE tkg_edges ( + id INTEGER PRIMARY KEY, + edge_type TEXT NOT NULL, + source_node_id INTEGER NOT NULL REFERENCES tkg_nodes(id) ON DELETE CASCADE, + target_node_id INTEGER NOT NULL REFERENCES tkg_nodes(id) ON DELETE CASCADE, + file_uuid TEXT NOT NULL, + properties TEXT DEFAULT '{}', + created_at TEXT DEFAULT (datetime('now')), + UNIQUE(file_uuid, edge_type, source_node_id, target_node_id) +); +``` + +### 圖查詢(Recursive CTE,PG 和 SQLite 通用) + +```sql +-- 2-hop traversal:從 Audrey Hepburn 出發走兩步 +WITH RECURSIVE walk AS ( + SELECT target_node_id, 1 AS depth + FROM tkg_edges + WHERE source_node_id = ( + SELECT id FROM tkg_nodes WHERE label = 'Audrey Hepburn' + ) + UNION ALL + SELECT e.target_node_id, w.depth + 1 + FROM tkg_edges e JOIN walk w ON e.source_node_id = w.target_node_id + WHERE w.depth < 3 +) +SELECT n.label, w.depth +FROM walk w JOIN tkg_nodes n ON w.target_node_id = n.id; +``` + +**這份 SQL 在 PG 和 SQLite 上完全一致,無需修改。** + +--- + +## 3. 檔案內容包要求 + +每個 `file_{uuid}_{ver}.tar.gz` 必須包含 TKG: + +``` +file_aeed71342a899fe4b4c57b7d41bcb692_v1.0.tar.gz +├── data.sql ← PG dump (videos, chunk, vectors, faces) +├── tkg.sqlite ← SQLite TKG (new requirement) +├── file_info.json +└── checksums.md5 +``` + +### 匯出方式 + +```bash +# From PG: export node/edge data to SQLite +sqlite3 tkg.sqlite "ATTACH ...; CREATE TABLE ...; INSERT INTO ..." +``` + +```bash +# Or: tkg_builder.py generates both PG INSERT and SQLite +python3 tkg_builder.py build \ + --uuid {uuid} \ + --pg-output pg_tkg.sql \ + --sqlite-output {uuid}.tkg.sqlite +``` + +--- + +## 4. Momentry Core 實作要求 + +### trait 定義 + +```rust +pub trait TkgStore: Send + Sync { + async fn get_nodes(&self, file_uuid: &str) -> Result>; + async fn get_edges(&self, file_uuid: &str) -> Result>; + async fn traverse(&self, node_id: i64, max_depth: i32) -> Result>; + async fn neighbors(&self, node_id: i64) -> Result>; +} +``` + +### 兩個實作,同一份 SQL + +```rust +// PG backend +struct PgTkg { pool: PgPool } +impl TkgStore for PgTkg { + async fn traverse(&self, node_id: i64, max_depth: i32) -> ... { + sqlx::query_as(SAME_RECURSIVE_CTE_SQL) // WITH RECURSIVE ... + } +} + +// SQLite backend +struct SqliteTkg { conn: rusqlite::Connection } +impl TkgStore for SqliteTkg { + async fn traverse(&self, node_id: i64, max_depth: i32) -> ... { + conn.prepare(SAME_RECURSIVE_CTE_SQL) // WITH RECURSIVE ... (same SQL) + } +} +``` + +### 啟動時選擇後端 + +```rust +let tkg: Box = match env::var("MOMENTRY_TKG_BACKEND") + .unwrap_or("pg".into()).as_str() +{ + "sqlite" => Box::new(SqliteTkg::open("tkg.sqlite")?), + _ => Box::new(PgTkg::new(&pg_pool)), +}; +``` + +--- + +## 5. 一致性原則 + +| 面向 | 原則 | +|------|------| +| **Schema** | PG 和 SQLite 表結構完全一致 | +| **匯出** | pipeline 建 TKG 時同時輸出 PG + SQLite | +| **查詢** | Recursive CTE SQL 語法一致,兩邊通用 | +| **檔案包** | 每個 tar.gz 必須含 `tkg.sqlite` | +| **分發** | PG 在 API server,SQLite 隨包分發 | +| **唯一異動** | `tkg_builder.py` 加 `--sqlite-output` 參數 | + +--- + +## 6. 好處 + +| | PG-only (現狀) | + SQLite TKG | +|---|:--:|:--:| +| 檔案包自帶 TKG | ❌ | ✅ | +| 離線圖查詢 | ❌ | ✅ | +| Recursive CTE trait | ❌ 無 | ✅ 兩端統一 | +| Desktop/Tauri | ❌ | ✅ 單檔即可 | +| 跨機器轉移 | dump/restore | 複製 .sqlite | diff --git a/docs_v1.0/M4_workspace/2026-05-13_api_3002_test_report.md b/docs_v1.0/M4_workspace/2026-05-13_api_3002_test_report.md new file mode 100644 index 0000000..a494113 --- /dev/null +++ b/docs_v1.0/M4_workspace/2026-05-13_api_3002_test_report.md @@ -0,0 +1,55 @@ +# API 測試報告 — 3002 (Production) vs API 文件 + +**Date**: 2026-05-13 +**From**: M4 + +--- + +## 測試範圍 + +| 環境 | Port | Schema | +|------|:---:|--------| +| Production | 3002 | `public` (chunks, old schema) | +| Development | 3003 | `dev` (chunk, new schema) | + +共測試 18 個 endpoint。 + +--- + +## 結果:18/18 全過 (3002) + +所有 endpoint 返回 200。無 500 或 404。 + +--- + +## DOC vs ACTUAL 格式差異(5 處) + +與 API_REFERENCE_V1.0.0.md 的差異: + +| # | Endpoint | DOC key | ACTUAL key | 影響 | +|---|----------|---------|------------|------| +| 1 | `/files` | `"files"` | `"data"` | Portal/Client 需改 | +| 2 | `/files` | 缺 `success` | `success:true` | | +| 3 | `/resources` | `"resources"` | `"data"` | | +| 4 | `/search/universal` | `"count"` | `"total"` | Portal 需改 | +| 5 | `/search/universal` | 缺分頁 | `page`, `page_size`, `took_ms` | | +| 6 | `/identities` | 缺分頁 | `count`, `page`, `page_size` | | +| 7 | `/faces/candidates` | ✅ | ✅ | 無差異 | + +--- + +## 3002 vs 3003 差異 + +| Endpoint | 3002 | 3003 | 原因 | +|----------|:--:|:--:|------| +| `/file/:uuid/chunks` | 200 | **404** | dev 已 rename chunks→chunk | +| `/search/universal` | 需 `uuid` | 可省略 | 不同 schema 行為 | +| Identity endpoints | 不同資料 | 不同資料 | dev 有 v2.0 含 identity | + +--- + +## 建議 + +1. **API doc 修正**:5 個欄位名錯誤(已回報 M4_workspace/2026-05-12_api_doc_verification.md) +2. **/chunks 不一致**:dev 已移除但 prod 仍可用 — M5 需決定遷移時程 +3. **3002 schema 更新**:public schema 仍需 M5 v1.0 migration diff --git a/docs_v1.0/M4_workspace/2026-05-13_api_test_v2_compatibility.md b/docs_v1.0/M4_workspace/2026-05-13_api_test_v2_compatibility.md new file mode 100644 index 0000000..a091def --- /dev/null +++ b/docs_v1.0/M4_workspace/2026-05-13_api_test_v2_compatibility.md @@ -0,0 +1,33 @@ +# api_test.sh v2.0 相容性回報 + +**Date**: 2026-05-13 +**From**: M4 +**To**: M5 + +--- + +v2.0 匯入後 api_test.sh 37/39。2 個 fail 是測試資料格式變更導致: + +## Fail 1: Identity UUID 404 + +``` +GET /api/v1/identity/2b0ddefe-e2a9-4533-9308-b375594604d5 → 404 +``` + +**原因**:v2.0 重新匯入 identity(428 個 `PERSON_aeed7134_xxx`),舊 UUID 不存在。 + +**建議**:test script 改用 `GET /api/v1/identities` 取得最新 identity UUID,或使用固定 test UUID。 + +## Fail 2: chunk/0-01 404 + +``` +GET /api/v1/file/aeed7134.../chunk/0-01 → 404 +``` + +**原因**:v2.0 chunk ID 改為純數字(`0`, `1`, `2`...),不再使用 `{parent}-{seq}` 格式。 + +**建議**:test script 改用 `chunk/0` 或 `chunk/875`(現有 chunk ID)。 + +## 結論 + +兩個 fail 都是資料格式變更導致,非 bug。api_test.sh 需要更新對應 v2.0 資料格式。 diff --git a/docs_v1.0/M4_workspace/2026-05-13_chunk_vectors_unique_conflict.md b/docs_v1.0/M4_workspace/2026-05-13_chunk_vectors_unique_conflict.md new file mode 100644 index 0000000..3c6bc46 --- /dev/null +++ b/docs_v1.0/M4_workspace/2026-05-13_chunk_vectors_unique_conflict.md @@ -0,0 +1,42 @@ +# public.chunk_vectors unique constraint 衝突 + +**Date**: 2026-05-13 +**From**: M4 +**To**: M5 +**Priority**: High + +--- + +## 問題 + +`public.chunk_vectors` 有兩個 unique constraint: + +```sql +"chunk_vectors_chunk_id_key" UNIQUE CONSTRAINT, btree (chunk_id) -- ❌ 衝突 +"chunk_vectors_chunk_id_uuid_key" UNIQUE CONSTRAINT, btree (chunk_id, uuid) -- ✅ 正確 +``` + +`chunk_vectors_chunk_id_key` 要求 chunk_id 在整個 table 中唯一。但兩個不同 UUID 的檔案可以有相同的 chunk_id(如兩者都用純數字編碼)。 + +### 實際衝突 + +``` +aeed7134: chunk_id=2091 → chunk_vectors (aeed7134, 2091) +23b1c87 : chunk_id=2091 → INSERT FAILS (duplicate unique on chunk_id alone) +``` + +### 影響 + +23b1c87 的 2,340 chunk_vectors 無法匯入 public schema。 + +## 修正 + +```sql +ALTER TABLE public.chunk_vectors DROP CONSTRAINT IF EXISTS chunk_vectors_chunk_id_key; +``` + +`chunk_vectors_chunk_id_uuid_key` 已正確提供 `(chunk_id, uuid)` 複合唯一,不需要單獨的 chunk_id unique。 + +## 驗證 + +修正後 23b1c87 vectors 可成功匯入,aeed7134 vectors 不受影響。 diff --git a/docs_v1.0/M4_workspace/2026-05-13_coordinate_registry_report.md b/docs_v1.0/M4_workspace/2026-05-13_coordinate_registry_report.md new file mode 100644 index 0000000..81b5922 --- /dev/null +++ b/docs_v1.0/M4_workspace/2026-05-13_coordinate_registry_report.md @@ -0,0 +1,64 @@ +# 空間與時間座標系統 Registry — M5 匯報 + +**Date**: 2026-05-13 +**From**: M4 +**To**: M5 +**Ref**: `docs_v1.0/REFERENCE/SPATIAL_COORDINATE_REGISTRY.md` + +--- + +## 完成項目 + +M4 已完成 Momentry Core 所有空間與時間座標系統的清查、校準、文檔化: + +| Registry | 系統數 | 檔案位置 | +|----------|:--:|------| +| 空間 (spatial) | 18 個系統 | `REFERENCE/SPATIAL_COORDINATE_REGISTRY.md` §座標系統清單 | +| 時間 (temporal) | 16 個系統 | 同上 §時間座標系統 | + +--- + +## 一致的部分(✅) + +- **Top-Left 原點**:所有儲存/API/渲染層一致 +- **ffmpeg drawbox/crop**:與 `face_detections` 原點一致,直接 passthrough +- **3D 視覺化 Y 翻轉**:`SpaceTimeCube.vue` / `Face3DViewer.vue` 正確處理 +- **FrameTime**:Rust 核心時間型別定義清晰,`.round()` 統一 + +--- + +## 需 M5 修正的問題(8 項) + +### 🔴 High (3) + +| # | 問題 | 影響 | 位置 | +|---|------|------|------| +| **B1** | **Python `int()` vs Rust `.round()` 不一致** | Python 用 `int` (truncate), Rust 用 `.round()`。1.999s@30fps: Python=59f, Rust=60f。pre_chunks 跨語言傳遞會有 ±1 frame 誤差 | 15+ Python scripts | +| **B2** | **15+ Python scripts hardcoded FPS** | `25.0`, `24.0`, `30.0` 硬寫,非從 probe 讀取。非 25fps 影片 chunk 時間全錯 | `story_pipeline_full.py` 等 | +| — | **Landmark 座標不匹配** | 已另案報告 `2026-05-13_landmark_coord_mismatch.md` | `face.json` | + +### 🟡 Medium (3) + +| # | 問題 | 影響 | 位置 | +|---|------|------|------| +| **B3** | **`register_single_file` vs `probe_by_uuid` total_frames 不一致** | 前者 `as u64` (truncate), 後者 `.floor()` | `server.rs:763,1136` | +| **B4** | **`timestamp_secs` 冗餘欄位** | 與 `frame_number` 可能不同步,建議 deprecated | `face_detections` | +| **B5** | **`format_sec_frame()` 用 `.ceil()` 處理 fps** | 29.97fps 當 30fps 處理 | `time.rs:99` | + +### 🟢 Low (2) + +| # | 問題 | 影響 | 位置 | +|---|------|------|------| +| **B6** | **TypeScript 預設 fps=30** | 非 30fps 影片前端 frame 計算錯誤 | `client.ts:273` | +| **B7** | **`start_time` 只到 0.1s 精度** | 高精度 seek 失真 | `trace_agent_api.rs` | +| **B8** | **`total_frames` 型別不一致** | `Option` / `u64` / `i64` 三種 | DB structs | + +--- + +## 建議 M5 行動 + +1. **Python second→frame 改用 `round()`** — 最高優先,消除 ±1 frame 差異 +2. **Python FPS 改從 probe 讀取** — 不要 hardcode +3. **統一 `total_frames` 計算方式** — 註冊和探測用相同方法 +4. **`timestamp_secs` 標記 deprecated** — 新增資料不再依賴 +5. **更新 `api_test.sh`** — 驗證 frame 計算正確性 diff --git a/docs_v1.0/M4_workspace/2026-05-13_deploy_required_files_fix.md b/docs_v1.0/M4_workspace/2026-05-13_deploy_required_files_fix.md new file mode 100644 index 0000000..6c09174 --- /dev/null +++ b/docs_v1.0/M4_workspace/2026-05-13_deploy_required_files_fix.md @@ -0,0 +1,35 @@ +# deploy.sh — REQUIRED_FILES 未定義 + +**Date**: 2026-05-13 +**From**: M4 +**To**: M5 + +--- + +## Bug + +`deploy.sh` line 52 引用 `${REQUIRED_FILES[@]}`,但變數從未定義: + +```bash +echo "[1/8] Verifying package..." +MISSING=0 +for f in "${REQUIRED_FILES[@]}"; do ← line 52: REQUIRED_FILES is undefined +``` + +執行時報錯:`REQUIRED_FILES[@]: unbound variable` + +## Fix + +在 for loop 前加入定義: + +```bash +REQUIRED_FILES=("data.sql" "file_info.json") +``` + +修正後行號 50-52 應為: + +```bash +echo "[1/8] Verifying package..." +REQUIRED_FILES=("data.sql" "file_info.json") +MISSING=0 +``` diff --git a/docs_v1.0/M4_workspace/2026-05-13_deploy_script_fix.md b/docs_v1.0/M4_workspace/2026-05-13_deploy_script_fix.md new file mode 100644 index 0000000..23700e7 --- /dev/null +++ b/docs_v1.0/M4_workspace/2026-05-13_deploy_script_fix.md @@ -0,0 +1,68 @@ +#!/bin/bash +# Momentry Release Package — Deploy Script +# 原則: 一包一檔,只管內容所涵蓋的檔案 +# Usage: bash deploy.sh + +set -euo pipefail +DIR="$(cd "$(dirname "$0")" && pwd)" +UUID=$(basename "$DIR") +PG_BIN="${PG_BIN:-/Users/accusys/pgsql/18.3/bin}" +DB_NAME="${DB_NAME:-momentry}" +DB_USER="${DB_USER:-accusys}" +DEMO_DIR="${DEMO_DIR:-/Users/accusys/momentry/var/sftpgo/data/demo}" + +echo "=== Momentry Package Deploy ===" +echo "UUID: $UUID" +echo "Time: $(date '+%Y-%m-%d %H:%M:%S')" +echo "" + +# 1. Verify package — only this one file +echo "[1/4] Verifying package..." +REQUIRED_FILES=("data.sql" "file_info.json") +for f in "${REQUIRED_FILES[@]}"; do + if [ ! -f "$DIR/$f" ]; then + echo "ERROR: Missing required file: $f" + exit 1 + fi +done +echo " ✅ Package verified (single file: $UUID)" + +# 2. Import data.sql (already contains videos.chunk.chunk_vectors.face_detections.identities...) +echo "[2/4] Importing DB data..." +"$PG_BIN/psql" -U "$DB_USER" -d "$DB_NAME" -f "$DIR/data.sql" 2>&1 | tail -3 +echo " ✅ Data imported" + +# 3. Set video status to completed (已處理) +echo "[3/4] Setting deployment status..." +"$PG_BIN/psql" -U "$DB_USER" -d "$DB_NAME" -c " + UPDATE videos SET status = 'completed' WHERE file_uuid = '$UUID' +" > /dev/null 2>&1 +echo " ✅ Status set to 'completed'" + +# 4. Copy video to demo dir (only this package's video, not scanning others) +VIDEO_FILE=$(ls "$DIR"/*.mp4 "$DIR"/*.mov "$DIR"/*.avi "$DIR"/*.mkv 2>/dev/null | head -1) +if [ -n "$VIDEO_FILE" ]; then + VIDEO_NAME=$(basename "$VIDEO_FILE") + DEST="$DEMO_DIR/$VIDEO_NAME" + if [ ! -f "$DEST" ]; then + cp "$VIDEO_FILE" "$DEST" + echo "[4/4] Video copied: $VIDEO_NAME" + else + echo "[4/4] Video already present, skipping" + fi +else + echo "[4/4] No video file in package, skipping" +fi + +# Verify +CHUNKS=$("$PG_BIN/psql" -U "$DB_USER" -d "$DB_NAME" -t -A -c "SELECT COUNT(*) FROM dev.chunk WHERE file_uuid='$UUID'" 2>/dev/null || echo "?") +FACES=$("$PG_BIN/psql" -U "$DB_USER" -d "$DB_NAME" -t -A -c "SELECT COUNT(*) FROM dev.face_detections WHERE file_uuid='$UUID'" 2>/dev/null || echo "?") + +echo "" +echo "=== Deploy Complete ===" +echo " UUID: $UUID" +echo " Status: completed" +echo " Chunks: $CHUNKS" +echo " Faces: $FACES" +echo "" +echo "Verify: http://localhost:3003/api/v1/file/$UUID" diff --git a/docs_v1.0/M4_workspace/2026-05-13_deploy_sh_remaining.md b/docs_v1.0/M4_workspace/2026-05-13_deploy_sh_remaining.md new file mode 100644 index 0000000..f9964ae --- /dev/null +++ b/docs_v1.0/M4_workspace/2026-05-13_deploy_sh_remaining.md @@ -0,0 +1,38 @@ +# Re: deploy.sh — 剩餘問題 + +**Date**: 2026-05-13 +**From**: M4 +**To**: M5 + +--- + +已確認 M5 更新版本。修正項目 ✅ 通過: +- chunk_type filter 已移除 +- identity count 驗證已加入 +- TKG 驗證已加入 +- schema prefix (`dev.chunk` 等) 已加入 + +## 三個剩餘問題 + +### 1. ⚠️ 影片複製區塊重複(行 40-65) + +行 40-53 和行 54-65 是同一段邏輯的重複貼上。執行時會複製兩次。 + +**修正**:刪除行 54-65。 + +### 2. ❌ 匯入後未設定 status = 'completed' + +內容包已處理完畢,匯入後該檔案應為「已處理」。目前 deploy.sh 沒有 `UPDATE videos SET status`。 + +**修正**:在 import 後加入: + +```bash +"$PG_BIN/psql" -U "$DB_USER" -d "$DB_NAME" -c \ + "UPDATE dev.videos SET status = 'completed' WHERE file_uuid = '$UUID'" +``` + +### 3. ⚠️ 仍提示啟動 pipeline(行 99-100) + +內容包已為完整處理後狀態,不需要再觸發 pipeline。提示會誤導使用者。 + +**修正**:刪除或改為驗證提示。 diff --git a/docs_v1.0/M4_workspace/2026-05-13_landmark_coord_mismatch.md b/docs_v1.0/M4_workspace/2026-05-13_landmark_coord_mismatch.md new file mode 100644 index 0000000..9f511bf --- /dev/null +++ b/docs_v1.0/M4_workspace/2026-05-13_landmark_coord_mismatch.md @@ -0,0 +1,54 @@ +# Face Landmark Coordinate Mismatch + +**Date**: 2026-05-13 +**From**: M4 +**To**: M5 +**Priority**: High + +--- + +## Discovery + +`face.json` contains landmark data for ALL 70,691 face detections (right_eye 6pts, left_eye 6pts, nose 8pts). However, landmark coordinates do not align with their corresponding bounding boxes. + +## Data + +| Metric | Value | +|--------|------:| +| Total faces | 70,691 | +| Faces with landmarks | 70,691 (100%) | +| Total landmark points | 1,413,820 | +| **Points OUTSIDE bbox** | **1,239,047 (87.6%)** | +| **Faces with ≥1 outside point** | **64,673 (91.5%)** | + +## Example Violation + +``` +frame=4572: + bbox: [x=0, y=870, 325×325] ← bottom of 1080p frame + left_eye: [(-11, 139), (2, 144), ...] ← top of frame + + y difference: bbox y=870, eye y=139 → 731px apart in different regions +``` + +## Likely Causes + +| # | Cause | +|---|-------| +| 1 | Landmarks use **frame-level coordinates**, bbox uses **face-cropped coordinates** | +| 2 | Landmark data and bbox data are **mismatched** (belong to different detections) | +| 3 | Y-axis may be flipped (different detector convention) | + +## Impact + +- ❌ Landmark visualization (eye/nose overlay) will be incorrect +- ❌ Landmark-based identity matching will fail +- ❌ Data unusable for downstream landmark analysis +- ⚠️ Demo showing face landmarks will display incorrectly + +## Required Fix + +1. Unify landmark and bbox coordinate system +2. Re-run face pipeline with corrected landmark output +3. Regenerate `face.json` and PostgreSQL data +4. Re-deliver updated file content package diff --git a/docs_v1.0/M4_workspace/2026-05-13_release_binary_owner.md b/docs_v1.0/M4_workspace/2026-05-13_release_binary_owner.md new file mode 100644 index 0000000..2841611 --- /dev/null +++ b/docs_v1.0/M4_workspace/2026-05-13_release_binary_owner.md @@ -0,0 +1,31 @@ +# Decision #1 更正:Release Binary 由 M5 交付 + +**Date**: 2026-05-13 +**From**: M4 +**To**: M5 +**Ref**: `2026-05-13_release_decision_response.md` + +--- + +## 更正 + +`momentry_core` 的 build pipeline 由 M5 維護。M4 只負責測試與部署,不負責 build。 + +## 修正後的 Release 分工 + +| Item | Owner | 說明 | +|------|:--:|------| +| **`momentry` release binary** | **M5** | M5 編譯交付(version 1.0.0, build 01742b2) | +| `.env.production` config | M4 | `DATABASE_SCHEMA=public` etc. | +| `aeed7134` package | M5 | 檔案內容包 | +| `23b1c87` package | M5 | 檔案內容包 | + +## 其他 5 項決策不變 + +| # | 決策 | Owner | +|---|------|:--:| +| 2 | Schema: public.chunks → public.chunk (RENAME) | M4 執行 | +| 3 | Identity: 保留 prod 15 TMDB + merge dev data | M4 執行 | +| 4 | Downtime: Maintenance mode (503) | M4 執行 | +| 5 | Scope: both aeed7134 + 23b1c87 | M5 交付兩包 | +| 6 | Config: `.env.production` with `DATABASE_SCHEMA=public` | M4 準備 | diff --git a/docs_v1.0/M4_workspace/2026-05-13_release_decision_request.md b/docs_v1.0/M4_workspace/2026-05-13_release_decision_request.md new file mode 100644 index 0000000..a1b57dc --- /dev/null +++ b/docs_v1.0/M4_workspace/2026-05-13_release_decision_request.md @@ -0,0 +1,19 @@ +# Release SOP Decision Log — 請 M5 回覆 + +**Date**: 2026-05-13 +**From**: M4 +**To**: M5 +**Ref**: `REFERENCE/RELEASE_SOP_V2.0.md` §8 + +--- + +SOP 第 8 章 Decision Log 有 6 項待 M5 決定。確認後即可執行 release: + +| # | 問題 | 選項 | +|---|------|------| +| **1** | Production binary 來源? | A. M5 提供 release binary / B. M4 從 source 自編 `cargo build --release` | +| **2** | Schema: public.chunks → public.chunk? | A. 執行 RENAME / B. 保留 `chunks` 另做相容 | +| **3** | Identity merge? | A. 保留 prod 現有 15 TMDB + import dev data / B. 清空 prod 只用 dev data | +| **4** | Downtime 方式? | A. Maintenance mode (503 all requests) / B. Hard stop + restart | +| **5** | Release scope? | A. `aeed7134` only / B. both `aeed7134` + `23b1c87` (YouTube) | +| **6** | Production config? | A. `DATABASE_SCHEMA=public` via .env / B. Hardcoded in binary | diff --git a/docs_v1.0/M4_workspace/2026-05-13_release_fixes_required.md b/docs_v1.0/M4_workspace/2026-05-13_release_fixes_required.md new file mode 100644 index 0000000..68f281f --- /dev/null +++ b/docs_v1.0/M4_workspace/2026-05-13_release_fixes_required.md @@ -0,0 +1,73 @@ +# Release 執行時發現的修正需求 + +**Date**: 2026-05-13 +**From**: M4 +**To**: M5 +**Priority**: High + +--- + +## 發現問題 + +aeed7134 deploy to `public` schema 時發生 3 個 error,均已當場手動修正後通過: + +| # | Error | Root Cause | Fix Applied | +|---|-------|-----------|-------------| +| 1 | `videos_pkey` duplicate | 舊 `public.videos` data 未清除 | `DELETE FROM public.videos WHERE file_uuid = '{uuid}'` | +| 2 | `column "file_uuid" does not exist` | `public.identities` 缺 v2 schema column | `ALTER TABLE public.identities ADD COLUMN file_uuid VARCHAR(64)` | +| 3 | `identity_bindings_pkey` duplicate | 舊 bindings 未清除 | `DELETE FROM public.identity_bindings WHERE identity_id IN (...)` | + +--- + +## 對 deploy.sh 的修正 + +### Step 2 pre-clean 需擴展 + +當前 pre-clean 只刪除 identities: + +```bash +DELETE FROM identities WHERE file_uuid = '{uuid}'; +``` + +需擴展為清除全部受影響 table: + +```bash +DELETE FROM chunk_vectors WHERE uuid = '{uuid}'; +DELETE FROM chunk WHERE file_uuid = '{uuid}'; +DELETE FROM face_detections WHERE file_uuid = '{uuid}'; +DELETE FROM tkg_edges WHERE file_uuid = '{uuid}'; +DELETE FROM tkg_nodes WHERE file_uuid = '{uuid}'; +DELETE FROM identity_bindings WHERE identity_id IN ( + SELECT id FROM identities WHERE tmdb_id IS NOT NULL OR name LIKE 'PERSON_{short_uuid}%' +); +DELETE FROM identities WHERE file_uuid = '{uuid}' OR source = 'tmdb'; +DELETE FROM videos WHERE file_uuid = '{uuid}'; +``` + +--- + +## 對 release SOP 的修正 + +### SOP Step 3 schema migration 需加入 + +```sql +ALTER TABLE identities ADD COLUMN IF NOT EXISTS file_uuid VARCHAR(64); +``` + +(dev schema 已有此 column,v2.0 111614 package identity CSV 包含 file_uuid) + +--- + +## Release 現狀 + +aeed7134 已成功 deploy to public schema: + +| Table | Count | +|-------|------:| +| chunk | 2,407 | +| faces | 70,691 | +| videos | 1 | +| TMDB identities | 15 | +| tkg_nodes | 6,457 | +| tkg_edges | 21,028 | +| status | completed | diff --git a/docs_v1.0/M4_workspace/2026-05-13_release_notes.md b/docs_v1.0/M4_workspace/2026-05-13_release_notes.md new file mode 100644 index 0000000..f84afc9 --- /dev/null +++ b/docs_v1.0/M4_workspace/2026-05-13_release_notes.md @@ -0,0 +1,111 @@ +# Release Notes — v1.0.0 (Production 3002) + +**Date**: 2026-05-13 +**Build**: `301da08` +**Deployed by**: M4 + +--- + +## Release Scope + +### Binaries + +| Binary | Size | Source | +|--------|:----:|--------| +| `momentry` (production) | 21 MB | M5 build | +| `release` CLI | 3.5 MB | M5 build | + +### File Packages + +| Package | UUID | Content | +|---------|------|---------| +| Charade (HD) | `aeed71342a899fe4b4c57b7d41bcb692` | 1920×1080, 25fps | +| Charade (YouTube) | `23b1c872379d4ec06479e5ed39eef4c5` | 640×360, 23.98fps | + +### Data Per Package + +| Table | HD | YouTube | +|-------|:--:|:--:| +| chunk | 2,407 | 2,340 | +| chunk_vectors (768D) | 2,407 | 2,340 | +| face_detections | 70,691 | 70,729 | +| identities (TMDB) | 15 actors | 280 clusters | +| identity_bindings | 18,635 | 18,635 | +| tkg_nodes | 6,457 | 5,776 | +| tkg_edges | 21,028 | 18,847 | + +### TMDB Matched Actors + +| Actor | TMDB ID | Package | +|-------|:------:|:--:| +| Cary Grant | 2638 | HD | +| Audrey Hepburn | 1932 | HD | +| James Coburn | 5563 | HD | +| George Kennedy | 12950 | HD | +| Dominique Minot | 41714 | HD | +| Ned Glass | 18870 | HD | +| Jacques Marin | 26890 | HD | +| Paul Bonifas | 41716 | HD | + +--- + +## Schema Changes + +| Change | Schema | +|--------|--------| +| `chunks` → `chunk` (rename) | public | +| Drop `old_chunk_id`, `chunk_index` | public | +| Add `timestamp_secs` to `face_detections` | public | +| Add `file_uuid` to `identities` | public | +| Drop `chunk_vectors_chunk_id_key` (duplicate unique) | public | + +--- + +## Verification + +### API (all 200) + +| Category | Endpoints | Result | +|----------|-----------|:--:| +| Health | `/health`, `/health/detailed` | ✅ | +| Auth | login, logout | ✅ | +| Files | list, scan, detail, probe | ✅ | +| Chunk | `/file/{uuid}/chunk/{id}` | ✅ | +| Search | universal, frames, visual | ✅ | +| Identities | list, detail, files, chunks | ✅ | +| Face Trace | sortby, faces, 3D | ✅ | +| Media | video, thumbnail, trace video | ✅ | +| Resources | list | ✅ | + +### Database + +| Table | Count | +|-------|------:| +| Files registered | 38 | +| TMDB identities | 15 | +| Total chunks (both files) | 4,747 | +| Total faces (both files) | 141,420 | +| Total TKG nodes | 12,233 | +| Total TKG edges | 39,875 | + +--- + +## Deployment Process + +1. **Backup**: Full `public` schema dump (1.3 GB) +2. **Schema Migration**: `chunks→chunk`, drop deprecated columns, add new columns +3. **Deploy aeed7134**: All 8 tables imported via `sed dev.→public.` per-table SQL files +4. **Deploy 23b1c87**: Fixed `chunk_vectors_chunk_id_key` constraint conflict, all 8 tables imported +5. **Binary Swap**: Old binary stopped, M5 `momentry_v1.0.0` deployed, restarted on 3002 +6. **Verification**: All API endpoints 200, both files queryable + +--- + +## Known Notes + +| Item | Note | +|------|------| +| `/files` status | API hardcodes `"ready"`, does not reflect actual DB status (reported to M5) | +| `/files/scan` | Only scans video extensions (mp4/mov/mkv/avi/webm), misses jpg/png | +| deploy.sh schema | Uses `sed dev.→public.` for public deployment (pending M5 native SCHEMA support) | +| chunk_vectors constraint | `chunk_vectors_chunk_id_key` dropped from public (was preventing multi-file import) | diff --git a/docs_v1.0/M4_workspace/2026-05-13_scan_status_issues.md b/docs_v1.0/M4_workspace/2026-05-13_scan_status_issues.md new file mode 100644 index 0000000..300871f --- /dev/null +++ b/docs_v1.0/M4_workspace/2026-05-13_scan_status_issues.md @@ -0,0 +1,77 @@ +# 3002 Production — Scan 與 Files 狀態驗證 + +**Date**: 2026-05-13 +**From**: M4 + +--- + +## Issue 1: Scan 覆蓋不足 + +| 指標 | 數值 | +|------|:----:| +| Demo 目錄實際檔案數 | **106** | +| Scan 回傳檔案數 | **23** | +| 缺漏 | **83** | + +### 原因 + +`scan_directory_recursive` 只過濾 `["mp4", "mov", "mkv", "avi", "webm"]`(server.rs:1905)。Demo 目錄有大量 `.jpg`、`.png` 檔案(animal1.jpg, animal2.jpg 等)不在過濾清單中。 + +### 影響 + +- Portal FilesView 只顯示 23 個檔案 +- 83 個圖片/非影片檔案完全看不到 +- `.jpg` `.png` 應該加入 allowed_extensions + +--- + +## Issue 2: `/files` 狀態與事實不符 + +| | DB 實際狀態 | API 回報 | +|---|:--:|:--:| +| completed | 3 | — | +| processing | 2 | — | +| pending | 8 | — | +| failed | **23** | — | +| — | — | **ready: 36** | + +### 原因 + +`identity_api.rs:97` hardcodes status: + +```rust +status: "ready".to_string(), // Hardcoded for now +``` + +API 回傳的 `status` 不是從 DB 讀取的,是寫死的。 + +### 影響 + +- Portal FilesView 顯示所有檔案為「已就绪」(綠色 ✅),即使 23 個是 `failed` +- 使用者看不到真實的處理狀態 +- Portal 狀態過濾器(pending/processing/completed)全部無效 + +### 修正 + +```rust +// identity_api.rs — 從 DB 讀取 status +let records = state.db.list_files(page_size, offset).await?; +let data = records.into_iter().map(|r| FileItem { + file_uuid: r.file_uuid, + file_name: r.file_name, + file_path: r.file_path, + status: r.status.unwrap_or("unknown".to_string()), // ← 改這行 +}).collect(); +``` + +--- + +## Issue 3: Scan registered vs /files list 不一致 + +| | Scan | /files list | +|---|:--:|:--:| +| Registered files | 21 | 36 | + +**15 個已註冊檔案不在 Scan 結果中** — 這些檔案在 DB 中有紀錄,但 demo 目錄中對應的實體檔案已不存在(被刪除或移動)。 + +**2 個 Scan 中的檔案未註冊** — `Charade_YouTube_24fps.mp4` 和另一個檔案。它們在目錄中但未在 `public.videos` 中註冊。 diff --git a/docs_v1.0/M4_workspace/2026-05-13_v2_package_issues.md b/docs_v1.0/M4_workspace/2026-05-13_v2_package_issues.md new file mode 100644 index 0000000..e5797f9 --- /dev/null +++ b/docs_v1.0/M4_workspace/2026-05-13_v2_package_issues.md @@ -0,0 +1,110 @@ +# V2.0 檔案內容包 — 問題與建議 + +**Date**: 2026-05-13 +**From**: M4 +**To**: M5 + +--- + +## 問題清單 + +### ❌ Issue 1: TKG 未匯入 PostgreSQL + +TKG 資料(6,457 nodes, 21,028 edges)只存在 SQLite,未匯入 PG。`deploy.sh` 只跑 `data.sql`,沒有 TKG 匯入步驟。 + +```sql +-- PG: 0 tkg_nodes, 0 tkg_edges +-- SQLite: 6457 tkg_nodes, 21028 tkg_edges +``` + +**建議**:`data.sql` 應包含 `COPY dev.tkg_nodes` + `COPY dev.tkg_edges`。 + +--- + +### ⚠️ Issue 2: TMDB 匹配未套用 + +428 個 identity 全部命名為 `PERSON_aeed7134_xx`(原始 clustering 輸出),tmdb_id 全為 NULL。 +HANDOVER_V2.0.md 提到「TMDB matched: Audrey Hepburn 843 traces, Cary Grant 482」,但實際身份表中無對應。 + +| | DOC says | ACTUAL | +|---|---------|--------| +| Audrey Hepburn | 843 traces | 0 | +| Cary Grant | 482 traces | 0 | +| Any tmdb_id | — | 0/428 | + +**trace_to_identity.json 中有 trace→identity 映射**(5,483 records),但 identity 名稱仍是 `PERSON_xxx`。 + +**建議**:TMDB matching step 需在打包前執行,identity 名稱應更新為 TMDB 演員名。 + +--- + +### ⚠️ Issue 3: identity_bindings 數量不一致 + +| 來源 | bindings 數 | +|------|-----------:| +| PG (after import) | 10,966 | +| SQLite | 5,483 | +| HANDOVER doc | 5,483 | + +PG 數量是 SQLite 的 2 倍,因為 `data.sql` 的 COPY 匯入了**跨檔案**的全局 identity_bindings,不是此檔案專屬。 + +**建議**:`data.sql` 匯出時過濾 `WHERE identity_id IN (SELECT id FROM identities WHERE source = 'auto')` 或只匯出此檔案的 binding。 + +--- + +### ⚠️ Issue 4: 只有 sentence chunks + +| chunk_type | v1.0 | v2.0 | +|-----------|-----:|-----:| +| sentence | 4188 | 2407 | +| cut | 1130 | **0** | +| story | 280 | **0** | +| trace | 423 | **0** | + +只匯出了 sentence chunk,缺少 cut/story/trace/visual 類型。 + +**建議**:確認 M5 pipeline 是否已完成 scene/story/trace chunking。若已完成,`data.sql` 應包含所有 chunk_type。 + +--- + +### ❌ Issue 5: Landmark 座標不匹配 + +已另案報告 (`2026-05-13_landmark_coord_mismatch.md`)。 +87.6% 地標點不在 bbox 範圍內。 + +--- + +### ⚠️ Issue 6: SQLite 向量無法直接查詢 + +sqlite-vec 資料表存在(chunk/face/voice embeddings),但需要 `vec0` extension 才能查詢。CLI 無法直接使用。 + +**建議**:包內附 `vec0.dylib` 或提供 `sqlite-vec` 安裝 script。 + +--- + +## 建議優先級 + +| # | Issue | Priority | 說明 | +|---|-------|:--:|------| +| 1 | TKG → PG | 🔴 | 核心資料缺失 | +| 2 | TMDB matching | 🔴 | Identity 無法用於 Portal 顯示 | +| 5 | Landmark mismatch | 🔴 | 資料無法用 | +| 4 | Missing chunk types | 🟡 | 影響功能完整度 | +| 3 | identity_bindings count | 🟡 | 資料不乾淨 | +| 6 | SQLite vec0 query | 🟢 | 可用第三方工具 | + +--- + +## 已確認正常 + +| 項目 | 狀態 | +|------|:--:| +| PG copy 匯入 (6 tables) | ✅ | +| SQLite 結構 + 資料 | ✅ | +| Face coverage 100% | ✅ | +| Face traces 5483 | ✅ | +| Chunk 2407 sentences | ✅ | +| Speaker map 899 | ✅ | +| Video file 存在 | ✅ | +| API 功能 (37/39) | ✅ | +| Demo 21/21 | ✅ | diff --git a/docs_v1.0/M4_workspace/2026-05-14_deploy_force_flag.md b/docs_v1.0/M4_workspace/2026-05-14_deploy_force_flag.md new file mode 100644 index 0000000..caa1954 --- /dev/null +++ b/docs_v1.0/M4_workspace/2026-05-14_deploy_force_flag.md @@ -0,0 +1,35 @@ +# deploy.sh — 新增 --force 參數 + +**Date**: 2026-05-14 +**From**: M4 +**To**: M5 + +--- + +## 變更 + +`deploy.sh` 加入 `--force` 參數,跳過覆寫確認提示。 + +## 使用方式 + +```bash +# 互動式(有舊資料時會詢問確認) +SCHEMA=public bash deploy.sh + +# 強制覆蓋(不詢問,直接取代) +SCHEMA=public bash deploy.sh --force +``` + +## 行為 + +| 情境 | 無 --force | 有 --force | +|------|:--:|:--:| +| 無舊資料 | 直接部署 | 直接部署 | +| 有舊資料 | ⚠️ 顯示警告 + [y/N] 確認 | 🔧 跳過確認,直接取代 | + +## 檔案變更 + +| 檔案 | 說明 | +|------|------| +| `deploy.sh` | L15-16: 新增 `FORCE` 變數 + `--force` 參數解析 | +| `deploy.sh` | L80-82: 確認區塊加入 `FORCE` 跳過邏輯 | diff --git a/docs_v1.0/M4_workspace/2026-05-14_inference_down.md b/docs_v1.0/M4_workspace/2026-05-14_inference_down.md new file mode 100644 index 0000000..215b56c --- /dev/null +++ b/docs_v1.0/M4_workspace/2026-05-14_inference_down.md @@ -0,0 +1,37 @@ +# 2026-05-14_inference_down.md + +## 問題 + +生產環境 (3002) 的 inference engine (gemma4 LLM) 無法連線。 + +## 環境資訊 + +| 項目 | 值 | +|------|-----| +| API Server | localhost:3002 (production) | +| Schema | public | +| 時間 | 2026-05-14 15:50 +08:00 | + +## 現狀 + +### 正常 +- **Local embedding** (embeddinggemma-300m): ✅ 運行中 (port 11436, MPS) +- **Local embedding** (bge-m3-q8_0): ✅ 運行中 (port 8082) +- **Local embedding** (mxbai-embed): ✅ 運行中 (port 8083) +- **CoreML face embedding**: ✅ 運行中 (port 11435) + +### 異常 +- **M5 inference** (gemma4): ❌ 無法連線 + - `http://192.168.110.201:8081` → 無回應 + - `http://192.168.110.201:11434` → 無回應 + +### 影響 +- `/api/v1/stats/inference` 回報兩個 engine 皆 error(但 health check hardcode 了 localhost,未反映 M5 真實端點) +- 生產環境 searchable_chunks: **4 / 15,367** — embedding/summarization pipeline 已停滯 +- 8 筆影片 pending,無法完成處理 + +## 請求 + +1. 確認 M5 上的 gemma4 inference 服務狀態 +2. 重啟 inference 服務(port 8081 或實際使用的端點) +3. 確認後請回覆 `2026-05-14_inference_down_response.md` diff --git a/docs_v1.0/M4_workspace/2026-05-14_pipeline_v2_deploy_report.md b/docs_v1.0/M4_workspace/2026-05-14_pipeline_v2_deploy_report.md new file mode 100644 index 0000000..7580b05 --- /dev/null +++ b/docs_v1.0/M4_workspace/2026-05-14_pipeline_v2_deploy_report.md @@ -0,0 +1,34 @@ +# 165302 包部署回報 — 數量差異 + +**Date**: 2026-05-14 +**From**: M4 +**To**: M5 +**Ref**: `2026-05-14_pipeline_v2_delivery.md` + +--- + +## 部署結果 + +包已成功部署到 `public` schema,aed7134 status = completed。 + +## 數量差異 + +| Metric | Delivery Doc 宣稱 | 實際 Import | 差異 | +|--------|:--------:|:--------:|:----:| +| Face traces | 9,825 | 5,483 | -4,342 | +| TKG edges | 370,310 | 30,987 | -339,323 | +| TKG nodes | 9,900 | 9,900 | ✅ 一致 | +| chunk | — | 2,407 | ✅ | +| faces | — | 70,691 | ✅ | + +TKG edge CSV 只有 30,991 lines,文件大小 8.5MB。與 doc 的 370,310 差距巨大。 + +## 改善項目 + +TMDB 演員從 7 位增加到 **15 位**(新增 Walter Matthau、Thomas Chelimsky、Marc Arian、Claudine Berg、Marcel Bernier、Albert Daumergue、Raoul Delfosse)。 + +## 問題 + +1. `identities_name_key` unique constraint 造成 import 失敗(已手動 DROP 解決) +2. TKG edge 數量與 delivery doc 不符 +3. Face trace 數量與 delivery doc 不符 diff --git a/docs_v1.0/M4_workspace/2026-05-14_schemafix_binary_still_dev.md b/docs_v1.0/M4_workspace/2026-05-14_schemafix_binary_still_dev.md new file mode 100644 index 0000000..dc33d71 --- /dev/null +++ b/docs_v1.0/M4_workspace/2026-05-14_schemafix_binary_still_dev.md @@ -0,0 +1,29 @@ +# schemafix binary — still querying dev + +**Date**: 2026-05-14 +**From**: M4 +**To**: M5 +**Priority**: High + +--- + +## Issue + +`momentry_v1.0.0_schemafix` 仍查詢 `dev.face_detections`,trace 9530 回 0。 + +## Evidence + +| Test | Result | +|------|--------| +| chunk/0 (uses schema::table_name) | ✅ 200 | +| trace/9530/faces | total=0 (DB has 434) | +| trace sortby total_traces | 5,483 (DB has 9,825) | +| Binary build_git_hash | NOT in /health | + +## Likely Cause + +Binary built **before** commit `e8f44d7` (trace_agent_api schema fix). File timestamp is delivery time, not build time. + +## Request + +Rebuild binary from HEAD (includes `e8f44d7`), verify `/health` shows `build_git_hash`, re-deliver as separate file. diff --git a/docs_v1.0/M4_workspace/2026-05-14_trace_schema_hardcode.md b/docs_v1.0/M4_workspace/2026-05-14_trace_schema_hardcode.md new file mode 100644 index 0000000..fcc59a3 --- /dev/null +++ b/docs_v1.0/M4_workspace/2026-05-14_trace_schema_hardcode.md @@ -0,0 +1,42 @@ +# trace_agent_api.rs — dev.face_detections hardcode + +**Date**: 2026-05-14 +**From**: M4 +**To**: M5 +**Priority**: High + +--- + +## Issue + +`src/api/trace_agent_api.rs` 硬編碼 `dev.face_detections`,無視 `DATABASE_SCHEMA` 環境變數。 + +## 影響 + +Production (3002, `DATABASE_SCHEMA=public`) 無法讀取新版 face trace 資料。 + +| DB has | API returns | +|--------|-------------| +| trace 9530 = 434 faces | trace 9530 = 0 | +| total_traces = 9,825 | total_traces = 5,483 (old data) | + +API 永遠查 `dev.face_detections`,不查 `public.face_detections`。 + +## Affected Lines + +| Line | Current | Should Be | +|------|---------|-----------| +| 101 | `FROM dev.face_detections` | `FROM {schema}.face_detections` | +| 110 | `FROM dev.face_detections` | `FROM {schema}.face_detections` | +| 146 | `FROM dev.face_detections` | `FROM {schema}.face_detections` | +| 253 | `FROM dev.face_detections` | `FROM {schema}.face_detections` | +| 271 | `FROM dev.face_detections` | `FROM {schema}.face_detections` | + +## Fix + +```rust +let table = schema::table_name("face_detections"); +sqlx::query(&format!("SELECT ... FROM {} WHERE ...", table)) +``` + +而非硬編碼 `"dev.face_detections"`。 diff --git a/docs_v1.0/REFERENCE/MARKBASE_DESIGN_V2.0.md b/docs_v1.0/REFERENCE/MARKBASE_DESIGN_V2.0.md new file mode 100644 index 0000000..15015b0 --- /dev/null +++ b/docs_v1.0/REFERENCE/MARKBASE_DESIGN_V2.0.md @@ -0,0 +1,919 @@ +--- +document_type: "design" +service: "MOMENTRY_CORE" +title: "MarkBase 設計文件 V2.0" +date: "2026-05-14" +version: "V2.0" +status: "active" +owner: "M4" +created_by: "OpenCode" +tags: + - "markbase" + - "display-engine" + - "virtual-tree" + - "group-share" + - "storage-tier" + - "file-uuid" + - "sqlite" + - "design" +ai_query_hints: + - "查詢 MarkBase 設計文件 V2.0 的內容" + - "MarkBase 虛擬檔案樹如何設計" + - "MarkBase Group Share 怎麼實現" + - "MarkBase file_uuid 規則" + - "MarkBase 儲存層級 Hot Warm Cold 設計" + - "MarkBase 與 Momentry Core 整合方式" + - "MarkBase Display Mode trait 架構" + - "MarkBase 檔案操作 API 設計" +related_documents: + - "REFERENCE/MARKBASE_DESIGN_v1.0.0.md" + - "REFERENCE/file_uuid_spec.md" + - "REFERENCE/SPATIAL_COORDINATE_REGISTRY.md" +--- + +# MarkBase 設計文件 V2.0 + +| 項目 | 內容 | +|------|------| +| 建立者 | M4 / OpenCode | +| 建立時間 | 2026-05-14 | +| 文件版本 | V2.0 | + +--- + +## 版本歷史 + +| 版本 | 日期 | 目的 | 操作人 | 工具/模型 | +|------|------|------|--------|-----------| +| V1.0 | 2026-05-12 | 初版設計(Demo Display + Knowledge Graph) | M4 / OpenCode | DeepSeek V4 Pro | +| V2.0 | 2026-05-14 | 加入檔案樹、Group Share、儲存層級、技術棧、file_uuid 整合 | M4 / OpenCode | DeepSeek V4 Pro | + +--- + +## 概述 + +MarkBase 是 Momentry 生態系的 Display Engine 與檔案管理平台。從 V2.0 起,MarkBase 不再只是 Demo Runner 的 presentation layer,而是升級為具備虛擬檔案樹、跨用戶群組分享、多層級儲存管理、檔案操作 API 的完整平台。 + +**核心設計原則:** + +| 原則 | 說明 | +|------|------| +| 展示層先行 | Demo Display 功能保留,作為 demo runner 的固定顯示視窗 | +| 檔案層次化 | 虛擬檔案樹(Virtual Tree)讓用戶管理自己的資料結構 | +| 儲存層級化 | Hot/Warm/Cold 三級儲存,讓用戶掌控成本 | +| 群組協作 | Group Share 讓團隊內的檔案可讀寫 | +| 單一使用者隔離 | One user = one SQLite,不混用 | + +--- + +## 關鍵術語定義 + +| 術語 | 定義 | +|------|------| +| Virtual Tree | 用戶管理的邏輯檔案樹,非實體路徑 | +| FileNode | 虛擬樹中的節點,包含 label、別名、圖示、顏色 | +| Display Mode | 使用者選擇的檔案展示方式(List / Tree / Small Icon / Large Icon) | +| Group Share | 跨用戶的群組檔案分享(選項 A: Group SQLite) | +| Storage Tier | 三級儲存層級(Hot / Warm / Cold) | +| file_uuid | 32 字元十六進制檔案出生識別符,由 Momentry Core 計算 | +| Exit Record | 檔案移出管理時的留存記錄 | +| Mount | 實體儲存掛載點(NAS、外接硬碟、LTO) | + +--- + +## 1. 架構總覽 + +### 1.1 模組化 Rust 設計 + +``` +markbase/ +├── src/ +│ ├── main.rs # CLI entry point +│ ├── server.rs # axum HTTP server (port 11438) +│ ├── display/ # Display engine (from V1.0) +│ │ ├── mod.rs +│ │ ├── render.rs # .md → HTML (pulldown-cmark) +│ │ ├── highlight.rs # syntax highlighting (syntect) +│ │ ├── mermaid.rs # Mermaid rendering +│ │ └── page.html # core HTML template +│ ├── filetree/ # Virtual file tree (NEW V2.0) +│ │ ├── mod.rs # FileTree struct, init_from_sqlite +│ │ ├── node.rs # FileNode struct +│ │ ├── mode.rs # DisplayMode trait +│ │ ├── modes/ +│ │ │ ├── list.rs # list module (trait impl) +│ │ │ ├── tree.rs # tree module (trait impl, Phase 1) +│ │ │ ├── grid_sm.rs # small icon grid (trait impl) +│ │ │ └── grid_lg.rs # large icon grid (trait impl) +│ │ └── auto_layer.rs # auto-layer rules +│ ├── operations/ # File operations (NEW V2.0) +│ │ ├── mod.rs +│ │ ├── compress.rs # zip / tar +│ │ ├── transfer.rs # copy / move between tiers +│ │ ├── archive.rs # auto-archive logic +│ │ ├── restore.rs # restore from archive +│ │ ├── exit.rs # exit record management +│ │ └── registry.rs # file_registry table +│ ├── groups/ # Group share (NEW V2.0) +│ │ ├── mod.rs +│ │ ├── db.rs # Group SQLite create/open +│ │ ├── merge.rs # ATTACH + cross-DB merge +│ │ └── roles.rs # owner/editor/viewer +│ └── mount/ # Mount management (NEW V2.0) +│ ├── mod.rs +│ ├── tier.rs # Hot/Warm/Cold tier defs +│ └── history.rs # location_history table +``` + +**DisplayMode Trait 設計:** + +```rust +/// 展示模式的統一介面。 +/// 每個模式(List, Tree, Grid)實作此 trait。 +#[async_trait] +pub trait DisplayMode: Send + Sync { + /// 模式名稱(前端使用) + fn name(&self) -> &'static str; + + /// 將 FileTree 轉換為此模式的前端資料 + fn render(&self, tree: &FileTree, user_id: &str) -> Result; + + /// 此模式支援的排序方式 + fn sort_options(&self) -> Vec; + + /// 此模式支援的過濾器 + fn filter_options(&self) -> Vec; +} +``` + +### 1.2 One User = One SQLite + +``` +data/ +├── users/ +│ ├── demo.sqlite # 用戶 demo 的虛擬樹 + 操作記錄 +│ ├── warren.sqlite # 用戶 warren 的虛擬樹 + 操作記錄 +│ └── alice.sqlite # 用戶 alice 的虛擬樹 + 操作記錄 +├── groups/ +│ ├── groups.sqlite # 群組註冊表(group_id → path) +│ ├── 1.sqlite # 群組 1 的共用資料 +│ └── 2.sqlite # 群組 2 的共用資料 +└── system.sqlite # 系統層級資料(掛載點、全域設定) +``` + +| 原則 | 說明 | +|------|------| +| **用戶隔離** | 每個用戶獨立的 SQLite 檔案(user.sqlite) | +| **簡單部署** | 不需 PostgreSQL server,單檔即可 | +| **易於備份** | 複製 `.sqlite` 檔案即可 | +| **Portable** | 隨身碟帶著走,離線可用 | + +### 1.3 Momentry Core 整合(A+B 混合模式) + +``` +┌──────────────────────────────────────────────────────┐ +│ MarkBase │ +│ │ +│ ┌─────────────────┐ ┌─────────────────────────┐ │ +│ │ 模式 A: Crate │ │ 模式 B: HTTP API │ │ +│ │ (momentry_core │ │ (localhost:3003) │ │ +│ │ 作為依賴) │ │ │ │ +│ │ │ │ • file_uuid 驗證 │ │ +│ │ • file_uuid 計算 │ │ • chunk 查詢 │ │ +│ │ • 向量嵌入 │ │ • identity 查詢 │ │ +│ │ • 本地處理 │ │ • trace data │ │ +│ └─────────────────┘ └─────────────────────────┘ │ +│ │ +│ 選擇策略: │ +│ • 輕量運算 → Crate 模式(不啟動 server) │ +│ • 重查詢/伺服器操作 → HTTP API(需 server 運行) │ +└──────────────────────────────────────────────────────┘ +``` + +| 操作 | 模式 | 理由 | +|------|:----:|------| +| file_uuid 計算/驗證 | Crate | 純函數,不需 server | +| SHA256 | Crate | 本地計算 | +| Chunk 查詢(by file_uuid) | HTTP | 需存取 PostgreSQL | +| Identity 查詢 | HTTP | 需存取 PostgreSQL | +| Trace data(時序片段) | HTTP | 需存取 PostgreSQL | +| 向量搜尋(ANN) | HTTP | 需 Qdrant server | +| 文件轉換(soffice) | Crate/CLI | 本地處理 | + +--- + +## 2. 技術棧 + +### 2.1 Crate 依賴 + +| Crate | 用途 | License | +|-------|------|---------| +| axum 0.7 | HTTP server(port 11438) | MIT | +| tokio 1.0 | 非同步 runtime | MIT | +| rusqlite 0.32 | SQLite 客戶端(bundled) | MIT | +| r2d2 / r2d2_sqlite | SQLite 連接池 | MIT/Apache | +| serde / serde_json 1.0 | JSON 序列化 | MIT/Apache | +| sha2 0.10 | SHA256(file_uuid 驗證) | MIT/Apache | +| notify 6.0 | 檔案系統監控(Hot tier) | CC0/MIT | +| zip 2.0 | ZIP 壓縮 | MIT | +| tar 0.4 | TAR 打包(LTO 歸檔) | MIT/Apache | +| walkdir 2.0 | 目錄掃描 | MIT/Unlicense | +| chrono 0.4 | 日期時間 | MIT/Apache | +| tracing 0.1 | 結構化日誌 | MIT | +| pulldown-cmark | Markdown → HTML | MIT | +| syntect | 程式碼語法高亮 | MIT | +| anyhow / thiserror | 錯誤處理 | MIT/Apache | +| once_cell | 延遲初始化 | MIT/Apache | +| async-trait | async trait 支援 | MIT/Apache | + +### 2.2 SQLite 查詢策略 + +| 項目 | 決策 | +|------|:--:| +| Crate | rusqlite(同步 API) | +| 非同步包裝 | `tokio::task::spawn_blocking` | +| 連接池 | r2d2_sqlite | +| WAL 模式 | 啟用(預設) | + +```rust +// axum handler 中的使用模式 +async fn get_tree(State(pool): State) -> Result> { + let tree = tokio::task::spawn_blocking(move || { + let conn = pool.get()?; + let tree = FileTree::load(&conn, user_id)?; + Ok::<_, anyhow::Error>(tree) + }).await??; + + Ok(Json(tree)) +} +``` + +### 2.3 檔案系統監控 + +| 項目 | 決策 | +|------|:--:| +| Crate | notify 6.0(CC0/MIT) | +| 監控範圍 | 僅 Hot tier | +| 不監控 | Warm / Cold tier(變更頻率低) | +| 實作 | `notify::Watcher` + `mpsc::channel` → async stream | + +### 2.4 壓縮引擎 + +| 格式 | Crate | 用途 | +|------|-------|------| +| `.zip` | `zip` crate | 一般壓縮(用戶下載、備份) | +| `.tar.gz` | `tar` + `flate2` crate | LTO 歸檔(Cold tier) | + +不使用外部 CLI(ditto、hdiutil),全部以 Rust crate 實作。 + +### 2.5 檔案傳輸(Transfer Engine) + +#### 雙引擎策略 + +``` +TransferEngine: + ├── Direct 模式(std::fs::copy) + │ 適用:小檔案 (<50MB)、fallback + │ 特點:無外部依賴、簡單可靠 + │ + └── Rsync 模式(rsync CLI) + 適用:大檔案 (>=50MB)、tier 遷移、NAS 鏡像 + 特點:增量傳輸、續傳、校驗和 +``` + +#### 自動選擇邏輯 + +```rust +fn select_mode(file_path: &Path) -> TransferMode { + let size = std::fs::metadata(file_path).map(|m| m.len()).unwrap_or(0); + if size < 50 * 1024 * 1024 { // <50MB + TransferMode::Direct + } else if Command::new("rsync").arg("--version").output().is_ok() { + TransferMode::Rsync + } else { + TransferMode::Direct // rsync 不存在時 fallback + } +} +``` + +#### rsync 適用性分析 + +| 場景 | 工具 | 理由 | +|------|------|------| +| 單小檔複製 (<50MB) | `std::fs::copy` | rsync protocol overhead > 效益 | +| 大檔案遷移 (tier move) | **rsync** | 增量、續傳、校驗和,三合一 | +| Hot ↔ Warm 同一機器 | **rsync** | 大檔案 delta transfer 效益 | +| NAS ↔ NAS 鏡像 | **rsync** | `--delete` 鏡像模式 | +| 打包 .zip/.tar.gz | `zip` / `tar` crate | rsync 不做壓縮打包 | +| 寫 LTO 磁帶 | `tar` crate | rsync 無法寫磁帶 | + +#### rsync CLI 參數 + +| 參數 | 用途 | +|------|------| +| `-a` | archive mode(保留權限、時間戳) | +| `-v` | verbose(進度顯示) | +| `-P` | 等同 `--partial --progress`(續傳 + 進度) | +| `-c` | checksum mode(SHA256 驗證,非 time/size) | +| `-n` | dry-run(遷移前預覽) | +| `--delete` | 鏡像模式(NAS 同步用) | + +### 2.6 Group Share 跨 DB 查詢 + +使用 SQLite `ATTACH DATABASE`: + +```sql +ATTACH DATABASE '/path/to/groups/1.sqlite' AS g; +SELECT f.*, gf.permission +FROM file_registry f +JOIN g.file_registry gf ON f.file_uuid = gf.file_uuid; +``` + +**優勢:** 一行 SQL 解決,Rust 端不需額外合併邏輯。 + +### 2.7 非同步策略 + +``` +axum handler (async) + │ + ├── 快速操作(直接 await) + │ ├── serde_json 序列化 + │ ├── 驗證 + │ └── 記憶體操作 + │ + └── 阻塞操作(spawn_blocking) + ├── rusqlite 查詢 + ├── std::fs 檔案操作 + ├── SHA256 計算 + └── 壓縮/解壓 +``` + +**原則:** axum handler 本身是 async,遇到 rusqlite 或 std::fs 時,一律用 `tokio::task::spawn_blocking` 包裝。 + +--- + +## 3. file_uuid 規範 + +### 3.1 計算公式 + +``` +file_uuid = SHA256(mac_address | birthday | physical_path_at_birth | filename)[0:32] +``` + +詳細規範參見 `REFERENCE/file_uuid_spec.md`。 + +### 3.2 MarkBase 中的使用 + +| 欄位 | 來源 | 說明 | +|------|------|------| +| file_uuid | Momentry Core | MarkBase 不重新計算,直接復用 | +| 驗證 | `is_birth_uuid()` | 長度 32,不含 `_` | +| 關聯 | 主鍵 | `file_registry.file_uuid`、`file_nodes.file_uuid` | + +### 3.3 整合流程 + +``` +Momentry Core MarkBase + (檔案註冊) (匯入) +┌──────────┐ ┌──────────┐ +│ compute_ │ │ INSERT │ +│ birth_ │──── file_uuid ───▶│ INTO │ +│ uuid() │ 32 hex │ file_ │ +│ │ │ registry │ +└──────────┘ │(file_uuid) + └──────────┘ +``` + +--- + +## 4. 虛擬檔案樹 + +### 4.1 FileNode 結構 + +```rust +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct FileNode { + /// 節點唯一 ID(UUIDv4) + pub node_id: String, + + /// 顯示名稱 + pub label: String, + + /// 多語言別名 + pub aliases: Aliases, + + /// 關聯的 file_uuid(Momentry Core 來源) + pub file_uuid: Option, + + /// 父節點 node_id(root 為 None) + pub parent_id: Option, + + /// 子節點列表 + pub children: Vec, + + /// 節點類型 + pub node_type: NodeType, + + /// 自訂圖示(emoji 或 SVG 路徑) + pub icon: Option, + + /// 文字顏色(CSS hex) + pub color: Option, + + /// 背景顏色(CSS hex) + pub bg_color: Option, + + /// 建立時間 + pub created_at: String, + + /// 最後修改時間 + pub updated_at: String, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Aliases { + /// 繁體中文 + pub zh_tw: Option, + /// 英文 + pub en_us: Option, + /// 日文 + pub ja_jp: Option, + /// 韓文 + pub ko_kr: Option, + /// 法文 + pub fr_fr: Option, +} + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] +#[serde(rename_all = "snake_case")] +pub enum NodeType { + /// 虛擬資料夾(用戶建立,不對應實體路徑) + Folder, + /// 實體檔案(指向 file_uuid) + File, + /// 動態層級(auto-layer 產生) + DynamicLayer, +} +``` + +### 4.2 SQLite Schema(user.sqlite) + +```sql +CREATE TABLE IF NOT EXISTS file_nodes ( + node_id TEXT PRIMARY KEY, + label TEXT NOT NULL, + aliases_json TEXT NOT NULL DEFAULT '{}', + file_uuid TEXT, + parent_id TEXT, + children_json TEXT NOT NULL DEFAULT '[]', + node_type TEXT NOT NULL DEFAULT 'file', + icon TEXT, + color TEXT, + bg_color TEXT, + created_at TEXT NOT NULL DEFAULT (datetime('now')), + updated_at TEXT NOT NULL DEFAULT (datetime('now')), + sort_order INTEGER NOT NULL DEFAULT 0, + FOREIGN KEY (file_uuid) REFERENCES file_registry(file_uuid) +); + +CREATE TABLE IF NOT EXISTS file_registry ( + file_uuid TEXT PRIMARY KEY, + original_name TEXT NOT NULL, + file_size INTEGER, + file_type TEXT, + registered_at TEXT NOT NULL, + last_seen_at TEXT, + status TEXT NOT NULL DEFAULT 'active' +); +``` + +### 4.3 Display Modes + +用戶可切換四種展示模式(儲存在 `localStorage.display_mode`): + +| 模式 | 枚舉值 | 說明 | 實作模組 | +|------|--------|------|----------| +| **List** | `list` | 列表檢視:名稱、大小、日期 | `modes/list.rs` | +| **Tree** | `tree` | 樹狀檢視:展開/折疊層級 | `modes/tree.rs`(Phase 1) | +| **Small Icon** | `grid_sm` | 小圖示網格:適合縮圖檢視 | `modes/grid_sm.rs` | +| **Large Icon** | `grid_lg` | 大圖示網格:適合影片預覽 | `modes/grid_lg.rs` | + +每種模式實作 `DisplayMode` trait(參見 §1.1)。 + +### 4.4 多語言別名 + +| 欄位 | 語言 | 用途 | +|------|------|------| +| `zh_tw` | 繁體中文 | 預設語言 | +| `en_us` | 英文 | 國際使用 | +| `ja_jp` | 日文 | 日本用戶 | +| `ko_kr` | 韓文 | 韓國用戶 | +| `fr_fr` | 法文 | 法國/國際用戶 | + +用戶在前端選擇語言後,系統自動顯示對應別名。若該語言的別名不存在,fallback 到 `label`。 + +### 4.5 自動分層規則 + +系統根據預設規則自動為檔案建立虛擬層級: + +| 規則 | 條件 | 層級結構 | +|------|------|----------| +| **by_type** | 相同副檔名 | `Videos/`、`Images/`、`Documents/`、`Audio/`、`Other/` | +| **by_date** | 按建立日期 | `2026/`、`2026/05/`、`2026/05/14/` | +| **by_size** | 按檔案大小 | `<10MB`、`10–100MB`、`100MB–1GB`、`>1GB` | + +由 `auto_layer.rs` 實作,使用 `NodeType::DynamicLayer` 標記。 + +--- + +## 5. 群組分享 + +### 5.1 Group SQLite 架構(選項 A) + +``` +data/groups/ +├── groups.sqlite # 群組註冊表(全域) +│ └── groups( +│ group_id INTEGER PRIMARY KEY, +│ group_name TEXT, +│ db_path TEXT, # 指向 1.sqlite +│ created_by TEXT, # 建立者 user_id +│ created_at TEXT +│ ) +├── 1.sqlite # 群組 1 的共用資料 +└── 2.sqlite # 群組 2 的共用資料 +``` + +### 5.2 Group SQLite Schema + +```sql +-- groups/1.sqlite +CREATE TABLE group_members ( + user_id TEXT NOT NULL, + role TEXT NOT NULL DEFAULT 'viewer', -- owner / editor / viewer + joined_at TEXT NOT NULL DEFAULT (datetime('now')), + PRIMARY KEY (user_id) +); + +CREATE TABLE group_files ( + file_uuid TEXT NOT NULL, + added_by TEXT NOT NULL, + added_at TEXT NOT NULL DEFAULT (datetime('now')), + PRIMARY KEY (file_uuid), + FOREIGN KEY (added_by) REFERENCES group_members(user_id) +); +``` + +### 5.3 跨 DB 查詢(ATTACH) + +```rust +pub fn get_group_files(conn: &Connection, group_id: i64) -> Result> { + let group_db = format!("/data/groups/{}.sqlite", group_id); + conn.execute_batch(&format!("ATTACH DATABASE '{}' AS g", group_db))?; + + let mut stmt = conn.prepare(" + SELECT f.file_uuid, f.original_name, gm.role + FROM main.file_registry f + JOIN g.group_files gf ON f.file_uuid = gf.file_uuid + JOIN g.group_members gm ON gf.added_by = gm.user_id + ")?; + + // ... +} +``` + +### 5.4 角色權限 + +| 角色 | 讀取 | 寫入 | 刪除 | 邀請成員 | +|------|:----:|:----:|:----:|:----:| +| owner | ✅ | ✅ | ✅ | ✅ | +| editor | ✅ | ✅ | ❌ | ❌ | +| viewer | ✅ | ❌ | ❌ | ❌ | + +--- + +## 6. 儲存層級 + +### 6.1 三級定義 + +| 層級 | 符號 | 延遲 | 速度 | 成本 | 典型媒體 | +|------|:----:|------|------|------|----------| +| **Hot** | 🔥 | <10ms | 高速 | 高 | NVMe SSD / 內建硬碟 | +| **Warm** | 🌡️ | 10–500ms | 中等 | 中 | NAS(網路掛載) | +| **Cold** | ❄️ | >1s | 低速 | 低 | LTO 磁帶 / 外接 HDD | + +### 6.2 掛載點設定 + +管理員可設定每個層級的掛載路徑: + +```json +{ + "tiers": { + "hot": ["/Users/accusys/sftpgo/data", "/Volumes/RAID5/projects"], + "warm": ["/Volumes/NAS_Archive"], + "cold": ["/Volumes/LTO_Archive"] + } +} +``` + +### 6.3 自動歸檔規則 + +管理員可設定自動歸檔觸發條件: + +```json +{ + "auto_archive": { + "enabled": true, + "rules": [ + { + "condition": "idle_days > 90", + "action": "move_to_warm", + "schedule": "0 2 * * 0" + }, + { + "condition": "idle_days > 365", + "action": "move_to_cold", + "schedule": "0 3 * * 0" + }, + { + "condition": "tier_hot_usage > 80%", + "action": "move_oldest_to_warm", + "schedule": "0 * * * *" + } + ] + } +} +``` + +### 6.4 file_uuid 層級遷移 + +file_uuid **在遷移過程中不變**。檔案從 Hot 移到 Cold: + +1. 複製檔案到 Cold tier 路徑 +2. 驗證完整性(SHA256) +3. 寫入 `location_history` 記錄新位置 +4. 移除 Hot tier 的原始檔案 +5. `file_registry.last_seen_at` 更新 + +file_uuid 永遠指向 birth 時的 `physical_path_at_birth`(Hot 路徑),不因遷移而改變。 + +### 6.5 location_history 表 + +```sql +CREATE TABLE IF NOT EXISTS location_history ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + file_uuid TEXT NOT NULL, + location TEXT NOT NULL, -- 實際檔案路徑 + tier TEXT NOT NULL, -- hot / warm / cold + moved_at TEXT NOT NULL DEFAULT (datetime('now')), + reason TEXT, + moved_by TEXT, + verified INTEGER DEFAULT 0, -- 完整性驗證通過 + FOREIGN KEY (file_uuid) REFERENCES file_registry(file_uuid) +); + +CREATE INDEX idx_location_history_file_uuid ON location_history(file_uuid); +``` + +查詢目前位置: + +```sql +SELECT location, tier +FROM location_history +WHERE file_uuid = ? +ORDER BY moved_at DESC +LIMIT 1; +``` + +--- + +## 7. 檔案操作 API + +### 7.1 操作總覽 + +| 操作 | API | 說明 | +|------|-----|------| +| **Compress** | `POST /api/v2/files/compress` | 壓縮為 .zip 或 .tar.gz | +| **Transfer** | `POST /api/v2/files/transfer` | 複製/移動到 target tier | +| **Archive** | `POST /api/v2/files/archive` | 歸檔到 Cold tier | +| **Restore** | `POST /api/v2/files/restore` | 從 Cold tier 還原到 Hot tier | +| **Exit** | `POST /api/v2/files/exit` | 從 MarkBase 移除(保留記錄) | + +### 7.2 壓縮 + +```rust +// Compress 請求 +{ + "file_uuids": ["uuid1", "uuid2"], + "format": "zip", // "zip" | "tar.gz" + "output_path": "/path/to/output.zip" +} + +// Compress 回應 +{ + "status": "completed", + "output_path": "/path/to/output.zip", + "file_count": 2, + "compressed_size": 1048576 +} +``` + +### 7.3 Transfer(層級遷移) + +#### 請求/回應 + +```rust +// Transfer 請求 +{ + "file_uuids": ["uuid1"], + "target_tier": "cold", + "target_path": "/Volumes/LTO_Archive/2026/", + "delete_source": false +} + +// Transfer 回應 +{ + "status": "completed", + "file_uuid": "uuid1", + "new_location": "/Volumes/LTO_Archive/2026/uuid1.mp4", + "new_tier": "cold" +} +``` + +#### Transfer Engine 實作流程 + +``` +TransferEngine::execute(source, target, opts) + │ + ├── 1. select_mode(source) + │ │ + │ ├── size < 50MB ──→ DirectMode + │ └── size >= 50MB ──→ RsyncMode (fallback: DirectMode) + │ + ├── 2. preflight (RsyncMode) + │ ├── rsync -an --checksum source/ target/ + │ └── 回傳變更清單,供用戶確認 + │ + ├── 3. transfer + │ │ + │ ├── DirectMode: std::fs::copy + progress callback + │ │ + │ └── RsyncMode: rsync -avP --checksum source target + │ ├── -a archive mode + │ ├── -v verbose (進度) + │ ├── -P --partial (續傳) + --progress (進度) + │ └── -c checksum mode (SHA256 驗證替代 time/size) + │ + ├── 4. verify (RsyncMode) + │ └── rsync -acn source target (dry-run checksum,應為空) + │ + ├── 5. update location_history + │ └── INSERT INTO location_history (file_uuid, location, tier, ...) + │ + └── 6. cleanup + └── if delete_source: remove source file +``` + +#### Rsync vs Direct 選擇 + +| 條件 | 模式 | 原因 | +|------|:----:|------| +| `file_size < 50 MB` | Direct | rsync overhead > 效益 | +| `file_size >= 50 MB` 且 rsync 存在 | Rsync | 增量、續傳、校驗和 | +| `file_size >= 50 MB` 且 rsync 不存在 | Direct | 優雅 fallback | + +### 7.4 Archive / Restore + +Archive 為 Transfer 到 Cold tier 的便捷包裝。 +Restore 為從 Cold tier 還原到 Hot tier 的便捷包裝。 + +```rust +// Restore 請求 +{ + "file_uuid": "uuid1", + "target_path": "/Users/demo/restored/" // 選填,預設為原始 birth path +} + +// Restore 回應 +{ + "status": "completed", + "file_uuid": "uuid1", + "restored_to": "/Users/demo/restored/uuid1.mp4" +} +``` + +### 7.5 Exit 記錄 + +檔案移出 MarkBase 管理時,保留記錄以供審計: + +```sql +CREATE TABLE IF NOT EXISTS exit_records ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + file_uuid TEXT NOT NULL, + original_name TEXT NOT NULL, + exited_at TEXT NOT NULL DEFAULT (datetime('now')), + exited_by TEXT NOT NULL, + reason TEXT, + last_location TEXT, + FOREIGN KEY (file_uuid) REFERENCES file_registry(file_uuid) +); +``` + +```rust +// Exit 請求 +{ + "file_uuid": "uuid1", + "reason": "Project completed, moved to long-term archive" +} + +// Exit 回應 +{ + "status": "completed", + "file_uuid": "uuid1", + "exited_at": "2026-05-14T10:00:00Z" +} +``` + +--- + +## 8. API 參考 + +### 8.1 Tree API + +| 方法 | 路徑 | 說明 | +|------|------|------| +| `GET` | `/api/v2/tree/:user_id` | 取得用戶的完整虛擬樹 | +| `GET` | `/api/v2/tree/:user_id?mode=list` | 以特定模式取得樹 | +| `POST` | `/api/v2/tree/:user_id/node` | 建立新節點 | +| `PUT` | `/api/v2/tree/:user_id/node/:node_id` | 更新節點(label、icon、color、aliases) | +| `DELETE` | `/api/v2/tree/:user_id/node/:node_id` | 刪除節點 | +| `PUT` | `/api/v2/tree/:user_id/node/:node_id/move` | 移動節點(變更 parent) | +| `PATCH` | `/api/v2/tree/:user_id/node/:node_id/alias` | 更新特定語言的別名 | + +### 8.2 File API + +| 方法 | 路徑 | 說明 | +|------|------|------| +| `GET` | `/api/v2/files/:file_uuid` | 取得檔案資訊 | +| `POST` | `/api/v2/files/compress` | 壓縮檔案 | +| `POST` | `/api/v2/files/transfer` | 轉移檔案到 target tier | +| `POST` | `/api/v2/files/archive` | 歸檔到 Cold tier | +| `POST` | `/api/v2/files/restore` | 從 Cold tier 還原 | +| `POST` | `/api/v2/files/exit` | 移出管理 | +| `GET` | `/api/v2/files/:file_uuid/locations` | 查詢位置歷史 | +| `POST` | `/api/v2/files/validate` | 驗證檔案完整性(SHA256) | + +### 8.3 Mount API + +| 方法 | 路徑 | 說明 | +|------|------|------| +| `GET` | `/api/v2/mounts` | 列出所有掛載點 | +| `POST` | `/api/v2/mounts` | 註冊新的掛載點 | +| `PUT` | `/api/v2/mounts/:mount_id` | 更新掛載點 | +| `DELETE` | `/api/v2/mounts/:mount_id` | 移除掛載點 | +| `GET` | `/api/v2/mounts/:mount_id/status` | 查詢掛載點狀態(是否在線、容量) | + +### 8.4 Group API + +| 方法 | 路徑 | 說明 | +|------|------|------| +| `GET` | `/api/v2/groups` | 列出所有群組 | +| `POST` | `/api/v2/groups` | 建立新群組 | +| `DELETE` | `/api/v2/groups/:group_id` | 刪除群組 | +| `POST` | `/api/v2/groups/:group_id/members` | 邀請成員 | +| `DELETE` | `/api/v2/groups/:group_id/members/:user_id` | 移除成員 | +| `PUT` | `/api/v2/groups/:group_id/members/:user_id/role` | 變更角色 | +| `POST` | `/api/v2/groups/:group_id/files` | 分享檔案到群組 | +| `DELETE` | `/api/v2/groups/:group_id/files/:file_uuid` | 從群組移除檔案 | +| `GET` | `/api/v2/groups/:group_id/files` | 列出群組檔案 | + +--- + +## 9. 決策記錄 + +| # | 日期 | 決策 | 理由 | +|---|------|------|------| +| 1 | 2026-05-13 | Rust modular architecture (DisplayMode trait) | 與 Momentry Core 相同生態,模組化利於擴展 | +| 2 | 2026-05-13 | One user = one SQLite | 用戶隔離、簡單部署、檔案可攜 | +| 3 | 2026-05-13 | Group Share → Option A (Group SQLite) | 獨立可攜、不需專屬 server、備份簡單 | +| 4 | 2026-05-13 | Hot/Warm/Cold 三級儲存 | 真實世界檔案管理需求,結合 LTO/NAS/SSD | +| 5 | 2026-05-13 | Auto-archive rules (admin-configurable) | 減少手動管理,idle days + tier 容量觸發 | +| 6 | 2026-05-14 | file_uuid 從 Momentry Core 繼承,不重新計算 | 唯一來源,避免不一致 | +| 7 | 2026-05-14 | file_uuid 不因層級遷移而改變 | 凍結在 birth 時刻,確保身份穩定 | +| 8 | 2026-05-14 | Display mode 儲存在 localStorage | 純 UI 偏好,不需後端儲存 | +| 9 | 2026-05-14 | 檔案操作 API-first | 後端邏輯完成後再加 UI(壓縮、傳輸、歸檔) | +| 10 | 2026-05-14 | Exit records(保留記錄) | 審計需求,不直接刪除記錄 | +| 11 | 2026-05-14 | rusqlite (同步) + spawn_blocking (異步包裝) | 避免整個堆疊都必須 async,保持簡單 | +| 12 | 2026-05-14 | ATTACH DATABASE for Group Share 跨 DB 查詢 | 一行 SQL,不需 Rust 端合併 | +| 13 | 2026-05-14 | notify crate (僅 Hot tier) | 減少資源消耗,Warm/Cold 變更頻率低 | +| 14 | 2026-05-14 | zip + tar crate (不用外部 CLI) | 跨平台,不需 ditto/hdiutil | +| 15 | 2026-05-14 | Momentry Core 整合 A+B 混合模式 | 輕量運算用 crate,重查詢用 HTTP API | + +--- + +## 10. 版本歷史 + +| 版本 | 日期 | 目的 | 操作人 | 工具/模型 | +|------|------|------|--------|-----------| +| V1.0 | 2026-05-12 | 初版設計(Demo Display + Knowledge Graph) | M4 / OpenCode | DeepSeek V4 Pro | +| V2.0 | 2026-05-14 | 虛擬檔案樹、Group Share、儲存層級、技術棧、file_uuid、檔案操作 API | M4 / OpenCode | DeepSeek V4 Pro | diff --git a/docs_v1.0/REFERENCE/MARKBASE_DESIGN_v1.0.0.md b/docs_v1.0/REFERENCE/MARKBASE_DESIGN_v1.0.0.md new file mode 100644 index 0000000..dce2f4f --- /dev/null +++ b/docs_v1.0/REFERENCE/MARKBASE_DESIGN_v1.0.0.md @@ -0,0 +1,730 @@ +# MarkBase — Momentry 專屬 Display Engine 設計方案 v1.0 + +## 產品定位 + +**MarkBase** 是 Momentry 專屬的 Display Engine,擔任 **demo runner 的固定顯示器**。 + +不只是 Markdown 閱讀器,而是一個可控的內容呈現視窗,能夠動態展示: + +| 內容類型 | 展示方式 | +|----------|----------| +| .md 文件 | 渲染為排版清晰的 HTML | +| Mermaid 圖表 | 流程圖、時序圖、ER 圖等 | +| API 回應 JSON | 語法高亮的格式化 JSON | +| 影片 | 嵌入 video player(支援 HLS / MP4)| +| 圖片 | 支援單張或輪播 | +| HTML | 直接內嵌 | +| 文字/程式碼 | syntax highlight | + +**定位一句話:** *Demo runner 的 presentation layer,一個專注、乾淨、可控的內容顯示器。* + +| 面向 | 說明 | +|------|------| +| 願景 | Momentry 生態系的 UI 輸出終端 | +| 核心場景 | demo runner 的固定 display 視窗 | +| 平台 | macOS native(Rust + axum + Tauri WebView)| +| 授權 | Momentry 專屬工具,隨 momentry_core 發布 | + +--- + +## 命名 + +**MarkBase** — Markdown + Display Base + +> 承載所有內容類型的顯示基底。 +> 簡短、好記、產品感。 + +--- + +## 階段規劃 + +### Phase 0:Demo Display(MVP — 立即價值) + +**目標**:取代 md_reader + 影片播放,成為 demo runner 的固定顯示視窗 + +| 功能 | 說明 | +|------|------| +| 文件渲染 | CommonMark + GFM(表格、task list、strikethrough、footnotes)| +| Mermaid 圖表 | 內建渲染(無需 CDN),支援 flowchart / sequence / class / ER / mindmap | +| 程式碼高亮 | syntax highlighting(支援 50+ 語言)| +| JSON 格式化 | API response 自動格式化 + 語法高亮 | +| 影片播放 | MP4 / HLS 嵌入播放(取代 browser 開啟 trace video)| +| 全螢幕 mode | 乾淨無干擾的展示模式,適合 presentation | +| CLI 控制 | 透過 stdin / HTTP 動態載入內容,無需重新啟動 | +| 與 demo runner 整合 | `--display` flag 啟動作為固定顯示視窗 | + +#### Demo Runner 整合流程 + +``` +demo_runner.py --display MarkBase.app (固定顯示視窗) +┌────────────────────┐ ┌────────────────────┐ +│ Step 3: Markdown │ ──HTTP──▶│ 渲染 GUIDE.md │ +│ Step 11: Trace 5 │ ──HTTP──▶│ 播放 trace_5.mp4 │ +│ Step 13: 3D Cube │ ──HTTP──▶│ 顯示 iframe: portal │ +│ Step 22: API resp │ ──HTTP──▶│ 顯示格式化 JSON │ +└────────────────────┘ └────────────────────┘ + (控制端) (顯示端) +``` + +demo runner 透過 `--display` 啟動 MarkBase 作為顯示視窗,然後每步透過 HTTP 推送內容: + +```python +# demo_runner.py 範例 +step_type = "markdown" → POST /display {"type":"md","file":"GUIDE.md"} +step_type = "video" → POST /display {"type":"video","url":"trace_5.mp4"} +step_type = "curl" → POST /display {"type":"json","data":response} +step_type = "browser" → POST /display {"type":"url","url":"..."} +``` + +### Phase 2:Knowledge Base + +**目標**:從閱讀器升級為個人知識庫管理器 + +| 功能 | 說明 | +|------|------| +| 多文件索引 | 監控目錄,自動索引所有 .md | +| 全文檢索 | 跨文件模糊搜尋 + 標題索引 | +| 標籤管理 | YAML frontmatter tags → 標籤雲 | +| Backlinks | 文件間的雙向連結([[wiki-link]])| +| 收藏/書籤 | 標記常用文件 | +| 閱讀歷史 | 最近開啟 / 最近搜尋 | + +### Phase 3:Collaboration + +**目標**:多人協作與發布 + +| 功能 | 說明 | +|------|------| +| 評論/註釋 | 段落層級註解 | +| 版本歷史 | git-based diff 檢視 | +| 靜態站點生成 | .md → 整站 HTML(用於發布)| +| Web 版本 | 瀏覽器可讀(可選自托管)| + +--- + +## CLI 設計(Portal / Demo 使用) + +### 主要命令 + +``` +markbase display ← 啟動顯示視窗(blocking,等待 HTTP 控制) +markbase display "GUIDE.md" ← 啟動並立刻顯示文件 +markbase preview "GUIDE.md" ← (保留) 單次預覽,不回傳控制權 +markbase render "GUIDE.md" ← (保留) 輸出 HTML 到 stdout +``` + +### display — 核心命令(給 demo runner 使用) + +```bash +# 啟動顯示視窗,demo runner 透過 HTTP 控制 +markbase display + +# 指定控制埠(預設 11438) +markbase display --port 11438 + +# 全螢幕模式 +markbase display --fullscreen + +# 啟動時先顯示文件 +markbase display GUIDE.md +``` + +### HTTP 控制 API(display 模式下啟用) + +`markbase display` 啟動後在 `localhost:11438` 監聽控制請求: + +```bash +# 顯示 .md 文件 +curl -X POST http://localhost:11438/display \ + -H "Content-Type: application/json" \ + -d '{"type":"md","file":"/path/to/doc.md","focus":"API 搜尋"}' + +# 播放影片 +curl -X POST http://localhost:11438/display \ + -d '{"type":"video","url":"/path/to/trace.mp4","start":10,"end":30}' + +# 顯示格式化 JSON +curl -X POST http://localhost:11438/display \ + -d '{"type":"json","data":"{\"status\":\"ok\"}"}' + +# 內嵌網頁 +curl -X POST http://localhost:11438/display \ + -d '{"type":"url","url":"http://localhost:1420/trace-viz/..."}' + +# 顯示圖片 +curl -X POST http://localhost:11438/display \ + -d '{"type":"image","url":"/path/to/thumbnail.jpg"}' + +# 控制命令 +curl -X POST http://localhost:11438/control \ + -d '{"cmd":"fullscreen"}' +curl -X POST http://localhost:11438/control \ + -d '{"cmd":"zoom","level":1.5}' +curl -X POST http://localhost:11438/control \ + -d '{"cmd":"close"}' +``` + +### demo_runner.py 整合 + +```python +class MarkBaseDisplay: + """控制 MarkBase 顯示視窗。""" + def __init__(self, port=11438): + self.port = port + self.process = None + + def start(self): + self.process = subprocess.Popen(["markbase", "display", + "--port", str(self.port)], ...) + time.sleep(1) # wait for server + + def show(self, type, **kwargs): + """顯示內容。type: md/video/json/url/image""" + body = {"type": type, **kwargs} + requests.post(f"http://localhost:{self.port}/display", json=body) + + def show_step(self, step): + """根據 demo step 類型自動選擇顯示方式。""" + t = step["type"] + if t == "curl": + self.show("json", data=run_curl(step["cmd"])) + elif t == "browser": + self.show("url", url=step["url"]) + elif t == "markdown": + self.show("md", file=step["cmd"], focus=step.get("focus")) + elif t == "video": + self.show("video", url=step.get("url")) + + +--- + +## 技術架構 + +``` +┌─────────────────────────────────────────┐ +│ MarkBase App │ +├─────────────────┬───────────────────────┤ +│ Frontend │ Engine │ +│ (SwiftUI) │ (Rust core) │ +│ │ │ +│ • 視窗管理 │ • 解析 .md → AST │ +│ • 選單、快捷鍵 │ • Mermaid 渲染 │ +│ • 設定介面 │ • Code highlight │ +│ • 搜尋 UI │ • 全文索引 │ +│ • 目錄樹 │ • 文件監控 │ +└─────────────────┴───────────────────────┘ + │ │ + ▼ ▼ + macOS Native API Rust 二進制 + (WebKit + Swift) (pulldown-cmark + syntect + mermaid-rs) +``` + +### 為什麼 Engine 用 Rust? + +| 原因 | 說明 | +|------|------| +| 效能 | 大型 .md 文件(1000+ 行)瞬間渲染 | +| 無 runtime | 單一二進制,無 Node.js/Python 依賴 | +| 現有基礎 | 可直接重用 md_reader 的 rendering 邏輯 | +| Mermaid 內嵌 | 可用 mermaid-rs crate 替代 CDN | + +### 為什麼 Frontend 用 SwiftUI? + +| 原因 | 說明 | +|------|------| +| Native 體驗 | macOS native 視窗、menu bar、快捷鍵 | +| WebKit 整合 | 直接嵌入 WKWebView 渲染 HTML | +| 系統整合 | Spotlight、QuickLook、分享功能 | +| 效能 | 比 Electron 省 200MB+ 記憶體 | + +--- + +## UI 設計 + +### 主視窗佈局 + +``` +┌────────────────────────────────────────────────┐ +│ Menu Bar: File Edit View Window Help │ +├──────────┬─────────────────────────────────────┤ +│ │ │ +│ 左側欄 │ 主內容區 │ +│ ────── │ ───────────────── │ +│ 📁 文件 │ # 標題 │ +│ ├ README│ 正文... │ +│ ├ Guide│ ```code block``` │ +│ └ API │ 表格 │ +│ │ [Mermaid diagram] │ +│ 目錄 │ │ +│ ────── │ │ +│ • Introduction│ │ +│ • Getting...│ │ +│ • API Ref │ │ +│ │ │ +├──────────┴─────────────────────────────────────┤ +│ Status Bar: 字數 | 段落 | UTF-8 | dark mode toggle│ +└────────────────────────────────────────────────┘ +``` + +### 快捷鍵 + +| 按鍵 | 功能 | +|------|------| +| `Cmd+O` | 開啟 .md 文件 | +| `Cmd+F` | 全文搜尋 | +| `Cmd+Shift+F` | 跨文件搜尋 | +| `Cmd++` / `Cmd+-` | 調整字級 | +| `Cmd+D` | Toggle dark mode | +| `Cmd+B` | 左側目錄 toggle | +| `Cmd+P` | 列印 / PDF 匯出 | +| `Esc` | 關閉搜尋 / 回到瀏覽 | + +--- + +## 目錄結構 + +``` +markbase/ +├── Cargo.toml # Rust core +├── src/ +│ ├── main.rs # CLI entry point +│ ├── render.rs # .md → HTML +│ ├── highlight.rs # Code syntax highlighting +│ ├── mermaid.rs # Mermaid rendering +│ ├── search.rs # Full-text search +│ └── watch.rs # File watcher +├── app/ # SwiftUI app +│ ├── MarkBase.xcodeproj +│ ├── MarkBase/ +│ │ ├── ContentView.swift +│ │ ├── SidebarView.swift +│ │ ├── SearchView.swift +│ │ └── SettingsView.swift +│ └── markbase-cli # Embedded Rust binary +└── docs/ + └── ARCHITECTURE.md +``` + +--- + +## 與現有 md_reader 的差異 + +| 面向 | md_reader | MarkBase | +|------|-----------|----------| +| 語言 | 純 Rust CLI | Rust engine + SwiftUI app | +| 架構 | 單一 main.rs 1134 行 | 模組化 6+ 檔案 | +| 視窗 | 簡陋的 WebKit 視窗 | 完整 SwiftUI + WKWebView | +| 搜尋 | ❌ 無 | ✅ Cmd+F + 跨文件搜尋 | +| 目錄 | ❌ 無 | ✅ 左側 heading tree | +| File watcher | ❌ 無 | ✅ 自動索引目錄 | +| dark mode | ❌ 無 | ✅ 系統跟隨 + 手動 | +| Mermaid | CDN-based | 內建引擎 | +| Code highlight | ❌ 無 | ✅ syntect 50+ 語言 | +| 命名 | 功能描述 | 產品品牌 | + +--- + +## 技術選型記錄 + +> 2026-05-12 新增 + +### 1. 轉檔引擎 + +| 工具 | License | 用途 | +|------|---------|------| +| pandoc 3.9 | GPL 2.0 | MD ↔ DOCX/PPTX/PDF | +| LibreOffice 26.2 | Apache 2.0 | 任何格式 ↔ 任何格式 (headless CLI) | +| mmdc | MIT | Mermaid → SVG/PNG | +| rsvg-convert | LGPL | SVG → PNG | + +### 2. 編輯器選型 + +| 方案 | 決策 | 理由 | +|------|:--:|------| +| CodeMirror 6 | ✅ 選用 | MIT, 190KB gzip, CDN 免 npm, 模組化 | +| Monaco (VS Code) | ❌ | 5MB 太大,需 webpack | +| Ace | ❌ | 維護停滯 | + +### 3. Markdown 生態分析 + +| 工具 | License | 類型 | MarkBase 啟發 | +|------|---------|------|--------------| +| glow | MIT | CLI 渲染 | 保留為獨立 CLI viewer | +| MarkText | MIT | WYSIWYG GUI | 參考 split-pane 編輯/預覽設計 | +| mdcat | MPL 2.0 | CLI | 參考 terminal 圖片渲染 | +| bat | MIT/Apache | CLI | 參考語法高亮策略 | +| mdBook | MPL 2.0 | CLI | 作為靜態文件站匯出格式 | +| MkDocs | BSD | CLI | 備選文件站方案 | +| Obsidian | Proprietary | Desktop PKM | 參考 `[[wiki links]]`、graph view、backlinks | + +### 4. 桌面 vs Web + +| 決策 | 選擇 | 理由 | +|------|:--:|------| +| Web first | ✅ | 任何裝置可用,同一份 HTML/JS/CSS | +| Tauri shell | ✅ 可選 | <10MB, 跨平台 macOS/Win/Linux | +| Electron | ❌ | 300MB 過於肥大 | + +### 5. MarkBase vs Obsidian 定位 + +| | Obsidian | MarkBase | +|------|:--:|:--:| +| 定位 | 個人知識管理 (PKM) | **文件處理引擎 + 編輯器** | +| 資料格式 | .md only | 全格式 (via soffice) | +| 搜尋 | 全文 | RAG + embedding (Qdrant) | +| 後端 | 無 | axum HTTP + PSQL + Qdrant | +| CLI | 無 | ✅ CLI first | +| Pipeline | 無 | ✅ Chunking + LLM pipeline | +| 跨裝置 | 付費 sync | 自建 server 即可 | +| 大小 | ~300MB (Electron) | <10MB (Tauri) | +| 授權 | Proprietary (個人免費) | Momentry 專屬 | + +### 6. CLI 設計 + +``` +markbase display [--port 11438] [FILE] 啟動顯示伺服器 +markbase render [-o output.html] Markdown → HTML +markbase serve 檔案瀏覽 + 編輯器 (計畫中) +``` + +### 7. 架構對比 + +``` +Obsidian: MarkBase: +┌──────────────────────┐ ┌──────────────────────┐ +│ Electron Shell │ │ Tauri / Browser │ +│ ┌────────────────┐ │ │ ┌────────────────┐ │ +│ │ Renderer │ │ │ │ Renderer │ │ +│ │ ├─ CodeMirror │ │ │ │ ├─ CodeMirror │ │ ← 相同 +│ │ ├─ Graph/D3 │ │ │ │ ├─ Mermaid.js │ │ ← 相同 +│ │ ├─ Mermaid.js │ │ 相同 │ │ └─ pulldown │ │ +│ │ └─ MathJax │ │ │ └────────────────┘ │ +│ └────────────────┘ │ │ ┌────────────────┐ │ +│ ┌────────────────┐ │ │ │ Rust Backend │ │ ← MarkBase 獨有 +│ │ Plugin API │ │ │ │ ├─ axum HTTP │ │ +│ │ 1,800+ plugins │ │ │ │ ├─ Embedding │ │ +│ └────────────────┘ │ │ │ ├─ Qdrant ANN │ │ +│ ┌────────────────┐ │ │ │ ├─ pgvector │ │ +│ │ FS Access │ │ │ │ ├─ PG TKG │ │ +│ │ .md files only │ │ │ │ ├─ SQLite TKG │ │ +│ │ └────────────────┘ │ │ │ ├─ sqlite-vec │ │ +│ └──────────────────────┘ │ │ └─ Pipeline │ │ +``` + +### 8. 向量儲存:sqlite-vec + Datasette + +> 2026-05-12 採用 + +#### 選型 + +| 需求 | pgvector (PG) | Qdrant | sqlite-vec | 決策 | +|------|:--:|:--:|:--:|:--:| +| Production API (3003) | ✅ | — | — | pgvector (已有) | +| HNSW ANN 搜尋 | ⚠️ | ✅ | — | Qdrant (已有) | +| Desktop 本機 RAG | ❌ 需裝 PG | ❌ 需 server | ✅ 單檔 | sqlite-vec | +| 檔案包內嵌向量 | ❌ | ❌ | ✅ 隨包分發 | sqlite-vec | +| 離線可用 | ❌ | ❌ | ✅ | sqlite-vec | +| Web UI 查詢 | — | — | via Datasette | Datasette | + +#### sqlite-vec 規格 + +| 屬性 | 值 | +|------|-----| +| License | MIT + Apache 2.0(雙授權) | +| 作者 | Alex Garcia | +| 贊助 | Mozilla Builders + Fly.io + Turso + SQLite Cloud | +| Stars | 7,600+ | +| 語言 | Pure C,零依賴 | +| 大小 | ~200KB `.dylib` | +| ANN 引擎 | exhaustive, IVF, DiskANN | +| Rust binding | `cargo add sqlite-vec` | + +#### Datasette(選配 Web UI) + +| 屬性 | 值 | +|------|-----| +| License | Apache 2.0 | +| 作者 | Simon Willison | +| 定位 | SQLite → Web UI + JSON API | +| Plugins | 154 個 | +| sqlite-vec 插件 | `datasette-sqlite-vec`(同一作者) | + +#### 使用範例 + +```sql +.load ./vec0 + +CREATE VIRTUAL TABLE chunks USING vec0( + embedding float[768], + file_uuid text, + chunk_type text, + text_content text +); + +INSERT INTO chunks VALUES (?, 'uuid-123', 'sentence', 'hello world'); + +SELECT rowid, text_content, distance +FROM chunks WHERE embedding MATCH ? +ORDER BY distance LIMIT 10; +``` + +#### 四層向量架構 + +``` +Production ← Qdrant (HNSW ANN, fast at scale) + ← pgvector (transactional, alongside chunk data) + ↓ backup / export + +Portable ← sqlite-vec (.sqlite single file, package distributable) + ← Datasette (optional Web UI) +``` + +### 9. Qdrant Graph 分析 + +> 2026-05-12 結論:Qdrant **沒有**原生 Graph 功能,是純向量資料庫 + +#### Qdrant 現有功能 + +| 功能 | 說明 | 圖論等級 | +|------|------|:--:| +| **Payload filtering** | 向量搜尋 + JSON 條件過濾 | ⚠️ 偽關聯查詢 | +| **Collection aliases** | 多 collection 聯合查詢 | ⚠️ 基礎 | +| **Hybrid Queries** | 向量 + 關鍵字混合 | ❌ | +| **Qdrant Edge** | 嵌入式向量搜尋 | ❌ 非 Graph | +| **Data Graphs (第三方)** | Neo4j + Qdrant hybrid RAG | ✅ 非原生 | + +#### Payload filtering 的極限 + +可以模擬 1-hop 關係(例如「找 Cary Grant 說話的 chunk」),但不能做真正的 graph traversal: + +```json +// ✅ 1-hop:filter speaker = "Cary Grant" +{"filter": {"must": [{"key": "speaker", "match": {"value": "Cary Grant"}}]}} + +// ❌ 2-hop:graph traversal Qdrant 無法做到 +// "誰跟 Cary Grant 在同一個場景出現?" +// "這些人中誰又跟 Audrey Hepburn 對話?" +``` + +| 限制 | 說明 | +|------|------| +| ❌ 2-hop+ traversal | 無法跨節點關聯查詢 | +| ❌ 邊緣權重/時間 | 無 edge property 概念 | +| ❌ Graph algebra | 無 `shortest_path`, `PageRank` 等演算法 | +| ❌ Cypher/GQL | 無圖查詢語言 | + +#### Momentry TKG 決策 + +| | Qdrant-only | PG TKG | SQLite TKG | Neo4j | +|---|:--:|:--:|:--:|:--:| +| 向量搜尋 | ✅ 原生 | via pgvector | via sqlite-vec | via plugin | +| Graph traversal | ❌ | ✅ CTE | ✅ CTE | ✅ 原生 | +| 2-hop+ 查詢 | ❌ | ✅ | ✅ | ✅ | +| 時間範圍邊緣 | ❌ | ✅ | ✅ | ✅ | +| 部署 | 需 server | 需 PG | **單檔** | 需 Java | +| 檔案包分發 | ❌ | ❌ | ✅ | ❌ | +| 適合規模 | 大 | 中 | 小-中 | 大 | + +#### 架構分工 + +``` +Qdrant → 向量搜尋(ANN)- 核心效能 +PG → TKG 圖查詢(Recursive CTE)- API server +SQLite → TKG 圖查詢(Recursive CTE)- 檔案包/離線 +``` + +--- + +## 亮點:知識圖譜 (Knowledge Graph) + +> 2026-05-12 新增 + +### Obsidian vs MarkBase 圖譜對比 + +| | Obsidian Graph | MarkBase Knowledge Graph | +|------|:--:|:--:| +| 節點來源 | 手動建立的 `.md` 筆記 | AI pipeline 自動產生的 chunks | +| 邊緣來源 | 手寫 `[[wikilinks]]` | **語意相似度**、結構層級、共現關係 | +| 生成方式 | 人工 | **自動**(embedding + clustering) | +| 影片支援 | ❌ | ✅ face traces, speaker graph, scene transitions | +| 實體辨識 | ❌ | ✅ 人臉/說話者/物件/場景 | +| 規模 | 數百節點 | **數萬節點**(chunk 級) | +| 過濾 | 無 | 時間範圍、置信度、chunk type | + +### 圖譜類型 + +#### A. 語意關係圖(Semantic Graph) + +以 embedding 餘弦相似度建立邊緣,相近 chunk 靠近。 + +``` +[Audrey Hepburn 說話] ──0.82── [Cary Grant 回應] + │ │ + │ 0.75 │ 0.78 + ▼ ▼ +[討論離婚原因] ──0.91── [緊張對話場景] +``` + +**演算法**: +1. 取所有 chunk embedding +2. 計算 pairwise cosine similarity +3. 保留 top-K 相似邊(K=5 預設) +4. 用 UMAP/t-SNE → 2D 座標 +5. D3.js force layout 渲染 + +#### B. 結構層級圖(Hierarchy Graph) + +文件 → 章節 → 段落 的三層樹狀結構。 + +#### C. 人物關係圖(Identity Graph) + +基於 face_detections + speaker_assign。 + +``` +Cary Grant ──[對手戲]── Audrey Hepburn + │ │ + │[對話] │[場景共現] + ▼ ▼ +Walter Matthau ────── Ned Glass +``` + +#### D. 時序演進圖(Timeline Graph) + +Chunks 按時間軸排列,場景切換點標記。X 軸 = 時間,Y 軸 = 說話者。 + +### 渲染技術 + +| 層 | 工具 | License | +|----|------|---------| +| 力導向佈局 | D3-force (d3.js v7) | ISC | +| 降維 (UMAP) | umap-js | MIT | +| 2D 繪圖 | Canvas / SVG via D3 | ISC | +| 3D 繪圖 | Three.js | MIT | +| 節點過濾 | Crossfilter / vanilla JS | — | + +### API 設計 + +``` +GET /api/v1/graph/:file_uuid/identity → 人物關係圖資料 +GET /api/v1/graph/:file_uuid/semantic?depth=3 → 語意圖資料 +GET /api/v1/graph/:file_uuid/hierarchy → 結構層級圖 +GET /api/v1/graph/:file_uuid/timeline → 時序圖資料 +``` + +回傳格式: +```json +{ + "nodes": [ + {"id": "chunk_100", "label": "Cary Grant: What's your name?", "group": 3, "x": 0.1, "y": 0.5} + ], + "edges": [ + {"source": "chunk_100", "target": "chunk_104", "weight": 0.82, "type": "semantic"} + ] +} +``` + +### 互動設計 + +| 操作 | 行為 | +|------|------| +| Drag node | 拖曳節點 | +| Click node | 展開 chunk 內容預覽 | +| Scroll | 縮放圖譜 | +| Filter bar | 依 chunk_type / speaker / confidence 過濾 | +| Double-click | 聚焦該節點,展開子圖 | +| Hover edge | 顯示相似度分數 | + +### 圖譜渲染工具選型 + +> 2026-05-12 新增 + +#### 候選工具對比 + +| 工具 | License | 大小 | CDN | 圖論演算法 | 中國社群 | 最佳場景 | +|------|---------|:--:|:--:|:--:|:--:|------| +| **Cytoscape.js** | MIT | ~120KB | ✅ | ✅ BFS/DFS/PageRank | ⚠️ | 複雜網絡圖 | +| D3.js v7 | ISC | ~80KB | ✅ | ❌ 需自寫 | ⚠️ | 任何自訂圖表 | +| ECharts | Apache 2.0 | ~1MB | ✅ | ❌ | ✅ 非常大 | 通用圖表 + 地圖 | +| G6 (AntV) | MIT | ~500KB | ✅ | ✅ 多種佈局 | ✅ 非常大 | 關係圖專用 | +| vis-network | MIT/Apache | ~300KB | ✅ | ❌ | ❌ | 網絡圖 | +| Sigma.js | MIT | ~80KB | ✅ | ❌ | ❌ | WebGL 大圖 (>5000節點) | +| Graphviz | EPL 1.0 | ~3MB | ❌ CLI only | ✅ | ⚠️ | 靜態匯出 SVG/PNG | + +#### 選型過程 + +**第一輪篩選**:排除 CLI-only (Graphviz)、無 CDN、中文社群弱且圖論支援差的 (vis-network, Sigma.js)。 + +剩餘:Cytoscape.js, D3.js, ECharts, G6。 + +**第二輪深度評估**: + +| | Cytoscape.js | D3.js | ECharts | G6 | +|---|:--:|:--:|:--:|:--:| +| 力導向佈局 | ✅ 9 種 | ✅ 自寫 | ✅ 1 種內建 | ✅ 9 種 | +| 複合節點 (compound) | ✅ | ❌ | ❌ | ✅ | +| 圖論演算法 | ✅ 內建 | ❌ | ❌ | ✅ | +| JSON → Graph | ✅ 原生 | ⚠️ 手動 | ⚠️ 手動 | ✅ 原生 | +| TreeGraph | ⚠️ 需擴展 | ✅ | ❌ | ✅ 專用 | +| 大型圖效能 | ⚠️ (>5000會慢) | ✅ | ✅ Canvas | ✅ | +| 互動 API | ✅ 豐富 | ✅ 最靈活 | ✅ | ✅ | +| 零外部依賴 | ✅ | ✅ | ❌ (zrender) | ❌ | + +**最終決策**: + +| 場景 | 選用 | 理由 | +|------|:--:|------| +| 知識圖譜核心 | **Cytoscape.js** | 圖論演算法、fCoSE 佈局、JSON 原生對接、Obsidian/Mermaid 都用 | +| 統計輔助圖表 | **ECharts** | 中文社群大、Apache 背書、長條/圓餅/分佈圖開箱即用 | +| 樹狀層級圖 | **G6 TreeGraph** | 專用 API,文件結構圖最簡潔 | +| 自訂特殊需求 | **D3.js** | 保底方案,任何無法滿足的圖表 | + +#### Cytoscape.js 使用者背書 + +| 組織 | 用途 | +|------|------| +| **Mermaid** | 流程圖/時序圖渲染引擎 | +| **Obsidian** | 知識圖譜 (Graph View) | +| Amazon, Google, Meta, Microsoft | 內部網絡圖視覺化 | +| IBM, Cisco, Tencent, Uber | 網路拓樸視覺化 | +| GitHub | 相依性圖 | + +#### 整合架構 + +``` +MarkBase Knowledge Graph: +┌──────────────────────────────────────┐ +│ 圖譜類型 渲染引擎 │ +│ ───────── ──────── │ +│ 語意關係圖 → Cytoscape.js │ +│ 結構層級圖 → G6 TreeGraph │ +│ 人物關係圖 → Cytoscape.js │ +│ 時序演進圖 → ECharts timeline │ +│ 降維散點圖 → D3.js │ +│ 統計分佈圖 → ECharts │ +│ │ +│ 全部 CDN 載入,無需 npm │ +└──────────────────────────────────────┘ +``` + +### 在 MarkBase 中的整合 + +``` +MarkBase Control Bar: +⏮ ◀ ▶ ⏭ | Graph | Tree | Edit | 🔍 + ↑ + Knowledge Graph View +``` + +--- + +## 開發路線圖 + +| 階段 | 時程 | 交付 | +|------|:----:|------| +| P0 Core rendering | ✅ Done | Rust engine: .md→HTML with Mermaid + AJAX refresh | +| P1 macOS app | ✅ Done | Tauri shell (可選) | +| P2 File tree + Editor | 2-3d | CodeMirror 6 + lazy-load 樹狀瀏覽 + 存檔 | +| P3 Knowledge Graph | 3-5d | Cytoscape.js + G6 + ECharts: 語意/結構/人物關係圖譜 | +| P4 Knowledge base | 3-5d | 多文件索引、全文檢索、backlinks | +| P5 Export | 2d | 轉檔 CLI (md→pdf/docx/pptx) | +| P6 Collaboration | 5-10d | 評論、版本、靜態站點 | diff --git a/docs_v1.0/REFERENCE/RELEASE_NOTES_v1.0.0.md b/docs_v1.0/REFERENCE/RELEASE_NOTES_v1.0.0.md new file mode 100644 index 0000000..f84afc9 --- /dev/null +++ b/docs_v1.0/REFERENCE/RELEASE_NOTES_v1.0.0.md @@ -0,0 +1,111 @@ +# Release Notes — v1.0.0 (Production 3002) + +**Date**: 2026-05-13 +**Build**: `301da08` +**Deployed by**: M4 + +--- + +## Release Scope + +### Binaries + +| Binary | Size | Source | +|--------|:----:|--------| +| `momentry` (production) | 21 MB | M5 build | +| `release` CLI | 3.5 MB | M5 build | + +### File Packages + +| Package | UUID | Content | +|---------|------|---------| +| Charade (HD) | `aeed71342a899fe4b4c57b7d41bcb692` | 1920×1080, 25fps | +| Charade (YouTube) | `23b1c872379d4ec06479e5ed39eef4c5` | 640×360, 23.98fps | + +### Data Per Package + +| Table | HD | YouTube | +|-------|:--:|:--:| +| chunk | 2,407 | 2,340 | +| chunk_vectors (768D) | 2,407 | 2,340 | +| face_detections | 70,691 | 70,729 | +| identities (TMDB) | 15 actors | 280 clusters | +| identity_bindings | 18,635 | 18,635 | +| tkg_nodes | 6,457 | 5,776 | +| tkg_edges | 21,028 | 18,847 | + +### TMDB Matched Actors + +| Actor | TMDB ID | Package | +|-------|:------:|:--:| +| Cary Grant | 2638 | HD | +| Audrey Hepburn | 1932 | HD | +| James Coburn | 5563 | HD | +| George Kennedy | 12950 | HD | +| Dominique Minot | 41714 | HD | +| Ned Glass | 18870 | HD | +| Jacques Marin | 26890 | HD | +| Paul Bonifas | 41716 | HD | + +--- + +## Schema Changes + +| Change | Schema | +|--------|--------| +| `chunks` → `chunk` (rename) | public | +| Drop `old_chunk_id`, `chunk_index` | public | +| Add `timestamp_secs` to `face_detections` | public | +| Add `file_uuid` to `identities` | public | +| Drop `chunk_vectors_chunk_id_key` (duplicate unique) | public | + +--- + +## Verification + +### API (all 200) + +| Category | Endpoints | Result | +|----------|-----------|:--:| +| Health | `/health`, `/health/detailed` | ✅ | +| Auth | login, logout | ✅ | +| Files | list, scan, detail, probe | ✅ | +| Chunk | `/file/{uuid}/chunk/{id}` | ✅ | +| Search | universal, frames, visual | ✅ | +| Identities | list, detail, files, chunks | ✅ | +| Face Trace | sortby, faces, 3D | ✅ | +| Media | video, thumbnail, trace video | ✅ | +| Resources | list | ✅ | + +### Database + +| Table | Count | +|-------|------:| +| Files registered | 38 | +| TMDB identities | 15 | +| Total chunks (both files) | 4,747 | +| Total faces (both files) | 141,420 | +| Total TKG nodes | 12,233 | +| Total TKG edges | 39,875 | + +--- + +## Deployment Process + +1. **Backup**: Full `public` schema dump (1.3 GB) +2. **Schema Migration**: `chunks→chunk`, drop deprecated columns, add new columns +3. **Deploy aeed7134**: All 8 tables imported via `sed dev.→public.` per-table SQL files +4. **Deploy 23b1c87**: Fixed `chunk_vectors_chunk_id_key` constraint conflict, all 8 tables imported +5. **Binary Swap**: Old binary stopped, M5 `momentry_v1.0.0` deployed, restarted on 3002 +6. **Verification**: All API endpoints 200, both files queryable + +--- + +## Known Notes + +| Item | Note | +|------|------| +| `/files` status | API hardcodes `"ready"`, does not reflect actual DB status (reported to M5) | +| `/files/scan` | Only scans video extensions (mp4/mov/mkv/avi/webm), misses jpg/png | +| deploy.sh schema | Uses `sed dev.→public.` for public deployment (pending M5 native SCHEMA support) | +| chunk_vectors constraint | `chunk_vectors_chunk_id_key` dropped from public (was preventing multi-file import) | diff --git a/docs_v1.0/REFERENCE/RELEASE_SOP_V2.0.md b/docs_v1.0/REFERENCE/RELEASE_SOP_V2.0.md new file mode 100644 index 0000000..1e55acd --- /dev/null +++ b/docs_v1.0/REFERENCE/RELEASE_SOP_V2.0.md @@ -0,0 +1,280 @@ +# Release SOP — Dev → Production (3002) + +**Date**: 2026-05-13 +**Version**: 2.0 +**Status**: Draft — M5 Review Required + +--- + +## 1. Prerequisites + +### 必須確認 + +| # | 檢查項 | 方法 | 門檻 | +|:--:|------|------|:--:| +| 1 | Dev (3003) 39/39 API test | `bash api_test.sh` | ✅ 39/39 | +| 2 | 檔案內容包匯入成功 | 9 tables all > 0 | ✅ 通過 | +| 3 | TMDB identities 匹配 | `SELECT * FROM dev.identities WHERE source='tmdb'` | ✅ 7 actors | +| 4 | TKG 到位 | tkg_nodes + tkg_edges > 0 | ✅ 通過 | +| 5 | Release binary 已編譯 | `cargo build --release --bin momentry` | ✅ 成功 | +| 6 | `.env.development` 切換確認 | `DATABASE_SCHEMA=public` | ✅ | + + +## 2. Backup + +### 2.1 Database + +```bash +BACKUP_DIR="/Users/accusys/momentry_core_releases/backup_$(date +%Y%m%d_%H%M)" +mkdir -p "$BACKUP_DIR" + +# Full public schema dump +pg_dump -U accusys -d momentry --schema=public \ + --no-owner --no-acl \ + > "$BACKUP_DIR/public_schema_full.sql" + +# Data-only dump (smaller, faster restore) +pg_dump -U accusys -d momentry --schema=public \ + --data-only --no-owner --no-acl \ + > "$BACKUP_DIR/public_schema_data.sql" + +echo "Backup: $BACKUP_DIR" +``` + +### 2.2 Binary + +```bash +cp /path/to/release/momentry "$BACKUP_DIR/momentry_$(date +%Y%m%d)" +``` + +### 2.3 Release Info + +```bash +echo "Release: v1.0.0" > "$BACKUP_DIR/RELEASE_INFO.txt" +echo "Date: $(date)" >> "$BACKUP_DIR/RELEASE_INFO.txt" +echo "Binary: $(ls -la target/release/momentry)" >> "$BACKUP_DIR/RELEASE_INFO.txt" +echo "Schema: public" >> "$BACKUP_DIR/RELEASE_INFO.txt" +``` + + +## 3. Schema Migration + +### 3.1 Apply Migration + +```sql +-- Rename chunks → chunk (if not already done) +ALTER TABLE IF EXISTS public.chunks RENAME TO chunk; + +-- Drop deprecated columns +ALTER TABLE public.chunk DROP COLUMN IF EXISTS old_chunk_id; +ALTER TABLE public.chunk DROP COLUMN IF EXISTS chunk_index; + +-- Add new columns (v1.0.3+) +ALTER TABLE public.face_detections ADD COLUMN IF NOT EXISTS timestamp_secs float8; +``` + +### 3.2 Verify Schema + +```sql +SELECT column_name, data_type +FROM information_schema.columns +WHERE table_schema = 'public' AND table_name = 'chunk' +ORDER BY ordinal_position; +``` + +Expected: 24 columns, no `old_chunk_id`, no `chunk_index`. + + +## 4. Deploy Package + +### 4.1 Deploy via deploy.sh + +```bash +cd path/to/package/ +PG_BIN="/opt/homebrew/opt/postgresql@18/bin" \ +DB_NAME="momentry" DB_USER="accusys" \ +DATABASE_SCHEMA=public \ +bash deploy.sh +``` + +### 4.2 Manual Verification (after deploy) + +```sql +SELECT 'chunk' as tbl, count(*) FROM public.chunk WHERE file_uuid = '{uuid}'; +SELECT 'identities (tmdb)', count(*) FROM public.identities WHERE source = 'tmdb'; +SELECT 'tkg_nodes', count(*) FROM public.tkg_nodes WHERE file_uuid = '{uuid}'; +SELECT 'tkg_edges', count(*) FROM public.tkg_edges WHERE file_uuid = '{uuid}'; +``` + +Expected: chunk > 0, identities > 0, tkg_nodes > 0, tkg_edges > 0. + +### 4.3 Set Status + +```sql +UPDATE public.videos SET status = 'completed' WHERE file_uuid IN ( + 'aeed71342a899fe4b4c57b7d41bcb692', + '23b1c872379d4ec06479e5ed39eef4c5' +); +``` + + +## 5. Binary Swap & Restart + +### 5.1 Stop Old Server + +```bash +# Graceful stop +pkill -TERM momentry +sleep 5 + +# Force if needed +pkill -9 momentry 2>/dev/null +``` + +### 5.2 Deploy New Binary + +```bash +cp target/release/momentry /path/to/production/binary +chmod +x /path/to/production/binary +``` + +### 5.3 Start New Server + +```bash +DATABASE_SCHEMA=public /path/to/production/binary server --port 3002 +# or with .env +MOMENTRY_SERVER_PORT=3002 DATABASE_SCHEMA=public cargo run --release -- server +``` + +### 5.4 Health Check + +```bash +curl http://localhost:3002/health +# Expected: {"status":"ok","version":"1.0.0","build_git_hash":"..."} +``` + + +## 6. Verification + +### 6.1 API Tests + +```bash +API_KEY="muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69" +HOST="http://localhost:3002" + +# Health +curl "$HOST/health" +curl "$HOST/health/detailed" + +# Files +curl -H "X-API-Key: $API_KEY" "$HOST/api/v1/files?page=1&page_size=5" +curl -H "X-API-Key: $API_KEY" "$HOST/api/v1/files/scan" + +# Search +curl -X POST -H "X-API-Key: $API_KEY" -H "Content-Type: application/json" \ + -d '{"query":"Audrey Hepburn","uuid":"aeed71342a899fe4b4c57b7d41bcb692","limit":3}' \ + "$HOST/api/v1/search/universal" + +# Identities +curl -H "X-API-Key: $API_KEY" "$HOST/api/v1/identities?page=1&page_size=5" + +# Chunk detail (v1.0.3+) +curl -H "X-API-Key: $API_KEY" "$HOST/api/v1/file/aeed71342a899fe4b4c57b7d41bcb692/chunk/0" + +# Face trace +curl -X POST -H "X-API-Key: $API_KEY" -H "Content-Type: application/json" \ + -d '{"sort_by":"face_count","limit":3}' \ + "$HOST/api/v1/file/aeed71342a899fe4b4c57b7d41bcb692/face_trace/sortby" + +# Trace video +curl -o /dev/null -w "%{http_code}" -H "X-API-Key: $API_KEY" \ + "$HOST/api/v1/file/aeed71342a899fe4b4c57b7d41bcb692/trace/1934/video?padding=1" +# Expected: 200 + +# Full test suite +bash api_test.sh +# Expected: 39/39 +``` + +### 6.2 Portal Check + +| Page | URL | Expect | +|------|-----|--------| +| FilesView | `http://localhost:1420/files` | Charade = ✅ 已就绪 | +| SearchView | `http://localhost:1420/search` | File dropdown has Charade | +| Trace View | File → Traces | Face list visible | +| Video Playback | Search → Play | Video plays | + +### 6.3 Demo + +```bash +DEMO_BASE="http://localhost:3002" \ +DEMO_FILE="aeed71342a899fe4b4c57b7d41bcb692" \ +python3 scripts/demo_runner.py API_V1.0.0/DEMO_SCRIPT_v1.0.0.json --auto --speed 0.03 + +# Expected: 21/21 steps passed +``` + + +## 7. Rollback + +### 7.1 Binary Rollback + +```bash +pkill momentry +cp "$BACKUP_DIR/momentry_$(date +%Y%m%d)" /path/to/production/binary +DATABASE_SCHEMA=public /path/to/production/binary server --port 3002 +``` + +### 7.2 Schema Rollback + +```sql +-- Rename back if needed +ALTER TABLE public.chunk RENAME TO chunks; +``` + +### 7.3 Data Rollback + +```bash +psql -U accusys -d momentry < "$BACKUP_DIR/public_schema_full.sql" +``` + +### 7.4 Downtime Estimation + +| Step | Est. Time | +|------|:--------:| +| Backup | 2 min | +| Schema migration | 1 min | +| Package deploy | 5-10 min | +| Binary swap | 1 min | +| Verification | 5 min | +| **Total** | **~15-20 min** | + + +## 8. Decision Log + +| # | Decision | Owner | Decision | +|---|----------|:--:|------| +| 1 | Production binary source | M5 | M5 提供 release binary or M4 自編譯 | +| 2 | Schema: rename chunks→chunk? | M5 | M5 決定 public schema 結構 | +| 3 | Identity merge strategy | M5 | Keep prod 15 TMDB + merge with dev data? | +| 4 | Downtime window | M5 | 維護模式 (403 all) or hard stop? | +| 5 | Release scope | M5 | `aeed7134` only or both HD + YouTube? | +| 6 | .env / config for public schema | M5 | Production binary reads `DATABASE_SCHEMA=public` | + +--- + +## Appendix: Release Checklist + +- [ ] Dev 3003 passed all tests (39/39 + demo 21/21) +- [ ] Production old binary backed up +- [ ] Production DB full backup +- [ ] Schema migration SQL ready +- [ ] Package deployed to public schema +- [ ] Status set to completed +- [ ] New binary deployed +- [ ] Server restarted on 3002 +- [ ] Health check returns ok +- [ ] 39/39 API tests pass on 3002 +- [ ] Portal shows files correctly +- [ ] Demo runs 21/21 on 3002 diff --git a/docs_v1.0/REFERENCE/file_uuid_spec.md b/docs_v1.0/REFERENCE/file_uuid_spec.md new file mode 100644 index 0000000..e4f658c --- /dev/null +++ b/docs_v1.0/REFERENCE/file_uuid_spec.md @@ -0,0 +1,418 @@ +--- +document_type: "spec" +service: "MOMENTRY_CORE" +title: "file_uuid 規格文件" +date: "2026-05-14" +version: "V1.0" +status: "active" +owner: "M4" +created_by: "OpenCode" +tags: + - "file-uuid" + - "birth-uuid" + - "sha256" + - "storage" + - "identity" + - "naming-convention" + - "markbase" +ai_query_hints: + - "查詢 file_uuid 規格文件的內容" + - "file_uuid 計算公式是什麼" + - "file_uuid 與 birth_uuid 的關係" + - "如何產生 file_uuid" + - "file_uuid 路徑凍結規則" + - "MAC 位址優先級定義" + - "file_uuid 跨層級遷移如何處理" + - "file_uuid vs uuid 命名規範" +related_documents: + - "REFERENCE/MARKBASE_DESIGN_V2.0.md" + - "REFERENCE/SPATIAL_COORDINATE_REGISTRY.md" +--- + +# file_uuid 規格文件 + +| 項目 | 內容 | +|------|------| +| 建立者 | M4 / OpenCode | +| 建立時間 | 2026-05-14 | +| 文件版本 | V1.0 | + +--- + +## 版本歷史 + +| 版本 | 日期 | 目的 | 操作人 | 工具/模型 | +|------|------|------|--------|-----------| +| V1.0 | 2026-05-14 | 建立 file_uuid 規範文件 | M4 / OpenCode | DeepSeek V4 Pro | + +--- + +## 概述 + +本文件定義 Momentry Core 生態系中 **file_uuid** 的統一規格。file_uuid 是一個檔案出生時產生的不變標識符,用於跨系統(Momentry Core、MarkBase)的唯一檔案識別。 + +**核心原則:** + +- file_uuid 在檔案首次註冊(birth)時計算一次,之後永不改變。 +- file_uuid 包含位置訊息(路徑),作為身份的一部分。 +- 同一檔案移動到不同路徑會產生不同的 file_uuid(新位置 = 新身份)。 +- file_uuid 從 Momentry Core 繼承,MarkBase 直接復用。 + +--- + +## 關鍵術語定義 + +| 術語 | 定義 | +|------|------| +| file_uuid | 檔案出生標識符,32 字元十六進制字串 | +| birth_uuid | 與 file_uuid 同義,強調「出生時計算」的語意 | +| birthday | 檔案首次註冊時的 ISO 8601 時間戳,固定不變 | +| physical_path_at_birth | 檔案出生時的規範化絕對路徑 | +| MAC address | 本機內建網路介面的 MAC 位址(en0 > en1 > en2) | +| SHA256 | SHA-256 雜湊函數 | + +--- + +## 1. 計算公式 + +### 1.1 公式 + +``` +file_uuid = SHA256(mac_address | birthday | physical_path_at_birth | filename)[0:32] +``` + +分隔符為 `|`(pipe 字元)。 + +### 1.2 成分說明 + +| 成分 | 格式 | 範例 | 說明 | +|------|------|------|------| +| mac_address | `xx:xx:xx:xx:xx:xx` | `a1:b2:c3:d4:e5:f6` | 本機內建網路介面(非外接) | +| birthday | ISO 8601 + 時區 | `2026-04-27T22:00:00+08:00` | 檔案首次註冊時間戳 | +| physical_path_at_birth | 絕對路徑 | `/Users/demo/raw/video.mp4` | 去除尾端 `/` | +| filename | 純檔名 | `video.mp4` | 包含副檔名 | + +### 1.3 計算範例 + +輸入: +```text +mac: a1:b2:c3:d4:e5:f6 +birthday: 2026-04-27T22:00:00+08:00 +path: /Users/demo/raw +file: video.mp4 +``` + +組合字串: +```text +a1:b2:c3:d4:e5:f6|2026-04-27T22:00:00+08:00|/Users/demo/raw|video.mp4 +``` + +SHA256: +```text +e5f7a1b2c3d4... (64 字元) +``` + +取前 32 字元: +```text +e5f7a1b2c3d4e5f6a7b8c9d0e1f2a3b4 +``` + +### 1.4 Rust 實作(momentry_core) + +```rust +use sha2::{Digest, Sha256}; + +pub fn compute_birth_uuid( + mac_address: &str, + birthday: &str, + path: &str, + filename: &str, +) -> String { + let key = format!( + "{}|{}|{}|{}", + mac_address, + birthday, + path.trim_end_matches('/'), + filename + ); + let hash = Sha256::digest(key.as_bytes()); + hex::encode(hash)[0..32].to_string() +} +``` + +參考路徑:`src/core/storage/uuid.rs:141` + +--- + +## 2. 路徑規範 + +### 2.1 路徑來源 + +`physical_path_at_birth` 來自檔案首次被系統發現時的位置。在 Momentry Core 中,這通常是 SFTPGo 用戶的家目錄: + +``` +/Users/accusys/sftpgo/data/demo/raw/video.mp4 +``` + +### 2.2 路徑正規化規則 + +| 規則 | 說明 | +|------|------| +| 絕對路徑 | 必須使用絕對路徑(`/` 開頭) | +| 去除尾端 `/` | `trim_end_matches('/')` | +| 不解引用符號連結 | 路徑是記錄值,不解析 symlink | +| 不處理 `..` | 路徑是記錄值,不正規化 `..` | +| 保留大小寫 | macOS 預設不區分大小寫,但路徑保留原始大小寫 | + +### 2.3 路徑凍結原則 + +file_uuid 計算後,`physical_path_at_birth` 凍結不變。 + +| 情境 | file_uuid 行為 | +|------|---------------| +| 檔案移到同層級不同目錄 | **不變**(file_uuid 已凍結) | +| 檔案複製到另一路徑 | **新生產**(新位置 = 新 file_uuid) | +| 檔案從 Hot 移到 Cold tier | **不變**(路徑改變,但 file_uuid 仍指向原始 birth 路徑) | +| 檔案重新命名 | **不變**(filename 已凍結在 birth 時) | + +--- + +## 3. MAC 位址獲取規範 + +### 3.1 優先級 + +| 優先級 | 介面 | 說明 | +|:------:|------|------| +| 0 | en0 | Wi-Fi(通常是內建最優先) | +| 1 | en1 | 第二內建網路介面 | +| 2 | en2 | 第三內建網路介面 | +| 3 | enN (N>=3) | 其他 en 開頭介面 | +| 100 | 其他 | 非 en 開頭介面(最後才用) | + +### 3.2 排除規則 + +| 排除條件 | 理由 | +|----------|------| +| MAC = `00:00:00:00:00:00` | 無效 MAC | +| MAC = `ff:ff:ff:ff:ff:ff` | 廣播位址 | +| 外接 Thunderbolt/USB 網卡 | 非內建 | +| 可拆卸的硬體 | 換機器後會改變 | + +### 3.3 Fallback + +若所有介面都不符合,回傳 `00:00:00:00:00:00` 作為 fallback。 + +### 3.4 實作方式 + +使用 `ifconfig -a` 解析 `ether` 或 `lladdr`(macOS)。 + +參考路徑:`src/core/storage/uuid.rs:61` + +--- + +## 4. Birthday 格式規範 + +### 4.1 格式 + +``` +YYYY-MM-DDTHH:MM:SS±HH:MM +``` + +例如:`2026-04-27T22:00:00+08:00` + +### 4.2 時間來源 + +使用系統時鐘(`chrono::Local::now()`),在檔案首次註冊時記錄。 + +### 4.3 精確度 + +秒級(不含毫秒)。 + +### 4.4 穩定性要求 + +- birthday 一旦設定,永不改變。 +- 即使是同一天重複註冊同一檔案,birthday 也不變(已存在則跳過)。 +- 若因故需要重新計算 file_uuid(不建議),必須使用原始 birthday,而非當前時間。 + +--- + +## 5. 格式規範 + +### 5.1 長度 + +固定 32 字元,16 進制(0-9, a-f)。 + +### 5.2 範例 + +``` +e5f7a1b2c3d4e5f6a7b8c9d0e1f2a3b4 +384b0ff44aaaa1f1 +aeed71342a899fe4b4c57b7d41bcb692 +``` + +### 5.3 驗證函數 + +```rust +pub fn is_birth_uuid(uuid: &str) -> bool { + uuid.len() == 32 && !uuid.contains('_') +} +``` + +參考路徑:`src/core/storage/uuid.rs:159` + +--- + +## 6. 命名規範 + +### 6.1 統一使用 `file_uuid` + +| 命名 | 狀態 | 說明 | +|------|:----:|------| +| `file_uuid` | ✅ **統一使用** | 所有新程式碼、API、資料庫欄位 | +| `uuid` | ❌ 廢棄 | V3.x 命名,不再使用 | +| `UUID` | ❌ 廢棄 | 不使用大寫變體 | +| `video_uuid` | ❌ 廢棄 | V3.x 命名,改為 `file_uuid` | +| `birth_uuid` | ⚠️ 同義詞 | 內部函數命名可保留,對外一律 `file_uuid` | + +### 6.2 資料庫欄位 + +```sql +-- 所有包含 file_uuid 的表格 +file_uuid VARCHAR(32) NOT NULL +``` + +### 6.3 API 欄位 + +```json +{ + "file_uuid": "aeed71342a899fe4b4c57b7d41bcb692" +} +``` + +--- + +## 7. 跨系統整合 + +### 7.1 Momentry Core + +- 計算:`compute_birth_uuid()` @ `src/core/storage/uuid.rs:141` +- 使用位置:`src/core/ingestion.rs`(檔案註冊時) +- 資料庫:`videos.file_uuid`, `files.file_uuid`, `chunk.file_uuid`, `file_identities.file_uuid` 等 + +### 7.2 MarkBase + +- **復用 Momentry Core 的 file_uuid**,不重新計算。 +- MarkBase 的 `file_registry` 表中,`file_uuid` 來自 Momentry Core。 +- MarkBase 的 SQLite 資料庫使用 `file_uuid` 作為檔案的主鍵關聯。 + +### 7.3 整合模式 + +``` +Momentry Core (PostgreSQL) MarkBase (SQLite) +┌──────────────────────┐ ┌──────────────────────┐ +│ videos.file_uuid │───copy───▶│ file_registry.file_uuid │ +│ files.file_uuid │ │ file_nodes.file_uuid │ +│ chunk.file_uuid │ │ │ +└──────────────────────┘ └──────────────────────┘ +``` + +MarkBase 不重新計算 file_uuid,而是從 Momentry Core 匯入。 + +--- + +## 8. 唯一性分析 + +### 8.1 碰撞機率 + +32 字元 hex = 128 bits。 + +- 單一機器上的檔案數:< 10^6 +- 生日悖論:n=10^6 在 2^128 空間中的碰撞機率 ≈ 1.5 × 10^-27 +- **結論:實際上不可能碰撞。** + +### 8.2 跨機器唯一性 + +不同機器的 MAC 不同 → file_uuid 不同(即使路徑和生日相同)。 + +### 8.3 同檔案不同路徑 + +同一檔案從 `/demo/video.mp4` 複製到 `/demo/archive/video.mp4`: +- MAC 相同、生日不同(或相同)、路徑不同 → file_uuid 不同 +- **結論:不同路徑被視為不同檔案。** + +--- + +## 9. 儲存層級遷移 + +### 9.1 遷移情境 + +檔案從 Hot tier 遷移到 Cold tier: + +``` +Hot: /Volumes/RAID5/projects/video.mp4 +Cold: /Volumes/LTO_Archive/2026/video.mp4 +``` + +### 9.2 file_uuid 行為 + +file_uuid **不變**。它永遠指向 birth 時的 `physical_path_at_birth`(Hot 路徑)。 + +### 9.3 位置追蹤 + +遷移後,實際位置記錄在 `location_history` 表中(由 MarkBase 管理),與 file_uuid 分離: + +```sql +CREATE TABLE location_history ( + file_uuid TEXT NOT NULL, + location TEXT NOT NULL, -- 目前實際路徑 + tier TEXT NOT NULL, -- hot/warm/cold + moved_at TEXT NOT NULL, -- 遷移時間 + reason TEXT +); +``` + +### 9.4 檔案不在原位時的處理 + +查詢 `location_history` 取最新記錄,取得 `location` 欄位的目前路徑。若無記錄,則使用 birth 路徑。 + +--- + +## 10. 相容性 + +### 10.1 向前相容(V3.x → V4.0) + +| V3.x 欄位 | V4.0 對應 | +|-----------|-----------| +| `video_uuid` | `file_uuid`(概念對齊,公式不同) | + +### 10.2 資料遷移 + +V3.x 使用 `compute_uuid()`(僅路徑+檔名),V4.0 使用 `compute_birth_uuid()`(路徑+檔名+MAC+birthday)。 + +- 已是相同的 32 字元格式 +- 不變更已存在的 file_uuid(資料庫不回溯更新) +- 新註冊的檔案使用新公式 + +### 10.3 舊 UUID 格式 + +`compute_uuid()` 產生的 32 字元 hex 字串與 `compute_birth_uuid()` 在格式上完全相容(都是 32 hex),驗證函數 `is_birth_uuid()` 對兩者都會返回 `true`。 + +--- + +## 11. 反模式與禁止事項 + +| 禁止 | 理由 | +|------|------| +| ❌ 使用 `uuid` 命名欄位 | 模糊不清,無法區分是 file_uuid 還是 identity_uuid | +| ❌ 基於當前路徑重新計算 file_uuid | 破壞身份穩定性 | +| ❌ 使用相對路徑 | 不同工作目錄產生不同結果 | +| ❌ 排除 MAC 位址 | 跨機器唯一性無法保證 | +| ❌ 依賴檔案內容雜湊 | 內容可能改變,且計算成本高 | +| ❌ 使用人類可讀的 UUID (如 UUIDv4) | 缺乏位置編碼語意 | + +--- + +## 版本資訊 + +- 版本: V1.0 +- 建立日期: 2026-05-14