Zettelkasten Inbox / ブロック詳細
ABC Noteの章立てをカードの並べ替えで組み直す試作。本文のブロックそのものを動かす、アプリで最も危険な処理なので、対象の固定・題名の確認・危険な節の凍結・種類の限定という4重の安全装置が掛かっている。
抽象度 Lv.4(技術用語+システム上のやりとりまで/製品名・ライブラリ名は書かない)まだ本番に組み込まれていない試作。 ABC Noteは使っているうちに内容が溜まって長くなり、 あとから章立てを組み直したくなる。それを画面上のカードの並べ替えでやろう、という実験。
ただし対象は題名が「[TEST・削除可]」で始まるページ1つだけに固定されている。 ページのIDがプログラムに直接書き込まれていて、 さらに実行前に題名の接頭辞まで確認する。本番のABC Noteには絶対に触れない。
他のブロックが「Notionのプロパティを書き換える」のに対し、 ここだけは本文のブロックそのものを移動する。 アプリの中で最も壊す力が強い処理なので、安全装置がいちばん多い。
1. アクセス確認 → 11. 再構成ラボ(実験版) → (本番の流れには繋がっていない)
/lab/recompose という独立した画面から直接開くGET /api/lab/recompose と POST /api/lab/recompose409 で停止409409 で中止502本番の流れから完全に切り離してある。受信箱からも全体マップからもリンクが無い。 URLを直接打たないと辿り着けない。 試作であることを、機能の欠如ではなく導線の欠如で表している。
安全装置を3重に掛けている理由。 (1) 対象ページのIDを固定、(2) 題名の接頭辞を確認、(3) 危険なブロックを含む節を凍結。 本文のブロックを移動するというのは、失敗すると内容が消える操作だから。 他のブロックはプロパティを書き換えるだけなので、最悪でも上書きで済む。
「先にコピー、あとで削除」にしている理由。 逆にすると、削除は成功したのに複製に失敗した瞬間に内容が消える。 この順序なら、途中で失敗しても最悪「同じ内容が2か所にある」で済む。 失敗の向きを、取り返しのつく方へ倒している。
1回1枚に絞っている理由。複数枚を同時に動かすと、 移動の途中でブロックの位置がずれて、次の移動先の計算が狂う。 1枚動かすたびに全部読み直すほうが、遅いが確実。
| 工程 | 主体 | やりとりの内容 | 方向 |
|---|---|---|---|
| 11-1 | ブラウザ → ラボAPI | 現在の構成の要求 | → |
| 11-2 | ラボAPI → Notion | 固定ページとMarkdown表現の同時取得 | → |
| 11-3 | ラボAPI(内部) | 題名の接頭辞による安全確認 | ↻ |
| 11-4 | ラボAPI(内部) | 節とカードへの分割と、危険な節の凍結 | ↻ |
| 11-5 | ラボAPI → ブラウザ | カードの配置・統計・指紋 | ← |
| 11-6 | ブラウザ → ラボAPI | 指紋・新しい並び・動かしたカードのID | → |
| 11-7 | ラボAPI(内部) | 指紋の突き合わせと移動計画の作成 | ↻ |
| 11-8 | ラボAPI → Notion | 移動先へブロックを複製 | → |
| 11-9 | ラボAPI → Notion | 移動元のブロックを後ろから削除 | → |
| 11-10 | ラボAPI → Notion | 移動後の再取得と数の照合 | → |
| 11-11 | ラボAPI → ブラウザ | 新しい配置と検証の結果 | ← |
11-1 実験用の画面を開くと、いまのカードの並びを取りに行く。
技術的には GET /api/lab/recompose、
private, no-store。対象を指定する項目が無い――
どのページを見るかはプログラムの中に書き込まれていて、外から変えられない。
「対象を選べない」ことが、そのまま安全装置になっている。
11-2 Notionから2つのものを同時にもらう。ページの情報と、 そのページの中身を1本の文章にしたもの。
技術的には
GET /v1/pages/{id} と GET /v1/pages/{id}/markdown を同時に投げる。
後者はNotionが本文をMarkdown(見出しを # で表す軽量な書式)に変換して返す機能で、
このアプリでここだけが使っている。
ブロックを1つずつたどるより、文章として構造を掴むほうがカードに割りやすいため。
応答には「長すぎて途中で切った」印と「変換できなかったブロックの一覧」も入っている。
11-3 いちばん大事な確認。ページの題名が決められた印で始まっているかを見て、 違えばそこで全部止める。
技術的には 題名が
[TEST・削除可] で始まらなければ 409 +
「安全装置により停止しました。テスト用コピー以外は操作できません。」。
IDを固定してあるのに、さらに題名まで確認しているのは、
「テスト用のつもりだったページを、あとで本番に転用した」場合に備えた二重の網。
この確認は読み取りのときも保存のときも、毎回通る。
11-4 文章を大見出しで節に割り、その中を中見出しでカードに割る。 画像や埋め込みが混ざっている節は、動かせないように凍結する。
技術的には
# ではじまる行を節の区切り、## を カードの区切りにする。
各カードは本文中のページリンクを見て、実在するLiterature Noteを指していれば「動かせる」印を付ける。
そのうえで凍結の判定:節の中に
<unknown>(Notionが変換できなかったブロック)、画像、
ファイル・動画・音声・PDFのいずれかが含まれていたら、
その節ごと読み取り専用にして「画像・埋め込み・特殊ブロックを含むため、
このセクションは読み取り専用です。」と理由を出す。
変換できないものは、正しく複製できる保証が無いから触らないという判断。
最後に、節の数・カードの数・動かせるカードの数・凍結した節の数・
リンクの数・画像の数・未対応ブロックの数を数えて画面へ渡す。
11-5 カードの配置と一緒に、いまの状態を表す短い指紋を渡す。
技術的には 指紋は3つを繋げた文字列。 最終更新時刻+本文の長さ+本文から計算した短い値。 計算の前に、毎回変わってしまう部分(ブロック固有の番号など)を取り除いて安定させる―― そうしないと、中身が同じでも指紋が変わってしまう。 この指紋を画面が持っておき、保存時に送り返す。
11-6 カードを別の節へドラッグして保存すると、 新しい並びと「どの1枚を動かしたか」を、指紋と一緒に送る。
技術的には POST /api/lab/recompose に
{"fingerprint": "…", "arrangement": {…}, "movedCardId": "…"}。
新しい並び全体と、動かした1枚のIDを両方送るのは、
サーバー側が「本当に1枚だけ動いたのか」を検算するため。
3つのどれかが欠けていれば 400。
11-7 サーバーはまずNotionから最新の状態を取り直して、 指紋が一致するかを見る。違えば、書かずに断る。
技術的には これが
楽観的排他制御(先に鍵を掛けず、書く直前に「変わっていないか」を確かめる方式)。
ずれていれば 409 +「再読み込みしてからやり直してください。」。
8番の修正モードと同じ考え方だが、8番が関連づけの集合を比べるのに対し、
こちらは本文の内容から作った値を比べる――本文そのものを書き換えるので、
本文が変わっていないことを確かめる必要がある。
通ったら、送られてきた新しい並びと現在の並びを突き合わせて
「動いたのが本当に1枚か」「移動先はどこか」を計算する。
2枚以上動いていれば、そこで断る。
11-8 動かすカードに対応するNotionのブロックを、 まず移動先へコピーする。元はまだ消さない。
技術的には 2段構え。
(1) 位置の特定:Notionのブロックを全部読み、
文章から割った節・カードの構造と1つずつ突き合わせる。
見出しの数が合わない、見出しの文字が違う、といったずれがあれば
409 で中止(読んでから保存するまでの間に構造が変わったということ)。
(2) 複製できる形に変換:Notionから読んだブロックには
そのまま書き戻せない項目(作成日時、作成者、番号など)が混ざっているので、
書き込める形だけを残す。複製を許すブロックは12種類
(段落・中見出し・小見出し・箇条書き・番号付き・引用・囲み・コード・チェック項目・折りたたみ・区切り線・数式)。
これ以外が混ざっていたら 409 +「実験版で安全に移動できないブロックです。」。
ページへのリンクは、そのまま移せる形なら維持し、無理なら
文字+リンクに置き換えて内容だけは失わないようにする。
子を持つブロックは、中まで再帰的に複製する。
新しい節へ動かす場合は、大見出しごと新しく作る。
11-9 複製が成功したら、元のブロックを消す。 後ろから順に消していく。
技術的には DELETE /v1/blocks/{id} を1つずつ。
後ろから消すのは、前から消すと残りの位置がずれるから。
ここで失敗すると 502 +
「移動先への安全コピー後、元ブロックの整理に失敗しました。テストコピーには内容が残っています。」
――「内容は残っている」と明記しているのが親切な点で、
利用者は「消えたのではなく、両方にある」と分かる。
11-10 最後にもう一度ページ全体を読み直して、 リンク・画像・特殊ブロックの数が動く前と同じかを確かめる。
技術的には 照合するのは3つ。
(1) ページへのリンクのID一覧(並べ替えて連結して比較)、
(2) 画像の数、(3) 変換できないブロックの数。
1つでも違えば 502 +
「保存後の照合でリンク・画像・特殊ブロック数に差が見つかりました。」。
中身が1文字も欠けていないことまでは確かめていない。
数と一覧が合っていれば良し、という割り切りだが、
「移動で何かが失われた」という最悪の事態はこれで検出できる。
11-11 新しい配置と、検証で数えた数を返す。画面はそれで描き直す。
技術的には 200 +
新しい配置一式・新しい指紋・検証の結果(リンク数・画像数・未対応数・触ったブロック数)。
新しい指紋が返るので、続けてもう1枚動かせる。
画面側は結果をそのまま表示するので、
利用者は「何ブロック触って、リンクが何本残ったか」を毎回確認できる。
409 +「安全装置により停止しました。」。
読み取りの時点で止まるので、画面には何も出ない。409 +「再読み込みしてからやり直してください。」。
何も書かない。409 +どこが合わないかを示す日本語。何も書かない。409 +「『◯◯』は実験版で安全に移動できないブロックです。」。何も書かない。502 +
「元カードは変更していません。」と明記される。
移動先に途中まで書かれた可能性はあるが、元は無事。502 +
「テストコピーには内容が残っています。」。
この場合同じカードが2か所に存在する状態になる。自動では直さない。502 +
「テストページを確認してください。」。移動そのものは完了しているが、
何かが失われた可能性がある、という警告。※ 2026年8月14〜15日時点の zettelkasten-inbox のプログラムを読んで作成しました。過去の設計資料は参照していません。
※ 番号は全体図・この解説・シーケンス図で共通です。工程番号は「ブロック番号-連番」で書いています。