PersonalOS · 設計判断メモ · 2026-09-04 · 秘書あおい取りまとめ

PersonalOS ログの器

散文は Markdown、ログは SQLite ── この二層化は既に議論済みか。未議論なら、どう持つのがよいか。保管側(ds-store)と検索側(ds-retrieval)の2体が独立に実測し、突き合わせた結果。

既議論か

部分的に出ていた。2026-09-03 の健康データ取り込みセッションで「案C: 構造化データを別に持ち sqlite3 で集計」として登場したが、「grep が容量で破れたときの逃げ道として残す。いま決める必要は無い」で保留。shikaさんの案のうち (i) ログの正本自体を構造化テーブルにする / (ii) 「どのテーブルに何があるか」を索引に持たせる、の2点は未検討だった。

一致した結論

SQLite をログの正本にはしない。ただし値テーブルは正本から再生成できる派生物として、索引とは別ファイルの SQLite に持ち、AI は集計を SQL で投げる。「どのテーブルに何があるか」は索引に入れず、health スキルに固定3行(DB パス・.schema・検知器)を書く。対象は health/metrics/ だけ。

残る不一致

正本の形が1点だけ割れた。md のまま(ds-retrieval)か、1日1ファイルの追記専用 TSV(ds-store)か。両者とも「どちらも1つ払う」まで整理が進み、決め手は Q-1 shikaさん裁定 「.tsv という新形式を1ディレクトリに限って認めるか」。

今日やること

実装しない。本番の health/metrics/ は 2026-09-04 の1ファイル・全項目「未取得」・パーサ未実装。移行対象は0行で、形を今決めても捨てるコードが無い。書き込んだのは sessions/ 配下のみ。

1. 既に議論されていたか

2026-09-02
「構造化データ: PostgreSQL → SQLite」

requirement.md §17 の訂正表。ただしこの SQLite はチャンク索引(派生物)を指し、ログの正本テーブルの話ではない。

2026-09-03
案C として登場、保留

p3h-plan §4-3。「B(grep 集計)が容量で破れたときの逃げ道。P-1〜P-3 の例外を作る判断なので、いま決める必要は無い」。着地先は health/metrics/YYYY-MM-DD.md に確定。

2026-09-04(今回)
案を2点に分けて検討

(i) ログの正本を構造化テーブルにするか / (ii) カタログを索引に持たせるか。2体が独立に実測して突き合わせ。

shikaさんの案の分解と判定

案の要素判定要点
散文は Markdown 正本 → index build維持現行どおり。今回の対象外。
(i) ログの正本を SQLite テーブルにする採らないP-1(grep できる)P-2(差分が読める)を素の git で失う。派生物として持てば同じ SQL が書けるので、正本を変える理由が無い(§3)。
(i′) ログの値テーブルを SQLite で持つ(派生)採る正本から 38 ms で再生成。別ファイル index/metrics.sqlite。「最後のブロックを読む」知識を VIEW に1回書ける(§4)。
(ii) 「どのテーブルに何があるか」を索引に入れる索引には入れない索引は「住所を返す・値を返さない」道具。カタログは DB 自身(.schema)が持ち、引き口は health スキルの固定3行(§5)。
あすけん・ヘルスケアのログを対象にするmetrics/ に限る境界規則を満たす既存ファイルは health/metrics/2026-09-04.md の1本だけ。meals/ は散文で 28本中5本しか値の表が無く、SQL にしても行が無い(§2)。

2. 「ログ」の境界規則と棚卸し

「数値が多い」「表がある」は本文の文字列であって判別子にならない。ds-store は構造の述語で境界を引いた。3つ同時に満たすものだけがログ層に入る。

#述語検査の形
L-1記録階級(日付を必ず持つ・後から書き換えない・追記のみ)パス先頭 YYYY-MM-DD + doc_date_source = path_day
L-2空行と宣言済みヘッダ以外の全行が、固定の行文法に一致する(本体)health/metrics/README.md 検査3 と同型の awk(「決まった形以外の行: 0」)
L-3自由文を入れられる列(メモ列)を持たない列ヘッダの集合を宣言と突き合わせる
現物(開いて確認)L-1L-2L-3判定
health/metrics/2026-09-04.md✅✅✅純粋ログ(ただし観測値は0個)
health/weight.md❌❌❌ メモ列混合物 → 散文(09-04 に凍結済み)
health/sleep.md / exercise.md❌❌❌混合物 → 散文
health/meals/*.md(28本)✅❌ けんのコメント—混合物 → 散文。値の表があるのは5本
finance/cashflow/2026-08.md❌❌❌散文(表は付録)
mental/mood-log.md❌❌❌ 一言メモ混合物 → 散文
pokemon/battle-logs/*.md✅❌—散文(記録階級だが構造化ではない)
二層化は「既存データの移行」ではなく、これから機械が書く1ディレクトリの形を決める問題。混合物を「表の部分だけログに昇格」させない(表に無い行が集計から黙って落ちるため)。

3. なぜ SQLite を正本にしないか

ds-store が scratchpad に使い捨て git リポジトリと DB を作って実測した。3つとも「エラーが出ない壊れ方」に関わる。

Binary files differ
素の git diff はこの1行だけ。textconv を入れれば +INSERT 20本が読めるが、設定は .git/config にあり clone・GitHub・Obsidian には付いてこない。P-2 を失う。
ds-store §Q3-4 · 30日ぶん毎日コミットして観測
No destinations configured.
リポジトリの外(~/.local/share/personal-os/)に置くと、正本のバックアップ経路がゼロになる。Time Machine 未設定。作るなら「日次 .dump を git に入れる」=案c を遠回りしただけ。
ds-store §Q2-3 · tmutil destinationinfo 18:31
malformed → count = 981
4 KiB 1ページをゼロ埋めすると integrity_check は malformed。しかし索引経由の SELECT count(*) は 981 をエラー無しで返す。TSV なら壊れた37行だけ失い、残りは grep で読める。
ds-store §Q3-6

速さも理由にならない。「先月の平均体重」は md-grep 7.3 ms・SQLite 6.5 ms・TSV-awk 25.7 ms(1年規模・プロセス起動込み中央値)。差がつくのは「再推定を正しく読むか」であって速度ではない。

4. 派生 SQLite で何が良くなるか

ds-retrieval が本番の search/grep と、確定書式で246日ぶん合成したコーパスの両方で、3つの問いを実際に叩いた。

問い(a) 現行: search → grep → AI が数える(b) 派生 SQLite に SQL正しさ
先月の平均体重2,403 B(27行)83 B(値+母数3つ)同じ。ただし素朴平均は 68.681、最後のブロックだけなら 68.768。差 0.087 kg はエラー無しで出る
タンパク質が目安未満の日数(1年)23,983 B(日付つき grep。上限 30,000 B の 80%)77 B同じ(191 / 212 / 246 暦日)
体重が落ちた週の睡眠時間(2項目結合)42,630 B 上限超過。2回に分けて AI が awk で結合1,069 B(36週)この時点で AI は awk か SQL かの違いでプログラムを書いている
本当の利点は「最後のブロックを読む」知識の置き場。Apple は当日・前日の値を再推定で置き換えるため、同じ日にブロックが複数積まれるのが通常運転になる(追記のみ・dedup 禁止)。(a) では AI が毎回それを思い出して awk を書かないと少しずつ間違う。(b) は VIEW に1回書けば済む。

5. カタログを索引に入れない理由

「どのテーブルに何があるか」の置き場を5案で比較した。まず既存の辞書 health/metrics/README.md(索引に32チャンク入っている)が、質問の言葉で当たるかを本番索引で実測。

5 / 13
質問語(平均体重・体重 先月・PFC 集計 …)で README が結果に出た数。外れる理由は README の語彙にある: 平均 2回・先月 0・目安 0・PFC 0。
ds-retrieval Q2-0 · 本番索引 18:2x
--since で必ず落ちる
README は正本(doc_date NULL)なので、「先月の〜」と日付で絞る質問ほど辞書が消える。警告行は出るが「消えた21件に辞書が含まれる」とは書かれない。
ds-retrieval Q1-1 · health:21/23
.schema で足りる
列と VIEW は DB 自身が持っている。辞書を実物から作れば乖離は起きない。人が書くべきは「意味」(— と 未取得 の違い、睡眠時間が導出値であること)で、それは今の README にある。
ds-retrieval Q2-2
案判定理由
(A) second-brain.sqlite にカタログ表を index build が生成採らない(D) に「索引経由」という遠回りを足しただけ。search はそれを引く口を持たない。
(B) 辞書を Markdown 正本に置き、普通のチャンクとして索引に入れる副として残す実装物0だが、キーワード依存(5/13)・--since で必ず落ちる。
(C) search --metric 体重 --agg avg採らない「本文を一切見ない・値を返さない」と宣言した道具に集計を持ち込む。索引が集計に使えない3つの理由(重複・ヘッダ喪失・母数欠け)を search の中に持ち込む。
(D) health スキルに固定3行(DB パス・.schema・検知器)主にするスキル起動は決定的でキーワードに依存しない。形式が md でも tsv でも同じ。
(E) MCP サーバ今は採らないREADME Phase 5「必要になってから足す」。/usr/bin/sqlite3 3.51.0 が Bash から直接叩ける。

6. 索引に日次ファイルを残す代償(案a の側)

ds-store の「案a のとき search "体重" の上位30件がどれだけ日次ブロックで埋まるか」という問いに、ds-retrieval が本番の散文(health/ 8本+meals/ 28本+README)と合成日次365本を混ぜた索引で答えた。

30 / 30 が日次ブロック
search "体重" --format full -k 30 で散文は0本。体重を論じた overview.md・weight.md の21チャンクは k=30 の外。
ds-retrieval U-a · 18:43
着地から約5週
順序規則が「パス由来の日付を持つチャンクが先・doc_date DESC」なので、日次が31本を超えた日から本文ビューは散文を返さない。エラーも警告も出ない。
search:457-467
逃げ道なし
NOT語 -計測 は効かず(2ブロック目以降は H1 を含まない)、path 除外の引数は無い。--until 最初の計測日の前日 --include-undated は「知っている人しか書けない」。
ds-retrieval From 節 (2)
帰結: 案a(md 正本のまま)を採るなら、index-build に機械書きディレクトリの除外規則(+1実装・N-5 の例外)をパーサ着地と同時に入れる必要がある。ds-retrieval は当初の「壁に当たった日に決める」を撤回した。

7. 決定と無関係に、いま既に壊れているもの

#何が実測直し方
X-1health/metrics/README.md 検査12「体重の行数 = ファイル本数」追記ブロックがあると必ず落ちる(合成: 922行 / 365ファイル)。同じ README の検査2 は「ブロック数 × 10」と書いており、同一版内で前提が食い違う(時刻差ではない)。「行数 ≧ ファイル数、かつ 行数 = ブロック数」に
X-2README「集計のしかた」の grep -rh と、「1年1項目で 7,268 B(上限の24%)」確定した4列書式は1行 53 B(前提は 23 B)。追記 2.5 本/日で 48,866 B = 上限 30,000 B の 163%。1ブロック/日でも 19,345 B。「最後のブロックを採る」規則が実装されていない。「ファイルごとの最後のブロック」+「day value だけ出す」形に書き直し、P-9 の独立2経路一致もこの形で取り直す

あわせて ds-store の訂正: 「data/health/*.csv は git 履歴に入るから不採用」(M-14)は、採用した案a にも等しく当たる論法だった。不採用の実質は「新形式の導入」と「索引に入らない」の2点で、後者は N-5 が「隣に同名の .md を置く」出口を最初から用意している。

8. 合意された形

正本の形(md か TSV か)を除けば、両者の設計図は同じ。派生 DB は消して作り直せる位置に置き、索引は住所だけを返し続ける。

正本 · git 追跡
health/metrics/YYYY-MM-DD.md | tsv
1日1ファイル・追記専用・機械が書く。同じ日を取り直すとブロックが増える(dedup 禁止・T15)。
Q-1 で形が決まる
→
生成器 · 別ジョブ
metrics build
正本 → 派生 DB を再生成。38 ms / 9,810 行。index build には相乗りさせない(失敗が n_err に合算される)。
md なら表示書式を剥がすパーサが1箇所要る/tsv なら .import 相当
→
派生 · rm で消してよい
~/.local/share/personal-os/index/metrics.sqlite
observations(day, metric, value REAL, unit, status, source, route, captured_at, raw_ref)+ VIEW observations_last(日×項目の最後の captured_at)+ meta(built_at, n_files, n_rows)
辞書 · 副
health/metrics/README.md
項目の意味・値の3状態・単位・集計のしかた。索引に32チャンク入る(search から辿れるのは 5/13)。
索引 · 住所だけ
second-brain.sqlite
チャンクの住所を返す。値表もカタログ表も入れない。案a なら日次 md の除外規則が要る。
引き口 · 主
health スキルに固定3行
① DB の絶対パス ② sqlite3 <db> .schema ③ 検知器クエリ。AI は検知器を先に叩き、値のクエリはその後。
正本(git)派生物(再生成可)AI の引き口

検知器(値のクエリの前に必ず叩く)

SELECT metric, count(*) rows, count(distinct day) days,
       sum(status='ok') ok, sum(status='none') none, sum(status='unfetched') unfetched,
       group_concat(distinct unit) units, min(day) first, max(day) last
FROM observations GROUP BY metric;
-- 期待: 10行、units が各1種類、days が ls のファイル数と一致

SQL 経路で新たに生まれる「エラーを出さない間違い」9件(再取込の二重計上・欠測日の分母・lb 混入で 74.9 vs 68.6・coalesce(…,0)・週キーの %W と %V の不一致 など)と、それぞれの検知器は ds-retrieval Q4 にある。既存の「行数・ファイル数・暦日数を3つ並べる」規則は、1つの SELECT に count(*)・count(distinct day)・暦日を並べる形に翻訳される。

9. 残る不一致 ── 正本の形

案a md のまま ds-retrieval

払うもの
パーサ着地と同時に、索引の除外規則(+1実装・N-5 の例外)
残る利点
Obsidian で表として読める(P-3 の質)/受け入れ基準13本がそのまま生きる/--since D でその日の測定の住所が副産物で出る(ls で代替可)
構造上の代償
人向けの表示書式(7h12m・8,432 歩・70.6 kg)を、機械が生成器で剥がし直す
依存する前提
新形式を life に入れない(requirement.md:584-585)

案c 1日1ファイル TSV ds-store

払うもの
今、.tsv という新形式を1ディレクトリに限って入れる + N-5 検知器「.md 以外 N ファイル」の実装(未実装)
残る利点
索引に年922チャンクの数値が入らない(問題そのものが消える)/値が機械書式で生成器は .import 相当/git diff は +20行で読める
構造上の代償
その日の測定の有無は ls health/metrics/ | grep '^2026-09-04' で見る。README に1行要る(無いと search --since D の0件が「測っていない」に読める)
依存する前提
新形式を認める(Q-1)
両者の整理: 共通前提(追記設計の維持・派生 DB を作る・正本は git)は同じ。差は「どちらの1つを払うか」に尽きる。ds-retrieval は「検索側は案a・案c のどちらにも反対しない。残る根拠は保管側の軸」、ds-store は「新形式を入れないを優先するなら案a で、X-1・X-2 の修正と除外規則を将来払う」。

10. shikaさんに残す判断

11. 未確認(実測タスク)

#未確認確認方法・時期意味
U-c / U-R1本番の1行の長さと1日のブロック数の実分布パーサ着地後1週間、wc -c と grep -c '^## '合成の 53 B/行・2.5 本/日は前提。30,000 B との関係と VIEW の必要度が決まる
U-R2HealthKit 書き出しの単位が kg 固定かショートカットの出力を1回見るlb 混入で AVG が 74.9 vs 68.6 に黙って化ける
U-blaunchd の bash が health/metrics/ に書けるか(T-8)パーサ実装時に本番文脈で1回形式によらず同じ
U-dN-5 の検知器「.md 以外 N ファイル」が未実装index-build に「規格外」の文字列 0(18:30)案c の前提条件。無いと .tsv 365本の存在はどこにも出ない
U-elife の複製は GitHub の origin だけ観測時点で HEAD e2631ae は未 push(origin/main は 95debdf)形式によらず。正本の複製が1箇所しかない
U-R4README に質問側の語を足すと (B) の当たり率が上がるか「平均・先月・目安・PFC」を含む1行を足して再測上がっても --since で落ちる欠陥は残る。(D) 主の判定は変わらない