Skip to content

Release v1.40.0 - #962

Merged
hitalin merged 13 commits into
mainfrom
develop
Aug 4, 2026
Merged

hitalin merged 13 commits into
mainfrom
develop

Conversation

@hitalin

@hitalin hitalin commented Aug 4, 2026

Copy link
Copy Markdown
Collaborator

変更一覧(v1.39.0 → v1.40.0)

ユーザー可視の変化

  • home ∩ social 等の複数タイムラインに属するノートが、両方のカラムのキャッシュ表示に出る(後勝ち付け替えの解消 — 放置して復帰するとTLカラムに別公開範囲ノート混入 #651 の主因)
  • ストリーミング由来のアンテナ / チャンネル / ロール / リストのノートが初めてオフライン読み出しに乗る(キー不一致による孤児化の解消)
  • リスト TL の WS リアルタイム配信が直る(userList チャンネル名修正)。vmimi-relay 等フォーク独自 TL の WS 購読も修正
  • polling モードでアンテナ / チャンネル / ロールが更新されるように
  • 復帰時にキャッシュ表示が誤って恒久削除される事象が減る(ノート検証が「削除確認済み」と「通信エラー」を区別)
  • カラムクエリ: キャッシュ遡り検索がカラムの所属タイムラインだけを対象に(別種別が混ざらない)/ オンラインの「もっと読む」でもマッチ率が低いクエリでカラムが埋まる / 複数 ⚡ クエリでもキャッシュ検索が有効 / @(note) { ... } 形が 🐢 降格でも動く / セーフモードでクエリフィルタが停止
  • アンテナ / クリップ / リストを削除したとき、対応するキャッシュ所属も掃除される

更新時の注意(リリースノート転記用)

  • 初回起動時に DB スキーマ移行(V6)が自動で走る。 既存のノート・検索インデックス・アカウント・チャットはすべて保持される。キャッシュ 100 万件規模で 1 分前後のブロック + 一時的に DB サイズの最大 2 倍弱の空きディスクが必要。途中で強制終了しても DB は壊れない(次回起動で再試行)
  • 移行後の DB は旧バージョンでは開けない(DB 自体は無傷)。ダウングレードの可能性がある場合は、更新前に設定 → バックアップの取得を推奨
  • 同一マシンで notecli CLI / daemon / notetui / notebot を併用している場合は、それぞれ対応版(notecli v0.8.1+ / notetui・notebot の最新 main)に更新すること

🤖 Generated with Claude Code

hitalin and others added 6 commits August 4, 2026 08:22
notecli rev を 1f68a91 (V6 note_timelines) に bump し、cross-repo 影響の
全 18 項目を適用する。

backend:
- fetch 系 7 コマンド + api_get_timeline を ingest_notes + TimelineKey に置換
  (キー手組みの format! を型レベルで排除)
- QueryKey の antenna/channel/role/mentions kind を
  Timeline { account_id, key: canonical } に折り畳み (dedup の二重表現を解消)。
  query_open は削除 (無検証キーの裏口・フロント呼び出しゼロ)
- 購読 4 コマンドを subscribe_notes(TimelineKey) に統合
- キャッシュ整合フック: unfavorite / clip 除去 → remove_membership、
  antenna 更新 / list メンバー除去 → clear_timeline、
  エンティティ削除の後始末用に api_clear_timeline_cache を新設
- api_verify_notes: NO_SUCH_NOTE 確認時のみ miss 判定する
  VerifyNotesResult { verified, missing } に変更 — 復帰直後の通信エラーで
  キャッシュを恒久削除する誤爆 (原症状「復帰時に薄くなる」の一因) を修正
- api_delete_cached_note に account_id、api_get_cached_timeline_before に
  beforeNoteId (keyset cursor ペア)
- cache 系長時間コマンドを spawn_blocking 化、DB open 失敗時に
  nd:backend-fatal を emit

front:
- キー手組みを src/utils/columnCacheKey.ts に集約し既存バグ 4 件を修正
  (list snapshot キー / specified snapshot キー / role ミラー欠落 /
  tl 未設定時のミラー null)
- guest TL 強制変換を deckStore.updateColumn で永続化
- before 遡り 2 箇所を (createdAt, id) の同一ソースペア渡しに
- useNoteList の deleted イベントに accountId 注入
- verify-purge を missing のみ削除に追随 (+ 症状起点テスト)
- エンティティ削除成功後に apiClearTimelineCache (死にバケット掃除)
- cacheEviction に perTimelineLimit、bindings.ts / openapi.json 再生成

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
feat: notecli 実体/所属分離 (V6) への追随
package.json / src-tauri/Cargo.toml / tauri.conf.json / Cargo.lock /
openapi.json を 1.40.0 に同期。あわせて notecli の rev を v0.8.1
(272edae) に更新し、v0.8.0 のレビュー指摘修正 (TimelineKey の path
traversal 対策・per-timeline トリムの writer lock 解放・HTTP エラーの
safe_message 化) を取り込む。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
chore: bump version to 1.40.0 / notecli を v0.8.1 に更新
カラムクエリのキャッシュ遡り検索 (#783 Phase 3) はタイムライン種別で
絞っていなかった — notecli#30 以前は所属が後勝ち上書きで母集合を保証できず、
種別で絞ると取りこぼしになるため「混ざる側」に倒した妥協だった
(068bb21 が notecli#30 待ちと明文化)。

実体/所属分離が入ったので、notecli の scan_cached_notes に追加された
scope (notecli#57) を配線して妥協を解消する:

- qir_search_cache に timeline_key (canonical, nullable) を追加。
  parse 失敗は Err で顕在化、null は従来の全体走査
- フロントは loadMoreFromCache の searchable 分岐で columnCacheKey 由来の
  cacheKey を渡す — 別種別のノートが検索結果に混ざらなくなる
- notecli rev を 203d9d8 (scan scope 追加) に bump

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…uery

feat(column-query): キャッシュ検索をカラムの所属バケットで絞る
@github-actions

github-actions Bot commented Aug 4, 2026

Copy link
Copy Markdown

API surface diff

外部アプリ向け API 面 (src-tauri/openapi.json / src/bindings.ts) が変更されています。
互換性への影響 (#709) をレビューしてください。

src-tauri/openapi.json | 14 ++++++--
 src/bindings.ts        | 90 ++++++++++++++++++++++++++++++++++++--------------
 2 files changed, 77 insertions(+), 27 deletions(-)
Full diff
diff --git a/src-tauri/openapi.json b/src-tauri/openapi.json
index 82883e5..b55f9c3 100644
--- a/src-tauri/openapi.json
+++ b/src-tauri/openapi.json
@@ -6,7 +6,7 @@
     "license": {
       "name": "MIT"
     },
-    "version": "1.39.0"
+    "version": "1.40.0"
   },
   "paths": {
     "/api": {
@@ -1121,7 +1121,7 @@
           {
             "name": "tl_type",
             "in": "path",
-            "description": "Timeline type: home | local | social | global",
+            "description": "Timeline type: home | local | social | global (or fork-specific basic timelines like bubble)",
             "required": true,
             "schema": {
               "type": "string"
@@ -1170,6 +1170,16 @@
               }
             }
           },
+          "400": {
+            "description": "Invalid or non-basic timeline key",
+            "content": {
+              "application/json": {
+                "schema": {
+                  "$ref": "#/components/schemas/ApiErrorResponse"
+                }
+              }
+            }
+          },
           "401": {
             "description": "Unauthorized",
             "content": {
diff --git a/src/bindings.ts b/src/bindings.ts
index 5a7639c..d697552 100644
--- a/src/bindings.ts
+++ b/src/bindings.ts
@@ -88,14 +88,20 @@ async qirValidate(query: QirQuery) : Promise<QirValidation> {
  * 偽陰性を出さない規則に従うので (不変条件 (b))、FTS で落ちたノートが
  * 本来マッチするということはない。
  * 
+ * `timeline_key` (canonical 文字列) を渡すと当該バケット所属のみを母集合に
+ * する。実体/所属分離 (notecli#30) 以前は所属が後勝ち上書きで種別絞りが
+ * 取りこぼしになるため全体走査しかなかったが、その妥協は解消済み。
+ * null は従来どおりアカウントの全キャッシュを走査する。
+ * 
  * 走査上限に達したら打ち切って継続カーソルを返す。呼び出し側は必要なだけ
- * 繰り返す (一度の呼び出しで巨大キャッシュを読み切らせない)。
+ * 繰り返す (一度の呼び出しで巨大キャッシュを読み切らせない)。カーソルは
+ * 同じ timeline_key の続き読みにのみ使うこと。
  *
  * @see src-tauri/src/commands/column_query.rs
  */
-async qirSearchCache(accountId: string, query: QirQuery, limit: number | null, maxScannedRows: number | null, cursor: QirSearchCursor | null) : Promise<Result<QirSearchResult, { code: string; message: string; apiCode: string | null }>> {
+async qirSearchCache(accountId: string, query: QirQuery, timelineKey: string | null, limit: number | null, maxScannedRows: number | null, cursor: QirSearchCursor | null) : Promise<Result<QirSearchResult, { code: string; message: string; apiCode: string | null }>> {
     try {
-    return { status: "ok", data: await TAURI_INVOKE("qir_search_cache", { accountId, query, limit, maxScannedRows, cursor }) };
+    return { status: "ok", data: await TAURI_INVOKE("qir_search_cache", { accountId, query, timelineKey, limit, maxScannedRows, cursor }) };
 } catch (e) {
     if(e instanceof Error) throw e;
     else return { status: "error", error: e  as any };
@@ -277,7 +283,7 @@ async apiUpdateUserSetting(accountId: string, key: string, value: boolean) : Pro
 }
 },
 /** @see src-tauri/src/commands/timeline.rs */
-async apiGetTimeline(accountId: string, timelineType: TimelineType, options: TimelineOptions | null) : Promise<Result<NormalizedNote[], { code: string; message: string; apiCode: string | null }>> {
+async apiGetTimeline(accountId: string, timelineType: string, options: TimelineOptions | null) : Promise<Result<NormalizedNote[], { code: string; message: string; apiCode: string | null }>> {
     try {
     return { status: "ok", data: await TAURI_INVOKE("api_get_timeline", { accountId, timelineType, options }) };
 } catch (e) {
@@ -747,9 +753,25 @@ async apiGetCachedTimeline(accountId: string, timelineType: string, limit: numbe
 }
 },
 /** @see src-tauri/src/commands/timeline.rs */
-async apiDeleteCachedNote(noteId: string) : Promise<Result<null, { code: string; message: string; apiCode: string | null }>> {
+async apiDeleteCachedNote(accountId: string, noteId: string) : Promise<Result<null, { code: string; message: string; apiCode: string | null }>> {
     try {
-    return { status: "ok", data: await TAURI_INVOKE("api_delete_cached_note", { noteId }) };
+    return { status: "ok", data: await TAURI_INVOKE("api_delete_cached_note", { accountId, noteId }) };
+} catch (e) {
+    if(e instanceof Error) throw e;
+    else return { status: "error", error: e  as any };
+}
+},
+/**
+ * エンティティ削除 (antenna / clip / list — フロントは汎用 api_request で
+ * サーバー削除する) の後始末: 死にバケットの membership を破棄する。
+ * これが無いと削除済みエンティティの membership が sweep 対象外のまま
+ * per-account cap まで残留する (issue notecli#30 仕様 v5 §6-7)。
+ *
+ * @see src-tauri/src/commands/timeline.rs
+ */
+async apiClearTimelineCache(accountId: string, timelineKey: string) : Promise<Result<number, { code: string; message: string; apiCode: string | null }>> {
+    try {
+    return { status: "ok", data: await TAURI_INVOKE("api_clear_timeline_cache", { accountId, timelineKey }) };
 } catch (e) {
     if(e instanceof Error) throw e;
     else return { status: "error", error: e  as any };
@@ -757,12 +779,10 @@ async apiDeleteCachedNote(noteId: string) : Promise<Result<null, { code: string;
 },
 /**
  * Bulk-verify cached notes against the server.
- * Returns a map of note_id → fresh NormalizedNote for notes that still exist.
- * Missing notes (404) are omitted from the result.
  *
  * @see src-tauri/src/commands/timeline.rs
  */
-async apiVerifyNotes(accountId: string, noteIds: string[]) : Promise<Result<Partial<{ [key in string]: NormalizedNote }>, { code: string; message: string; apiCode: string | null }>> {
+async apiVerifyNotes(accountId: string, noteIds: string[]) : Promise<Result<VerifyNotesResult, { code: string; message: string; apiCode: string | null }>> {
     try {
     return { status: "ok", data: await TAURI_INVOKE("api_verify_notes", { accountId, noteIds }) };
 } catch (e) {
@@ -771,9 +791,9 @@ async apiVerifyNotes(accountId: string, noteIds: string[]) : Promise<Result<Part
 }
 },
 /** @see src-tauri/src/commands/timeline.rs */
-async apiGetCachedTimelineBefore(accountId: string, timelineType: string, before: string, limit: number | null) : Promise<Result<NormalizedNote[], { code: string; message: string; apiCode: string | null }>> {
+async apiGetCachedTimelineBefore(accountId: string, timelineType: string, before: string, beforeNoteId: string | null, limit: number | null) : Promise<Result<NormalizedNote[], { code: string; message: string; apiCode: string | null }>> {
     try {
-    return { status: "ok", data: await TAURI_INVOKE("api_get_cached_timeline_before", { accountId, timelineType, before, limit }) };
+    return { status: "ok", data: await TAURI_INVOKE("api_get_cached_timeline_before", { accountId, timelineType, before, beforeNoteId, limit }) };
 } catch (e) {
     if(e instanceof Error) throw e;
     else return { status: "error", error: e  as any };
@@ -2004,6 +2024,9 @@ async exportDb() : Promise<Result<boolean, { code: string; message: string; apiC
  * Import notecli.db from a user-chosen file via open dialog.
  * Replaces the current database file. Caller should relaunch the app afterwards
  * so that Rust re-opens the new DB with a fresh connection.
+ * 
+ * 注意: V6 (note_timelines) 適用済みの DB は旧バージョンのアプリへ持ち込めない
+ * (refinery の missing migration で open 不能。DB 自体は無傷)。
  *
  * @see src-tauri/src/commands/utility.rs
  */
@@ -2638,7 +2661,7 @@ async aiMigrateProviderToVault(provider: string, name: string, baseUrl: string,
 }
 },
 /** @see src-tauri/src/query_runtime.rs */
-async querySubscribeTimeline(accountId: string, timelineType: TimelineType, listId: string | null) : Promise<Result<QuerySnapshot, { code: string; message: string; apiCode: string | null }>> {
+async querySubscribeTimeline(accountId: string, timelineType: string, listId: string | null) : Promise<Result<QuerySnapshot, { code: string; message: string; apiCode: string | null }>> {
     try {
     return { status: "ok", data: await TAURI_INVOKE("query_subscribe_timeline", { accountId, timelineType, listId }) };
 } catch (e) {
@@ -2710,15 +2733,6 @@ async querySubscribeChatRoom(accountId: string, roomId: string) : Promise<Result
 }
 },
 /** @see src-tauri/src/query_runtime.rs */
-async queryOpen(key: QueryKey) : Promise<Result<QuerySnapshot, { code: string; message: string; apiCode: string | null }>> {
-    try {
-    return { status: "ok", data: await TAURI_INVOKE("query_open", { key }) };
-} catch (e) {
-    if(e instanceof Error) throw e;
-    else return { status: "error", error: e  as any };
-}
-},
-/** @see src-tauri/src/query_runtime.rs */
 async querySetRuntimeState(queryId: string, state: QueryRuntimeState) : Promise<Result<QuerySnapshot, { code: string; message: string; apiCode: string | null }>> {
     try {
     return { status: "ok", data: await TAURI_INVOKE("query_set_runtime_state", { queryId, state }) };
@@ -3121,13 +3135,18 @@ export type EmojiChangeKind = "added" | "updated" | "deleted"
  */
 export type EvictionConfig = { 
 /**
- * 各アカウントごとの note 上限。`None` なら無制限。
+ * 各アカウントごとの note (entity) 上限。`None` なら無制限。
  */
 perAccountLimit: number | null; 
 /**
  * `cached_at` の TTL (日)。`None` なら無期限保持。
  */
-ttlDays: number | null }
+ttlDays: number | null; 
+/**
+ * バケット (account_id × timeline_key) ごとの所属行上限。`None` なら無制限。
+ * トリムは membership とその対象限定の orphan entity のみを消す。
+ */
+perTimelineLimit: number | null }
 /**
  * EXIF 1 フィールド。tag はタグ名 (例: "DateTimeOriginal", "GPSLatitude")。
  */
@@ -3534,7 +3553,14 @@ updates: NoteUpdate[] }
  * the frontend receives a discriminated union; every variant carries `id`.
  */
 export type QueryItem = ({ kind: "note" } & NormalizedNote) | ({ kind: "notification" } & NormalizedNotification) | ({ kind: "chatMessage" } & ChatMessage)
-export type QueryKey = { kind: "timeline"; account_id: string; timeline_type: TimelineType; list_id: string | null } | { kind: "antenna"; account_id: string; antenna_id: string } | { kind: "channel"; account_id: string; channel_id: string } | { kind: "role"; account_id: string; role_id: string } | { kind: "mentions"; account_id: string } | { kind: "notifications"; account_id: string } | { kind: "chatUser"; account_id: string; other_id: string } | { kind: "chatRoom"; account_id: string; room_id: string }
+export type QueryKey = 
+/**
+ * notes 系購読の統一キー。`key` は `TimelineKey` の canonical 文字列
+ * (home / antenna:{id} / channel:{id} / role:{id} / user-list:{id} / mentions)。
+ * 折り畳まないと同一購読が 2 通りのキー表現を持ち dedup が破れる。
+ * mentions は識別子としてのみここに属し、購読自体は main チャンネル経由。
+ */
+{ kind: "timeline"; account_id: string; key: string } | { kind: "notifications"; account_id: string } | { kind: "chatUser"; account_id: string; other_id: string } | { kind: "chatRoom"; account_id: string; room_id: string }
 export type QueryReadModelSnapshot = { queryId: string; revision: number; 
 /**
  * Note ids in display order (newest first). 消費側は JS noteStore から
@@ -3657,7 +3683,6 @@ export type StreamStatusEvent = { accountId: string; state: StreamConnectionStat
 export type SummaryData = { title: string | null; description: string | null; icon: string | null; sitename: string | null; thumbnail: string | null; medias: string[]; player: Player | null; url: string; sensitive: boolean }
 export type TimelineFilter = { withRenotes: boolean | null; withReplies: boolean | null; withFiles: boolean | null; withBots: boolean | null; withSensitive: boolean | null }
 export type TimelineOptions = { limit?: number; sinceId: string | null; untilId: string | null; filters?: TimelineFilter | null; listId: string | null }
-export type TimelineType = string
 /**
  * 「確認なしで使う」のプラグイン個体単位の記憶。
  * 
@@ -3850,6 +3875,21 @@ ok: boolean;
  * 失敗時の理由 (SSRF / timeout / DNS など)。secret は含まない。
  */
 error: string | null }
+/**
+ * `api_verify_notes` の結果。
+ */
+export type VerifyNotesResult = { 
+/**
+ * サーバー上に現存が確認できたノート (note_id → 最新の NormalizedNote)
+ */
+verified: Partial<{ [key in string]: NormalizedNote }>; 
+/**
+ * サーバーが NO_SUCH_NOTE を返した = 削除が確認できたノート id。
+ * 通信エラー・レート制限・タイムアウトはどちらにも含まれない (生存扱い) —
+ * 復帰直後の不安定なネットワークでキャッシュを誤って恒久削除しないため
+ * (issue notecli#30 仕様 v5 §6-8)。
+ */
+missing: string[] }
 
 /** tauri-specta globals **/
 

@github-actions github-actions Bot added rust Pull requests that update rust code javascript Pull requests that update javascript code labels Aug 4, 2026
@coderabbitai

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Notedeck 1.40.0 introduces canonical timeline keys across Tauri commands, query subscriptions, cache storage, and deck columns. Cache operations gain account and timeline scoping, stable pagination cursors, structured verification results, safe-mode query bypasses, and updated query evaluation.

Changes

Timeline cache and query migration

Layer / File(s) Summary
Canonical timeline query contracts
src-tauri/src/query_runtime.rs, src-tauri/src/commands/timeline.rs, src/bindings.ts, src/core/*, src-tauri/openapi.json
Timeline queries now use canonical string keys. Dedicated query variants and queryOpen were removed.
Structured cache operations and cleanup
src-tauri/src/commands/*, src-tauri/src/lib.rs, src/bindings.ts
Cache ingestion, reads, deletion, verification, invalidation, and blocking cleanup now use structured timeline keys and account scope.
Shared account-aware column cache keys
src/utils/columnCacheKey.ts, src/components/deck/*, src/composables/useEntityCrud.ts
Deck columns and entity cleanup use shared account-aware cache-key generation.
Frontend cache pagination and verification
src/composables/useNoteColumn*.ts, src/composables/useNoteList.ts, src/components/window/*
Pagination preserves timestamp and note-ID cursors. Query searches receive timeline scopes. Verification removes only confirmed missing notes.
Safe mode and query evaluation
src/composables/useNoteColumn.ts, src/services/columnQuery/*, src/utils/safeMode.ts
Safe mode disables column query evaluation. Note field nullability and reference-function evaluation were updated.
Release and compatibility metadata
package.json, src-tauri/Cargo.toml, src-tauri/tauri.conf.json, src-tauri/src/commands/utility.rs
Versions changed to 1.40.0. The pinned notecli revision and V6 database compatibility documentation were updated.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant DeckColumn
  participant useNoteColumn
  participant TauriCacheAPI
  participant CacheDatabase
  DeckColumn->>useNoteColumn: request notes using column cache key
  useNoteColumn->>TauriCacheAPI: search cached notes with timeline scope
  TauriCacheAPI->>CacheDatabase: scan scoped cache bucket
  CacheDatabase-->>useNoteColumn: cached notes and cursor
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 34.48% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly identifies the v1.40.0 release, which matches the primary versioning and release changes.
Description check ✅ Passed The description thoroughly covers the changes, user-visible effects, and upgrade notes, but it omits the required test checklist and explicit Why and Screenshots sections.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch develop

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src-tauri/src/commands/timeline.rs`:
- Around line 162-176: Await the blocking cache invalidation task before
reporting success, while retaining warning-only handling when cleanup fails. In
src-tauri/src/commands/timeline.rs lines 162-176, update the antenna mutation
flow around update_antenna and clear_timeline; in src-tauri/src/commands/user.rs
lines 465-478, apply the same change to the user-list invalidation flow before
returning Ok(()). Add a race test that refreshes while invalidation is pending.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: c58fe863-c551-49fb-8c6e-fae2ee054c3c

📥 Commits

Reviewing files that changed from the base of the PR and between 9f059b8 and 7cf1dee.

⛔ Files ignored due to path filters (1)
  • src-tauri/Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (42)
  • package.json
  • src-tauri/Cargo.toml
  • src-tauri/openapi.json
  • src-tauri/src/commands/admin.rs
  • src-tauri/src/commands/column_query.rs
  • src-tauri/src/commands/timeline.rs
  • src-tauri/src/commands/user.rs
  • src-tauri/src/commands/utility.rs
  • src-tauri/src/lib.rs
  • src-tauri/src/query_runtime.rs
  • src-tauri/src/streaming.rs
  • src-tauri/tauri.conf.json
  • src/aiscript/events.test.ts
  • src/bindings.ts
  • src/commands/cliHandlers.ts
  • src/components/common/NoteMoreMenu.vue
  • src/components/deck/DeckAntennaColumn.vue
  • src/components/deck/DeckChannelColumn.vue
  • src/components/deck/DeckClipColumn.vue
  • src/components/deck/DeckColumnsArea.vue
  • src/components/deck/DeckExploreColumn.vue
  • src/components/deck/DeckFavoritesColumn.vue
  • src/components/deck/DeckListColumn.vue
  • src/components/deck/DeckMentionsColumn.vue
  • src/components/deck/DeckNotificationColumn.vue
  • src/components/deck/DeckRoleColumn.vue
  • src/components/deck/DeckTimelineColumn.vue
  • src/components/deck/DeckUserColumn.vue
  • src/components/window/NoteDetailContent.vue
  • src/composables/useColumnSetup.ts
  • src/composables/useCrossAccountNotes.ts
  • src/composables/useEntityCrud.ts
  • src/composables/useNoteColumn.dom.test.ts
  • src/composables/useNoteColumn.ts
  • src/composables/useNoteColumnCache.test.ts
  • src/composables/useNoteColumnCache.ts
  • src/composables/useNoteList.ts
  • src/core/queryRegistry.test.ts
  • src/core/queryRegistry.ts
  • src/utils/cacheEviction.ts
  • src/utils/columnCacheKey.test.ts
  • src/utils/columnCacheKey.ts

Comment thread src-tauri/src/commands/timeline.rs
@hitalin hitalin self-assigned this Aug 4, 2026
api_update_antenna / api_remove_user_from_list がバケット破棄を
detach していたため、コマンド成功を受けたフロントの再フェッチが
ingest した後に破棄が走り、再構築したばかりのキャッシュを消す
レースがあった (#962 レビュー指摘)。spawn_blocking のまま await に変更
(warn + Ok の失敗方針と tokio worker 退避は維持)。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
fix: clear_timeline の完了を待ってからコマンドを返す
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Aug 4, 2026

Copy link
Copy Markdown

Deploying notedeck with  Cloudflare Pages  Cloudflare Pages

Latest commit: 6e1c277
Status: ✅  Deploy successful!
Preview URL: https://04e10796.notedeck-d3a.pages.dev
Branch Preview URL: https://develop.notedeck-d3a.pages.dev

View logs

hitalin and others added 2 commits August 4, 2026 10:13
#783 の 2026-08-04 レビューコメントで確定した欠陥を修正する。

1. 🐢 降格経路で名前付きクエリ形 `@(note) { ... }` が fail-closed になる:
   createReferenceFilter が無条件ラップしていたため、fn 式のクエリは本体が
   fn 値になり全ノート error だった。⚡ コンパイラと同じ「単一 1 引数 fn 式」
   判定を入れてラップを切り替える (引数名は execFnSync の位置渡しで不問)
2. nullability の 3 面不一致: NOTE_FIELD_TYPES の files / reactions /
   localOnly から NULL を除去 (Rust serde 形は常に存在)。死に枝の解消と
   V25 偽警告 (files への不要ガード提案) の停止。localOnly は TS 型も必須化
   し JS 面 (undefined→null) と Rust 面 (常に bool) の評価不一致を封じる。
   null リテラル比較の許可規則により配布クエリ (MisStore text-only の
   `note.files == null`) は引き続きコンパイル可 (テストで固定)。serde 形に
   忠実な golden vector を追加
3. セーフモード連携 (#838 条件 3 / 仕様項目 10): readSafeMode() 時は
   compiledQuery を null にし、コンパイル・Worker 起動・QIR キャッシュ検索の
   すべてを最上流で止める (フィルタなし表示 = fail-open、プラグインと同じ
   「自動実行されるユーザーコードを止める」意味論)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
fix(column-query): レビュー指摘 3 件の修正 (#783)

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/composables/useNoteColumn.ts (1)

1000-1005: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Advance fetchCursor with compound ordering.

Compare (createdAt, id) in oldestOf and advanceFetchCursor using the backend keyset order. Timestamp-only comparisons retain the first equal-timestamp note and can repeat or skip notes during legacy and QIR cache pagination. Add duplicate-createdAt tests for both paths.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/composables/useNoteColumn.ts` around lines 1000 - 1005, Update the cursor
advancement logic in useNoteColumn.ts around oldestOf and advanceFetchCursor to
compare the compound backend keyset order (createdAt, id), not timestamps alone.
Ensure equal-createdAt notes advance according to id so legacy and QIR cache
pagination neither repeats nor skips notes, and add duplicate-createdAt coverage
for both paths.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@src/composables/useNoteColumn.ts`:
- Around line 1000-1005: Update the cursor advancement logic in useNoteColumn.ts
around oldestOf and advanceFetchCursor to compare the compound backend keyset
order (createdAt, id), not timestamps alone. Ensure equal-createdAt notes
advance according to id so legacy and QIR cache pagination neither repeats nor
skips notes, and add duplicate-createdAt coverage for both paths.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 6689bc5f-6b03-4f4e-a6f2-41c9c7f7fdbd

📥 Commits

Reviewing files that changed from the base of the PR and between 49d378e and 1702242.

⛔ Files ignored due to path filters (1)
  • src/services/columnQuery/golden/qir.generated.json is excluded by !**/*.generated.*
📒 Files selected for processing (15)
  • src/adapters/types.ts
  • src/components/deck/DeckQueryManagerColumn.vue
  • src/composables/useCrossAccountNoteActions.test.ts
  • src/composables/useNoteColumn.dom.test.ts
  • src/composables/useNoteColumn.ts
  • src/engine/threadMerge.test.ts
  • src/services/columnQuery/compiler.test.ts
  • src/services/columnQuery/compiler.ts
  • src/services/columnQuery/golden/vectors.json
  • src/services/columnQuery/referenceEvaluator.test.ts
  • src/services/columnQuery/referenceEvaluator.ts
  • src/services/entityResolution.test.ts
  • src/utils/noteViewModel.test.ts
  • src/utils/replyMentions.test.ts
  • src/utils/safeMode.ts

hitalin and others added 3 commits August 4, 2026 10:33
closes #964, closes #965

#965: 複数 ⚡ パーツの QIR を And 合成する composeQir を新設。let の
スロット番号はパーツごと 0 起点のため、「前パーツまでの最大スロット+1」で
renumber してから左結合の二項 And で束ねる (「slot は式全体で一意」という
QIR 契約の維持)。cacheSearchableQuery の fast.length === 1 制約を撤廃し、
名前付きクエリ 2 個トグル等でキャッシュ検索が黙って無効になる問題を解消。

#964: オンラインの loadMore で「API ページは取得できたがフィルタ後の獲得が
0」のとき、同一呼び出し内でキャッシュ検索を 1 パス連結する (issue 案 (b)
の N=1)。API ページは api_get_timeline で ingest 済みで、キャッシュ検索は
fetchCursor より古い側のみ走査するため二重読みにならない。マッチ率が低い
クエリでもオンラインの「もっと読む」1 回でカラムが埋まる。キャッシュ検索
1 パスは runCacheSearchPass に抽出し offline 経路 (loadMoreFromCache) と共用。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
feat(column-query): And 合成とオンライン時のキャッシュ検索チェーン
- §1.2 を新設: cheapCheckCapabilities の意味論 (宣言 skill のみ発動、
  未宣言だと毎 tick AI 呼び出し、time.now 等はスキップが効かない)
- §10 Known Limits に MisStore インストール時の mode 正規化
  (heartbeat → manual) と isPersona 未取込 (#967) を追記

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@github-actions github-actions Bot added the 📖Doc Documentation related issue/PR label Aug 4, 2026
@hitalin
hitalin merged commit a7c4cbc into main Aug 4, 2026
21 checks passed
This was referenced Aug 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

📖Doc Documentation related issue/PR javascript Pull requests that update javascript code rust Pull requests that update rust code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(column-query): オンライン時もキャッシュ検索でカラムを埋める

1 participant