第1幕: 現場 ── 再発
差し戻された不具合票を持って、自分は整理券の発券機の前に立っていた。昨夜、AI に一つの会話のまま、不具合を七件続けて直させた。七件目を直したら、五件目に直したはずの不具合が戻っていた。票のいちばん上に、赤いボールペンで二文字、書いてある。再発。
大槻さんの字だった。
夜明け前の遊園地は、音がしない。入口のゲートの前に、発券機が四台並んでいる。画面はまだ暗い。自分は一台の前にしゃがんで、操作パネルの蓋を開け、点検用の画面を出した。運休の操作を試すためのボタンがある。押した。画面の表示が「運休」に変わった。そのあと、その回の整理券の番号を読ませてみた。「有効」と出た。
運休したアトラクションの整理券が、使えるまま残っている。昨日の夕方のプレオープンで出た不具合の、五番だ。昨夜、三回やり直して、やっと直したところだった。
不具合票というのは、運営チーフの大槻さんが、見つけた不具合を一件ずつ書いて番号を振る、紙の票のことだ。昨日、招待客だけを入れたプレオープンのあと、大槻さんは票を七枚、自分に渡した。七枚の票の右下には、それぞれ大槻さんの字で「決まり」が一行ずつ書いてある。五番の決まりは、こうだった。運休の操作のあと、その回の整理券はすぐ使えなくする。
明日が、グランドオープンだ。この遊園地は改装で半年休んでいて、明日、もう一度開く。
空が、観覧車の向こうから少しずつ白くなっていた。フードコートのテーブルの上に、ポップコーンの袋がいくつも散らばっている。昨夜のうちに片づけたはずの袋だ。ゴミ箱の蓋が一つ、外れて転がっていた。袋のまわりに、黒い羽が一枚落ちていた。
カラスだ。改装のあいだ、誰もいない遊園地は、カラスの住みかになっていたらしい。大槻さんが、鳥の追い払いの業者を頼んだと言っていた。鷹の人が来るから、と。
そのとき、頭の上を何かが低く抜けた。
風が、首の後ろを撫でていった。見上げると、茶色い鳥が、フードコートの屋根のほうへ滑っていくところだった。屋根の上に並んでいたカラスが、一斉に飛び立った。ぎゃあぎゃあと鳴きながら、観覧車のほうへ逃げた。茶色い鳥は、屋根の縁にとまった。翼をたたむと、尾の先に白い帯が一本見えた。
鷹だった。
少しして、通用口のほうから、人が走ってきた。若い女の人だった。走ってくる途中で、まず左腕が目に入った。肘のあたりまで、使い込んだ革の手袋をはめている。次に、首から紐で下げた小さな笛が、胸の前で跳ねているのが見えた。薄手の上着に、作業用の太いズボン。目つきが鋭い。走ってきたのに、息はほとんど上がっていなかった。
その人は、フードコートの屋根を見上げてから、自分のほうを見た。
「屋根に上がるはしご、どこですか」
いきなりだった。自分は、立ち上がりながら、倉庫のほうを指さした。
「あ、あっちの、倉庫に。脚立なら」
「借ります」
それだけ言って、倉庫のほうへ歩き出す。自分はあわてて追いかけて、倉庫の鍵を開けた。脚立を一緒に運んで、フードコートの裏に立てた。自分が脚立の脚を押さえているあいだに、その人は身軽に上っていった。
脚立の下から見上げると、屋根の上で、その人が左の拳を差し出していた。鷹が、屋根の縁から拳へ跳び移った。その人は、腰の小さな袋から、肉の切れ端を一つつまんで、鷹にやった。鷹は、それを一口で飲み込んだ。ほうびなんだろうな、と思った。眠い頭では、そこまでだった。
降りてきたその人は、脚立をたたむのを手伝ってから、言った。
「鷹宮です。大槻さんに呼ばれて」
「早瀬です。ここの、システムの……」
自分は、何の担当だと言えばいいのか迷った。アプリもサーバーも、全部自分だった。この遊園地で、エンジニアは自分一人だ。
鷹宮さんは、続きを待たなかった。拳の上の鷹を見て、それから観覧車のほうを見上げた。観覧車の骨組みに、カラスがまた何羽か戻ってきていた。
自分はフードコートのテーブルにノートパソコンを開いた。画面の明るさが、夜明け前の目に痛かった。キーボードに指を置いたまま、昨夜の六時間を、一件目から辿り直した。
来園者向けのスマホアプリを作っているのは、自分だ。アトラクションの待ち時間を出す画面と、整理券を発行する画面がある。整理券は、乗る時刻の枠を先に取っておける券で、その時刻に乗り場へ行けば、列に並ばずに乗れる。アプリの裏のサーバーも、自分が見ている。コードの多くは、AI に書かせている。使っているのは Claude Code という、コーディングエージェントだ。こっちが頼んだことを、AI がファイルを読んで、コードを書いて、テストを走らせて、片づけていく。
昨夜の十時に、七枚の票を机に並べた。一枚目から順に、AI に頼んだ。会話は一つのままにした。新しく始め直さなかった。
そのほうが早いと思ったからだ。同じアプリの話だから、前の修正のことを AI が覚えていてくれたほうが、いちいち説明しなくて済む。実際、二件目からは、頼む文が短くなった。さっきの番号の画面で、とか、同じ関数で、とか書けば、AI はそれで分かってくれた。
一件目から四件目は、すんなり直った。整理券の番号の桁。待ち時間の単位。閉園まぎわの整理券の締め切り。再発行の回数。
五件目で、つまずいた。
運休した回の整理券が、使えるまま残る。雨や点検でアトラクションを運休にしても、その回の整理券を持っている来園者は、乗り場へ向かってしまう。
一回目、AI は画面の側で運休の操作を見て、整理券を消すように直した。試したら、運休してから五分のあいだ、整理券がまだ使えた。
調べると、整理券の一覧はキャッシュされていた。キャッシュというのは、一度サーバーから取ってきたデータを、しばらく取り直さずに使い回すことだ。毎回取りに行かないので速くなる。そのかわり、そのあいだに元のデータが変わっても気づかない。整理券の一覧は、五分間キャッシュされていた。だから、運休の知らせが五分遅れて届いていた。
二回目、AI はキャッシュの時間を一分に縮めた。テストは通った。
念のため、夜中に大槻さんに電話した。大槻さんは、寝ていたはずなのに、すぐ出た。一分でも駄目だ、と言われた。運休の放送から一分あれば、来園者は乗り場に着く。乗り場に着いてから「その券はもう使えません」と言われたら、と大槻さんは言った。言われたほうは、二度とうちの整理券を信じない。
キャッシュの時間を短くするのは、やめた。何秒にしても、その秒数のあいだは、運休したあとの整理券が使えてしまう。
三回目に、整理券の状態だけは、キャッシュせずに毎回サーバーから読むことにした。待ち時間の表示だけをキャッシュする。それで直った。整理券の状態はキャッシュしない。そう決めて、AI に伝えた。
六件目の途中、夜中の二時半ごろだったと思う。画面に、会話が長くなったので要約して詰めた、という知らせが出た。道具が自動でやったことだ。自分は、整理されたなら助かる、くらいに思った。それどころじゃなかった。
七件目は、来園者が増えると待ち時間の画面が重い、という票だった。明日は、プレオープンの何倍も人が来る。AI は、待ち時間と整理券の読み込みを一つの関数にまとめて、両方をキャッシュに通した。画面は速くなった。テストも通った。朝の四時に、自分は寝た。
五時半に、大槻さんからの電話で起こされた。開園前の点検で、五番がまた出た、と。
駆けつけて、七件目の差分を読んだ。整理券の読み込みが、またキャッシュに入っていた。五件目で外したはずの行が、七件目の修正の中で戻っていた。
何が戻ったのかは、分かった。なぜ戻ったのかが、分からない。五件目で決めたことは、AI に伝えてある。同じ会話の中の、数時間前のことだ。
寝不足で、頭が回っていなかった。それは自分でも分かっていた。分かっていて、どこから考えればいいのかが分からなかった。
九時に、開門の放送のテストがある。グランドオープンの前の日の、最後の通し練習だ。それまでに、五番を直したい。あと二時間半。
鷹宮さんは、観覧車の骨組みに並んだカラスを見上げていた。拳の上で、鷹の向きを少しだけ直した。
第2幕: 再現 ── 四回と一回
鷹宮さんは、フードコートの前に立って、観覧車のほうへ拳を振った。
鷹が、拳を蹴って飛び立った。低く、速かった。自分はテーブルから、その背中を目で追った。観覧車の骨組みのカラスが、気配に気づいて首を伸ばした。
鷹は、観覧車へは行かなかった。
途中で弧を描いて向きを変え、フードコートの屋根へ戻っていった。さっき、ほうびをもらった屋根だ。縁にとまって、翼をたたんだ。観覧車のカラスは、首を引っ込めて、また並び直した。
鷹宮さんは、黙って左腕を高く掲げた。鷹が屋根から降りてきて、拳にとまった。鷹宮さんは、鷹の胸のあたりを少しのあいだ見ていた。それから、フードコートの外のフェンスまで歩いて、鷹をフェンスの上にとまらせた。
「疲れてる。少し休ませます」
それだけ言って、フェンスの横に立った。
自分は、ノートパソコンに目を戻した。待つあいだに、昨夜の会話の履歴をさかのぼることにした。
ここで、会話というものを、自分の言葉で整理しておく。Claude Code で AI に頼みごとをすると、そのやりとりは一つの会話(セッション)としてつながっていく。こっちが新しい会話を始めないかぎり、前のやりとりは全部、同じ会話の中に残っている。AI が答えるときに読んでいるのは、いま書いた一文だけではない。その会話でこれまでにやりとりしたこと、全部だ。
そして、AI が一つの会話で一度に見渡せる量には、上限がある。それをコンテキストウィンドウと呼ぶ。会話のやりとりも、AI が読んだファイルの中身も、走らせたコマンドの出力も、指示書も、全部この中に入る。昨夜の会話には、七件分の依頼と、修正の説明と、テストのログが、全部入っていた。
Claude Code の公式のドキュメントの、使い方のコツのページに、こう書いてある。
LLM performance degrades as context fills. When the context window is getting full, Claude may start “forgetting” earlier instructions or making more mistakes.
会話がいっぱいになるほど、AI の調子は落ちる。いっぱいに近づくと、前の指示を「忘れ」たり、間違いが増えたりしうる。
一晩の会話だ。さぞ長かったんだろう、と思った。長すぎたから、五件目のことを忘れた。そういうことなんだろう。
自分は、昨夜の会話の中で、キャッシュの話がどこに何回出てくるかを、上から目で追い始めた。五件目の一回目の説明に一回。二回目に一回。三回目の説明にも、外した行として一回出てくる。七件目に一回。
途中で、数え間違えている気がしてきた。眠いせいだ。手で数えるのをやめて、小さなコードを書くことにした。
会話の一往復を、型にしておく。types.ts に、こう書いた。
| |
role は、誰の発言かだ。'user' が自分、'assistant' が AI、'tool' は AI が走らせたコマンドの出力やテストのログになる。'user' | 'assistant' | 'tool' の | は、三つの文字列のどれか一つ、という TypeScript のユニオン型だ。
昨夜の会話を、この形の配列に写した。本物の会話の履歴ファイルの形ではなく、この記事のために、昨夜の会話から要る分だけを写した架空のものだ。五件目のところは、こうなる。
| |
バッククォート(`)で囲んだ文字列の中に ${...} と書くと、そこに値が埋め込まれる。${MARK} や ${TICKET_DECISION} は、同じファイルの上のほうで決めた文字列だ。ATTEMPT_1 と ATTEMPT_2 は、一回目と二回目の AI の説明の文で、LOG_LINE は、三回目のあとのテストのログの一行だ。${MARK}, 60_000, readTickets) をつなげて読むと、cached('tickets', 60_000, readTickets) になる。MARK は cached('tickets' という文字列で、整理券の読み込みをキャッシュに通しているコードの目印だ。cached(key, ms, fn) は、うちのサーバーにある関数で、fn で取ってきた結果を ms ミリ秒のあいだ使い回す。5 * 60_000 は五分、60_000 は一分だ(数字の中の _ は、読みやすくするための区切りで、値は変わらない)。TICKET_DECISION は、票の決まりの文(運休の操作のあと、その回の整理券はすぐ使えなくする)で、IMPL_DECISION は、三回目に決めた実装の文(整理券の状態はキャッシュしない)だ。
七件目を頼んだとき、AI が読んでいたのは、その夜の会話の全部だ。それを一つの文字列につなぐ関数を、before.ts に書いた。ここでは、七件目の修正の説明まで含めて、その夜の会話を最後までつなぐ。
| |
import type は、型だけを読み込む書き方だ。.ts の拡張子まで書くのは、Node が TypeScript のファイルをそのまま走らせるときに、拡張子を補ってくれないからだ。map で一往復ずつ「誰: 本文」の形にして、join で空行をはさんでつなぐ。
数えるほうは、count.ts に書いた。
| |
文字列を needle で切ると、切れ目の数より一つ多い数のかけらになる。だから、かけらの数から一を引けば、出てきた回数になる。
その夜の会話の全部をつないで、整理券をキャッシュするコードの目印と、決めたことの文を数えた。
| |
四回と一回だった。
やめたやり方のコードは、四回書いてある。決めたことは、一回しか書いていない。四回のうち一回は、七件目の修正そのものだ。七件目を頼んだ時点では、三回と一回だった。七件目の AI は、それを全部読んだうえで、整理券の読み込みをキャッシュに通し、四回目を書いた。
それで AI が五件目を戻した、と言い切れるわけではない。AI が何を見てそう書いたのかは、外からは分からない。ただ、会話の中に残っているものの割合は、こうだった。やめたことのほうが、決めたことより多く書いてある。
フェンスのほうで、羽の音がした。
鷹宮さんが、鷹をフェンスから拳に戻していた。十分間ほど休ませただろうか。さっきと同じ、フードコートの前に立って、観覧車のほうへ拳を振った。
鷹は、また屋根へ行った。
今度は迷いもしなかった。まっすぐフードコートの屋根へ向かって、さっきと同じ縁にとまった。
鷹宮さんは、屋根の鷹を少しのあいだ見上げていた。それから、小さく息を吐いた。
「違った」
それだけだった。拳を上げて鷹を呼び戻すと、拳に据えたまま、歩き出した。フードコートを離れて、観覧車のほうへ。自分はノートパソコンを抱えて、あとを追った。
鷹宮さんは、観覧車の乗り場の前を通りすぎ、乗り場の建物の裏へ回り込んだ。そこからは、フードコートの屋根が見えなかった。乗り場の建物が、ちょうど間に立っている。
鷹宮さんは、そこで足を止めた。観覧車の骨組みを見上げて、拳を振った。
鷹は、骨組みへ一直線に飛んだ。カラスが、今度は一羽残らず飛び立った。観覧車の向こうの雑木林のほうへ、黒い影が散っていく。鷹は骨組みの上を一回りして、鷹宮さんの拳に戻ってきた。
自分は、乗り場の裏の、塗り替えたばかりのベンチの横に立っていた。
「休ませたのに、なんでまた屋根に行ったんですか」
鷹宮さんの目は、拳の上の鷹から離れなかった。
「休ませても、見えてる」
「見えてる?」
「屋根。今朝、あそこでほうびをやった」
脚立の下から見た、あの肉の切れ端のことだ。
「じゃあ、ここまで歩いたのは」
「見えないところから、放す」
鷹宮さんは、乗り場の建物のほうへ顎をしゃくった。建物の向こうに、フードコートの屋根がある。ここからは見えない。
「最初は、疲れだと思った。違った」
言い訳はしなかった。外した見立てを、外したまま、すぐ捨てて、歩いた。それだけだった。
自分は、ベンチに腰を下ろした。休ませても、屋根は見えている。なにかが、昨夜の自分と重なりかけた。でも、まだ言葉にならなかった。
膝にノートパソコンを載せた。分かっていることから、テストにしておくことにした。テストというのは、コードがこう振る舞うはずだ、という期待を、走らせて確かめられる形に書いたものだ。Node には、node --test という、テストを走らせる仕組みが最初から入っている。
いまの作りでは赤になるはずのテストを先に書いて、本当に赤になるのを確かめておく。確かめたいのは、七件目を頼んだときに AI が読むものだ。そこに、やめたコードが入っていてほしくない。決めたことは、入っていてほしい。テストの名前を書こうとして、手が止まった。「七件目の会話に渡さない」と書きかけて、消した。七件目だけの話じゃない。これから先、AI に何かを頼むたびに、AI はその会話を読む。「次の会話に渡さない」に書き直した。ここで言う次の会話は、この会話の続きで、次に何かを頼むときに AI が読むもののことだ。
| |
nightConversation() は、その夜の七件分の会話を、さっきの形の配列で返す関数だ。テストに渡す固定の入力を、fixture と呼ぶ。本物の AI は呼ばない。昨夜の会話を写した、決まった配列を渡すだけだ。assert.equal は、二つの値が等しいことを確かめる。assert.ok は、値が真であることを確かめる。
走らせた。
| |
一本目が赤で、二本目が緑だった。赤は失敗、緑は成功だ。決めたことは、ちゃんと会話の中にある。やめたコードも、四回、会話の中にある。忘れて消えたのではなかった。決めたことは残っていて、そのうえで、やめたやり方のほうが多く見えていた。
公式のページを、さらに読み進めた。こういう一節があった。
If you’ve corrected Claude more than twice on the same issue in one session, the context is cluttered with failed approaches.
一つの会話の中で、同じ問題を二回より多く直したなら、会話は失敗したやり方で散らかっている。
五件目だ。一回目は五分遅れた。二回目は一分でも駄目だった。三回目で、やっと決めた。自分のことだった。
この会話のまま、AI に「整理券はキャッシュするな」と、もう一回言い直せば済むんじゃないか。そうも思った。でも、この会話の中では、やめたコードが四回で、決めたことが一回だ。言い直せば、決めたことが二回になる。四回のほうは、そのまま残る。公式も、同じ問題を二回より多く直した会話は、失敗したやり方で散らかっている、と書いている。ここで同じ会話に言い直しを積むのは、昨夜と同じことをもう一回やるだけだ。同じ会話の中で言い直すのは、やめた。
では、昨夜の二時半に走った、あの要約は何だったのか。
Claude Code には、会話が長くなったときに、それまでの会話を要約に置き換えて、空きを作る仕組みがある。こちらから頼むときのコマンドと、道具が自動でやる仕組みの両方だ。日付つきで、手元のメモに写しておく。2026年10月18日時点の Claude Code の設定で、名前や動きは改訂で変わることがある。
/compact: それまでの会話を要約に置き換えて、コンテキストウィンドウに空きを作る。/compactのあとに一言添えると、要約で何を残すかの焦点を指示できる- 自動の圧縮(auto-compact): 会話がいっぱいに近づくと、道具が自動で要約する。
/autocompactで、どこまで埋まったら走るかを変えられる /context: いま、何がコンテキストウィンドウを埋めているかを表示する- 圧縮のあとも、プロジェクトの
CLAUDE.md(指示書)は、ディスクから読み直されて残る。会話そのものは、要約に置き換わる
公式の説明には、要約が残すものも挙がっていた。頼んだことと、その意図。大事な技術の考え方。読んだり書き換えたりしたファイルと、大事なコードの断片。起きたエラーと、その直し方。残っているタスクと、いまの作業。
やめたやり方を、どう扱うかは書いていなかった。残すとも、残さないとも書いていない。「起きたエラーと、その直し方」として、五件目の三回のやり直しが要約に入ることもあるかもしれない。入らないかもしれない。それは、こちらからは決められない。
要約して詰めるのは、鷹を休ませたのと同じだった。休ませても、屋根は見えていた。会話を詰めても、同じ会話の中にいるかぎり、やめたやり方の名残は会話の中にある。何をどれだけ残すかを決めているのは、要約のほうだ。
第3幕: 手順を整える ── 持っていくもの
公式のページには、会話を区切るための一行もあった。
/clear: reset context between unrelated tasks. Long sessions with irrelevant context can reduce performance.
関係のないタスクのあいだで、会話を空にする。関係のない文脈を抱えた長いセッションは、性能を落としうる。
これを、1タスク1セッションと呼ぶ。一つのタスクを一つの会話でやる。タスクが終わったら、会話を空にして、次のタスクは新しい会話で始める。前のタスクのやりとりを、次のタスクに持ち込まない。
これも、日付つきでメモに写した。2026年10月18日時点の Claude Code の設定だ。
/clear: 会話を空にして、新しい会話を始める。別名は/newと/reset- 空にした前の会話は、
/resumeで呼び戻すこともできる - 新しい会話は、毎回、指示書(
CLAUDE.md)を読み込んで始まる。公式のコマンドの一覧には、こう書いてある
/clearstarts fresh on a new task while keeping project memory.
七件は、七つの会話にすればよかった。一件終わるごとに、会話を空にして、新しく始める。鷹宮さんが、屋根の見えないところまで歩いたのと同じだ。
そこまで考えて、すぐに詰まった。
新しい会話は、昨夜のことを何も知らない。五件目で決めたことも、知らない。空にした会話で、いきなり「五番を直して」と頼んだら、AI はまた整理券をキャッシュに通すかもしれない。何も渡さなければ、知らないのと同じだ。会話を空にすれば、やめたコードは見えなくなる。でも、決めたことまで見えなくなる。
さっきの公式の一文には、続きがあった。
Run
/clearand start fresh with a more specific prompt that incorporates what you learned.
会話を空にして、学んだことを入れた、もっと具体的なプロンプトで始め直す。
学んだことを、入れる。それを、どうやって新しい会話に持っていくか。
ベンチの横で、鷹宮さんが、鷹を拳に据えたまま、手帳に何か書いていた。表紙の角が丸くすり切れた、小さな手帳だった。
「何、書いてるんですか」
「今朝の分」
鷹宮さんは、書く手を止めずに、手帳をこっちへ少し傾けた。読んでいい、ということらしかった。
頁の上に、今日の日付と、遊園地の名前。その下に、短い行が並んでいた。フードコートの屋根。追い払った。観覧車の骨組み。追い払った。一行ずつ、場所と、やったことだけ。
何回やり直したかは、書いていなかった。屋根に二回行かれたことも、休ませたことも、見立てを外したことも、どこにもなかった。
「失敗したことは、書かないんですか」
「回数は書かない」
鷹宮さんは、手帳を引き寄せて、また書き始めた。
「やめたことは、書く」
頁の下のほうに、もう一行あった気がした。メリーゴーラウンド、とあった気がする。でも自分は、そこまで読まなかった。腕の時計を見たら、八時を回っていた。開門の放送のテストまで、一時間を切りかけている。早く、新しい会話を試したかった。
回数は書かない。何回失敗したかは、持っていかない。決めたことだけ、紙に書いて持っていく。会話は全部捨てて、決めたことだけを新しい会話に渡せばいい。
自分は、そう受け取った。
引き継ぎ書を作ることにした。引き継ぎ書は、新しい会話の最初に AI に読ませる、短い文書だ。前の会話を丸ごと渡すかわりに、続きをやるのに要ることだけを書く。
その材料として、まず自分の記録を書く。NOTES.md というファイルを作って、昨夜の七件を、票の番号ごとに書き出した。時間がないので、決まりの文は、大槻さんが票に書いた言葉をそのまま写した。失敗したことは、全部捨てた。決めたことだけを、理由つきで一行ずつ。
| |
一件目から七件目まで、どの票もこの形にした。## で始まる行が票の番号と件名で、その下の - 決定: の行が決めたこと、/ 理由: のあとが理由だ。形を決めておけば、プログラムで読める。決めたことは、七件分を全部書く。五件目を直し直す会話にも、ほかの票で決めたことを渡す。七件目の修正が、五件目のコードに触って戻したからだ。どの票で決めたことが、次の会話でどのコードに触るかは、前もって分からない。決めたことは一行ずつで短いので、全部渡しても長くならない。
読むほうを書いた。決めたことの一行を、型にする。
| |
task は票の番号、what は決めたこと、why は理由だ。読む関数は、こうなった。
| |
一行ずつ見ていく。match は、正規表現に合った部分を取り出す。正規表現の ^ は行の頭、$ は行の終わりを表す。/^## (\d+)\b/ は、行の頭が ## で、そのあとに数字が続く行に合う。\d+ が一つ以上の数字で、\b は数字の切れ目だ。合えば、head[1] に一つ目の丸かっこ (\d+) の部分、つまり票の番号が入る。それを、いまの票の番号として覚えておく。
- 決定: で始まる行なら、決めたことの一件にする。m[1] が決めたことの文で、m[2] が理由だ。(.*?) の ? は、なるべく短く合わせる、という印で、これがあるから決めたことの文は「 / 理由: 」の手前で切れる。? がないと、理由まで決めたことの文に飲み込まれてしまう。\/ は、スラッシュそのものを表す書き方だ(正規表現の外枠にスラッシュを使っているので、中では印を付ける)。(?: \/ 理由: (.*))? は、「 / 理由: 」から後ろがあれば理由として取り出し、なければないままにする。理由がなければ m[2] は空なので、?? '' で空の文字列にしておく。それ以外の行は読まない。記録の中に自由に書いたメモは、引き継ぎ書には入らない。
組み立てる関数は、決めたことを並べるだけにした。
| |
Task は、新しい会話でやるタスクの型で、types.ts に置いた。
| |
目的と、範囲外(今回はやらないこと)と、次の一手を持つ。公式のページにも、いちばん役に立つ仕様書は、関わるファイルを名指しして、範囲外を書いて、確かめ方で終わる、とあった。引き継ぎ書も、それに寄せた。map で決めたことを一行ずつの形にする。... は、配列の中身を、外側の配列に一つずつ広げて並べる書き方だ。
最初に直すのは、五件目だ。七件目の修正で戻ったのだから、先に五件目を直し直す。tasks.ts に、五件目のタスクを書いた。
| |
loadBoard は、待ち時間の画面に出すデータを読み込むサーバーの関数だ。七件目で AI が、整理券と待ち時間の読み込みをここにまとめた。tasks.ts には、七件目のタスクの task7 も同じ形で書いて、票の番号から引ける表 tasks にまとめてある。
乗り場の裏で書いた二本のテストを、この組み立てで作った引き継ぎ書に向け直した。
| |
notesDecisionsOnly は、さっきの決めたことだけを書いた NOTES.md の中身だ。乗り場の裏で書いたときは、「次の会話」と言っても、同じ会話の続きで次に頼むことを指していた。いまは、空にしたあとの新しい会話を指す。テストの名前は同じままで、向ける先が変わった。走らせると、二本とも緑だった。やめたコードは入っていない。決めたことは入っている。
自分は、ノートパソコンを抱えて、鷹宮さんのところへ行った。鷹宮さんは、まだ手帳に何か書き足していた。
「会話は捨てて、決めたことだけ持っていけばいいんですね。失敗したことは、全部捨てる」
鷹宮さんは、ボールペンを動かしたままだった。
「会話は、捨てる」
言ったのは、前半だけだった。
自分は小さく頷いて、ベンチに戻った。
会話を空にした。新しい会話で、最初にこう頼んだ。この引き継ぎ書を読んで、五番を直してほしい。プロンプトには @HANDOFF.md と書いた。@ のあとにファイル名を書くと、AI はそのファイルを読んでから答える(2026年10月18日時点の Claude Code の使い方。公式のページには「Reference files with @ instead of describing where code lives.」とある)。HANDOFF.md は、さっきの関数で組み立てた文字列を、その場は手で書き出したファイルだ(書き出すスクリプトは、あとでまとめた)。
組み立てた引き継ぎ書は、こうなった。
| |
新しい会話の AI は、引き継ぎ書を読み、loadBoard のコードを読んだ。そこには、七件目で入った cached('tickets', 60_000, readTickets) の行がある。AI は、こういう趣旨の案を返してきた。決まりには、運休のあとすぐ使えなくする、とある。整理券のキャッシュの時間を、いまの一分から十秒に縮めれば、運休はすぐに届く。画面の速さもあまり落ちない。
手が止まった。
それは、五件目で、もうやめたやり方だった。夜中に大槻さんに電話して、一分でも駄目だと言われて、やめた。十秒でも同じだ。その十秒のあいだ、運休したあとの整理券は使える。
AI が引き継ぎ書を読まなかったわけではなかった。決めたことの文は、ちゃんと読んでいる。そのうえで、「すぐ」を十秒のキャッシュで満たそうとした。決めたことの文には、反していない。決めたことは、どう作るかまでは縛っていなかった。昨夜の会話の中では、それを縛っていたのは、やめたことのほうだった。キャッシュの時間を縮めるのは、何秒でも駄目だ、という結論だ。
新しい会話は、それを知らない。引き継ぎ書には、決めたことしか書いていない。
なら、決めたことを、もっと細かく書けばいいのか。整理券の状態はキャッシュしない、と。それなら十秒の案は出なかったかもしれない。でも、決めたことの文は、何をするかを言うだけだ。その一つ手前にある、もっともらしい近道を、もう試して駄目だったとは言わない。次にまた別の近道が、たとえば、運休のときだけキャッシュを捨てる、といった形で出てきたら、決めたことの文だけでは、それが昨夜検討済みなのかどうかが分からない。やめたことの行は、その近道を、理由ごと閉じておく。だから、両方要る。
自分は、五件目の却下を忘れていたわけではなかった。昨夜の会話の中では、それは決めたことの一部だと思っていた。キャッシュしないと決めた、その裏側にあるものとして。だから「失敗は全部捨てる」と書いたとき、却下の結論まで一緒に捨てていた。経緯を捨てたつもりで、結論まで捨てていた。
さっき、鷹宮さんの手帳の頁の下のほうに、もう一行あった。
ベンチの向こうで、鷹宮さんが、鷹を拳に据えて、メリーゴーラウンドの前を歩いていくところだった。メリーゴーラウンドの屋根には、改装で新しくした電飾の線が、何本も渡してある。その上に、カラスが一羽とまっていた。
鷹が、屋根のほうへ、一度だけ首を向けた。
鷹宮さんは、放さなかった。拳を少し下げて、そのまま歩き続けた。メリーゴーラウンドの前を通りすぎてから、ようやく、売店の屋根のカラスに向けて鷹を放った。
さっき読み流した一行が、メリーゴーラウンド、だったのを思い出した。何と書いてあったのかは、読んでいない。でも、いま見た電飾の線と、鷹宮さんが下げた拳で、見当はついた。あの屋根では、放さない。線に、鷹の脚が絡むから。
あの一行は、失敗の記録じゃない。もうやらない、という決定だ。書いておかなければ、次にここで鷹を放す人は、あの屋根のカラスを見て、放しうる。今朝、鷹宮さんが一度でもそこで放したかどうかは、知らない。知らなくていい。回数は書いていない。書いてあるのは、やめたことと、その理由だけだった。
調べてみると、同じことを書いている人がいた。AI のコーディングエージェントを毎日使っている開発者が、記録の付け方を書いた記事だ(Dan Hopwood「The Memory Bank Framework」)。
Git records what changed but it’s blind to what you decided not to do.
git は、変えたことを記録する。でも、やらないと決めたことは見ていない。何かを検討してやめても、何も変わらないから、コミットは残らない。だから、どこかに書いておかないと、AI は会話を新しくするたびに、同じ案を出してくる。そう書いてあった。実験ではなく、一年使ってきた人の経験として。
公式の「学んだことを入れる」は、こういうことだったんだと思う。やり直した経緯ではなく、学んだことの結論。その結論には、決めたことだけじゃなく、やめたことも入る。
自分は、NOTES.md に却下の行を足した。
最初は、念のため、全部残そうとした。やめたことを残すと決めたのだから、何をやってなぜ駄目だったのかも、そのまま残したほうがいいと思った。五件目の二回目の、キャッシュを一分にしたコードと、そのときのテストのログを、却下の行の下に貼りつけた。
貼りつけてから、乗り場の裏で書いたテストのことを思い出した。却下したコードは、次の会話に渡さない。あのテストは、まだここにある。やめたコードを記録に貼れば、記録は昨夜の会話と同じものになっていく。parseNotes は自由な行を読まないから、貼ったコードは引き継ぎ書には入らない。それでも、記録には残る。記録は、次の会話を始めるたびに自分が開くし、AI に直接読ませることもあるかもしれない。そのたびに、やめたコードがまた目に入る。昨夜の会話の中で、やめたコードが四回、決めたことが一回だったのと同じことが、記録の中で起きる。記録は、次の会話の材料だ。日記じゃない。
まず、記録を読む関数を、やめたことの行も読めるように広げた。notes.ts に、こう書いた。記録の一行の型も、決めたことか、やめたことかを持つようにした。
| |
| |
parseDecisions との違いは、この三行だけだ。(決定|却下) は、どちらかの語に合う。合った語が m[1] に入るので、それで kind を決める。中身は m[2]、理由は m[3] にずれる。? : は、条件が真なら前、偽なら後ろの値になる書き方だ。
記録の不備を見つける関数も、notes.ts に足した。
| |
checkReasons は、理由のない行を見つける。理由のない決定は、もっともらしい別の案と並べられたとき、守る根拠がない。理由のない却下は、状況が変わったときに、外してよいのかどうかを誰も判断できない。たとえば、運休の放送の仕組みが変わって、来園者に知らせが届くのが遅くなったら、一分のキャッシュは許されるのか。理由が「一分あれば乗り場に着く」と書いてあれば、そこから考え直せる。だから、決定にも却下にも理由が要る。
checkNotes は、それに加えて、記録の中のコードのかたまり(行頭の ```)と、テストのログの行(行頭の ok か not ok )と、却下の行の下に字下げして続けた行を見つける。forEach は配列の要素を一つずつ順に処理する書き方で、i は行の番号(0から数える)だ。見つけたら、何行目に何があったかを、説明の一覧に足していく。/^\s+\S/ は、行の頭に空白が一つ以上あって、そのあとに空白でない文字が続く行、つまり字下げした行に合う。コメントの ponytail: は、自分がこのプロジェクトで決めている印で、わざと簡単にしてあるところに付ける。ここでは、目印しか見ていないことを書いてある。貼られたものを全部見分けるものではない。
貼りつけた記録に当ててみた。
| |
全部、見つかった。自分は貼ったものを消して、却下を一行に書き直した。鷹宮さんの手帳の一行と、同じくらいの長さに。
| |
組み立てる関数も、書き直した。after.ts に、こう書いた。
| |
決めたことの節の下に、やめたことの節を足した。理由のない行が一つでもあれば、throw で例外を投げて、組み立てない。例外は、呼んだ側の処理をそこで打ち切って、何が起きたかを知らせる仕組みだ。コードのかたまりやログの行は、記録のファイルを読んだところで、checkNotes が先にはじく(あとで書く handoff.ts がそうする)。どちらかに引っかかった記録からは、引き継ぎ書を作らない。
そして、この関数は、会話を受け取らない。引数は、タスクと、記録だけだ。
テストを一本足した。やめたことの結論を、理由つきで、一行ずつ渡すこと。
| |
REJECTED_LINE は、引き継ぎ書に出るはずの一行(「- キャッシュの時間を短くして間に合わせる(何秒にしても、そのあいだは運休のあとの整理券が使える)」)だ。同じテストを、さっきの決めたことだけの組み立てに向けると、赤になった。やめたことの節が、そもそもないからだ。新しい buildHandoff に向けると、緑になった。
もう一本、確かめておきたいことがあった。昨夜の七件のうち、一件目から四件目と六件目は、失敗せずに一回で直った。AI が正しく仕事をした作業だ。そういう作業では、会話を続けていたときに AI が読んでいたものと、引き継ぎ書とで、決めたことは同じく入っていてほしい。違うのは、捨てるもの、つまりやり直しの経緯とログとやめたコードだけのはずだ。
| |
quietConversation() は、失敗のなかった五件だけの会話で、notesQuiet は、その五件の記録だ。タスクに task5 を渡しているのは、どれでもいいからで、ここで見ているのは決めたことの節だけだ。五件の決めたことが、どれも会話のほうにも、引き継ぎ書のほうにも入っている。会話を区切っても、正しくやった仕事の決めたことは、一つも落ちない。
ほかにも、引き継ぎ書にテストのログの行や、五件目の一回目・二回目の AI の説明の文が入らないこと、範囲外と次の一手が入ることを、テストにした。
自分は、メモの新しい行に書いた。
引き継ぎ書を、会話から作らない。会話を詰めた要約からも作らない。自分が書き留めた記録から作る。持っていくのは、決めたことと、やめたこと。どちらにも、理由をつける。何回やり直したかは、持っていかない。それは git と、空にする前の会話の中にある。
AI に、昨夜の会話を要約させて、引き継ぎ書を書かせることもできる。でもそれでは、何を残すかを決めるのが、また AI のほうになる。要約して詰めるのと、同じことだ。だから、記録は自分で書く。
昨夜の会話のまま頼む道と、記録から組み立てて新しい会話へ渡す道を、並べて描いてみた。

記録から渡す道で、自分が書くのは記録だけだ。そこから先は決まった手順で組み立てて渡し、記録が崩れていれば途中で止まる。破線から先、AI が引き継ぎ書を読んでどう直すかは、テストの外にある。
会話を空にした。新しい会話で、直した記録から組み立てた引き継ぎ書を読ませて、もう一度、五番を頼んだ。AI は、引き継ぎ書のやめたことの節を読んで、整理券の読み込みをキャッシュから外し、毎回サーバーから読む形に戻した。今朝のこの一回は、そうだった、というだけだ。
続けて、会話をもう一度空にした。七件目の引き継ぎ書を組み立てて、新しい会話で、待ち時間の画面を速くする件を頼んだ。AI は、待ち時間だけをキャッシュに通し、整理券は毎回読む形のまま、画面を速くした。これも、このときの一例だ。
売店の屋根のカラスがいなくなっていた。鷹宮さんは、観覧車のほうへ、もう一度、鷹を放った。
第4幕: 検める ── 毎回、書き直す
テストを、全部走らせた。
| |
全部、緑だった。
テストが本当に効いているかを確かめるために、仕掛けを一つずつわざと壊してみた。壊したのに緑のままなら、そのテストは何も見ていない。組み立てる関数がやめたことの行を読まないようにする。会話も一緒につなぐようにする。記録の不備を見つける関数が、何を渡されても空の一覧を返すようにする。理由の区切りを無視して、行全体を決めたことの文にしてしまう。決めたことを、最後の票の分だけにする。どれを壊しても、そのたびに赤が出た。壊したところは、元に戻した。
自分は、鷹宮さんと一緒に入口のほうへ戻った。八時半すぎに、大槻さんが事務所から出てきた。何も言わずに、発券機の一台の前にしゃがんで、点検用の画面を出した。運休のボタンを押す。その回の整理券の番号を読ませる。画面に「無効」と出た。
大槻さんは、五番の票の、赤い「再発」の横に、ボールペンで何か短く書き足した。何と書いたのかは、自分の位置からは見えなかった。大槻さんは票を自分に返して、事務所へ戻っていった。
自分は、ゲートの脇の植え込みの縁に座って、記録から引き継ぎ書を書き出すスクリプトを仕上げた。handoff.ts という名前にした。ファイルの先頭に、この組み立てが確かめていないことを、コメントで並べた。
| |
process.argv[2] は、コマンドの後ろに書いた最初の引数だ。readFileSync('NOTES.md', 'utf8') は、ファイルを文字列として読む('utf8' は文字の符号の種類)。node handoff.ts 5 と打てば '5' が入り、tasks.ts から五件目のタスクを選ぶ。記録に不備があれば、checkNotes の説明を出して、終了コード1で終わる。終了コードは、プログラムが終わるときに返す数で、0なら成功、0以外なら失敗だ。不備がなければ、HANDOFF.md を書き出す。
| |
コメントに書いたことを、もう一度、自分の言葉で並べておく。
この引き継ぎ書を渡せば、AI が前の修正を戻さなくなる、とは言えない。今朝、直した引き継ぎ書で始めた二つの会話で、AI が整理券をキャッシュに戻さなかったのは、このときの一例だ。AI の振る舞いは、毎回同じとは限らない。
記録の中身が正しいことも、確かめていない。決めたことが間違っていれば、間違いをそのまま渡す。書き忘れた決定は、渡らない。会話の中で決めたのに記録に書かなかったことは、引き継ぎ書には入らない。書いたのは自分で、確かめるのも自分だ。
テストが確かめているのは、自作の fixture に対して、自作の関数が組み立てる文字列の形までだ。本物の Claude Code が引き継ぎ書をどう読むかは、確かめていない。/clear や @ の扱いが改訂で変われば、この手順は黙って効かなくなりうる。自動の圧縮の要約に何が残るかも、この記事では再現していない。fixture の頭には、参照した公式のページと日付を書いた。
スピーカーから、ざらついた音がして、放送の係の人が、マイクのテストを始めた。開門の放送のテストまで、あと二十分だ。
鷹宮さんが、鷹を拳に据えたまま、腰の袋の口を締めていた。次の現場へ行く支度らしかった。
自分は、植え込みの縁から腰を上げた。
「書いた紙、次の会話でも使い回していいんですか。前のを書き足して」
鷹宮さんは、手帳を閉じかけた手を止めた。
「毎回、書き直す」
「毎回?」
「現場ごとに、頁を新しくする」
それだけだった。理由は足さなかった。
毎回、書き直す。
HANDOFF.md は、育てていくファイルにしない。新しい会話を始めるたびに、記録から組み立て直して、使ったら消す。前の引き継ぎ書を書き足して使い回すと、いつのまにか、もう効いていない決定や、終わったタスクの次の一手が混ざってくる。組み立て直せば、そのたびに、記録のいまの中身だけが入る。
記録をそのまま @NOTES.md で読ませないのは、組み立てる途中で三つのことをしているからだ。自由に書いたメモを落とす。範囲外と次の一手を足す。理由の抜けや、貼ったコードをはじく。
指示書の CLAUDE.md に、HANDOFF.md を読み込ませる手もある。CLAUDE.md の中に @ でファイル名を書くと、そのファイルも一緒に読み込まれる。でも、公式のページに、それに当てはまる一文があった。
Imports help you organize a long file but don’t reduce its context cost, because imported files also load at launch.
読み込ませたファイルも、会話を始めるときに毎回一緒に載る。それでは、使い捨てにならない。終わったタスクの引き継ぎ書が、関係のない次の会話にまで、ずっとついてくる。
ただ、ずっと守る決まりもある。整理券の状態はキャッシュしない。これは、五番の票が片づいても、来年も再来年も変わらない。そういうものだけは、指示書に書く。指示書は、毎回の会話の始めに読み込まれる。公式のページの言葉では、こうだ。
Both are loaded at the start of every conversation.
(「Both」は、指示書の CLAUDE.md と、Claude Code が自分で書き残す記憶のことだ。2026年10月18日時点の公式の説明。)
指示書に、一行だけ足した。
| |
指示書は、長くすると守られなくなる。だから、ずっと守るものだけを短く足す。作業の途中のことは、記録に書いて、引き継ぎ書で渡して、終わったら消す。決めたことが変わったら、記録を直すのは自分だ。
昨夜の会話に出てきたものを、どこへ持っていくかで分けた。

持っていく先は二つで、どちらも書くのは自分だ。持っていかないものも、消えたわけではない。要るときは、git と前の会話を開けばいい。
1タスク1セッションは、いつでも守る決まりでもない。公式のページにも、こうある。
Sometimes you should let context accumulate because you’re deep in one complex problem and the history is valuable.
一つの込み入った問題の途中なら、会話を積み重ねたほうがいいこともある。そのやりとり自体に価値があるからだ。でも、昨夜の七件は、関係のない七つのタスクだった。次からは、票一枚ごとに会話を一つにする。そして、昨夜の五件目のように、一つの票で二回直して直らなかったら、そこでも会話を空にして、学んだことを記録に書いてから始め直す。
鷹宮さんは、手帳の今日の頁を、根元から破り取った。
破った頁を裏返して、そこにボールペンで何か書き始めた。立ったまま、手帳を下敷きにして。四行、書いたように見えた。書き終えると、その一枚を、自分に差し出した。
「どうぞ」
自分は受け取った。鷹宮さんは、手帳の次の頁を開いて、いちばん上に何か書きながら、通用口のほうへ歩いていった。拳の上で、鷹が一度だけ羽を広げて、またたたんだ。次の現場の名前を書いていたんだと思う。読めなかった。
自分は、発券機の横のカウンターに、その一枚を置いた。
表には、今朝の記録が残っていた。フードコートの屋根。追い払った。観覧車の骨組み。追い払った。売店の屋根。追い払った。その下に、メリーゴーラウンドの屋根。放さない。電飾の線。
裏に、四行。
表と裏を、二回ずつ見比べた。表には、やったことと、やめたことが書いてある。裏には、それをどう任せるかが書いてある。表のどこにも、屋根に二回行かれたことは書いていなかった。
裏の、四行目の下が、一行ぶん空いていた。自分は、ボールペンで、そこに書いた。
スピーカーから、開門の放送のテストが流れ始めた。本日はご来園ありがとうございます。誰もいない入口に向かって、放送の声が、まっすぐ伸びていった。
放鷹カード
- 合図: 1タスク1セッション。票一枚ごとに会話を一つ。始めるときに、記録から組み立てた引き継ぎ書を読ませる
- 検め方: 引き継ぎ書に、決めたことと、やめたことの結論が、理由つきで一行ずつ入っているか。やり直しの経緯、ログ、やめたコードが入っていないか
- 止めないもの: 決めたことと、その理由は、一つも落とさずに次の会話へ渡す。やめたことも、結論と理由を一行で渡す
- 鷹のせいじゃなかった: 七件を一つの会話で続けた。要約されたなら整理された、と思った。会話を区切ったあとは、やめたことまで捨てた
- 自分の言葉で: 持っていかないのは、何回やったか。持っていくのは、何をやめたか。引き継ぎ書は、会話を始めるたびに記録から組み立て直す
