●EVENT — 本日9月9日、Apple が「Surprise and Shine」と題したイベントを開きます。日本時間では9月10日の未明2時からです●EXPECT — iPhone 18 Pro と Pro Max、折りたたみ機、2nm プロセスの A20 Pro チップ、そして iOS 27 以下の配信日発表が見込まれています●WAIT — この記事を書いている時点ではまだ開催前です。噂の段階で書いたものと発表後に書いたものが混ざると、読む側には区別がつきません●MAX — Rork Max がネイティブ Swift を生成する以上、Apple の動きは他人事ではありません。標準の Rork は React Native、という線引きは繰り返し確認したいところです●SIMULATOR — Rork Max はクラウド上の Mac でコンパイルし、ブラウザ内で動くストリーミングの iOS シミュレータで確認できます。Xcode も Mac の実機も要りません●SEASON — OS が新しくなる時期は、自動生成の足場がいちばん揺れます。便利さを謳う記事ほど、この揺れに触れないと不誠実になると感じています●EVENT — 本日9月9日、Apple が「Surprise and Shine」と題したイベントを開きます。日本時間では9月10日の未明2時からです●EXPECT — iPhone 18 Pro と Pro Max、折りたたみ機、2nm プロセスの A20 Pro チップ、そして iOS 27 以下の配信日発表が見込まれています●WAIT — この記事を書いている時点ではまだ開催前です。噂の段階で書いたものと発表後に書いたものが混ざると、読む側には区別がつきません●MAX — Rork Max がネイティブ Swift を生成する以上、Apple の動きは他人事ではありません。標準の Rork は React Native、という線引きは繰り返し確認したいところです●SIMULATOR — Rork Max はクラウド上の Mac でコンパイルし、ブラウザ内で動くストリーミングの iOS シミュレータで確認できます。Xcode も Mac の実機も要りません●SEASON — OS が新しくなる時期は、自動生成の足場がいちばん揺れます。便利さを謳う記事ほど、この揺れに触れないと不誠実になると感じています
// lib/classificationLog.ts — 分類の質を時系列で残すimport * as SQLite from 'expo-sqlite';const db = SQLite.openDatabaseSync('finance.db');export async function initClassificationLog(): Promise<void> { await db.execAsync(` CREATE TABLE IF NOT EXISTS classification_log ( id TEXT PRIMARY KEY, expense_id TEXT NOT NULL, ai_category TEXT NOT NULL, confidence REAL NOT NULL, corrected_category TEXT, -- ユーザーが直した先(直さなければ NULL) created_at TEXT NOT NULL ); CREATE INDEX IF NOT EXISTS idx_cl_created ON classification_log(created_at); `);}export async function logClassification(entry: { id: string; expenseId: string; aiCategory: string; confidence: number;}): Promise<void> { await db.runAsync( `INSERT INTO classification_log (id, expense_id, ai_category, confidence, corrected_category, created_at) VALUES (?, ?, ?, ?, NULL, ?)`, [entry.id, entry.expenseId, entry.aiCategory, entry.confidence, new Date().toISOString()] );}// ユーザーがカテゴリを手直ししたら、その事実を後追いで記録するexport async function recordCorrection(expenseId: string, correctedTo: string): Promise<void> { await db.runAsync( `UPDATE classification_log SET corrected_category = ? WHERE expense_id = ?`, [correctedTo, expenseId] );}
実装上の注意を一つ。分類ログと家計データは同じ SQLite に置いていますが、テーブルは必ず分けています。支出そのものの記録と、分類がどれだけ信用できるかの記録は、目的がまったく違うからです。前者はユーザーのお金の履歴、後者は自分の AI の体調記録。混ぜると、家計画面のクエリが分類メタ情報で重くなり、分類の劣化も家計の数字に埋もれます。出口を分けておくと、どちらも軽いまま保てます。