---
title: "Project Database — 構造化した成果を保存・検索する"
description: "schemaの作成から行の保存・検索・WebUIでの確認、SQL制限、migration競合、書き込み結果不明時の復旧までを説明します。"
---

# Project Database — 構造化した成果を保存・検索する

Project Databaseは、顧客記録、調査結果、在庫など、agentと人間が再利用する構造化データの保存先です。行の背景や判断は[Shared Documents](/ja/docs/shared-documents)に残します。通常Projectごとにserver管理のCloudflare D1 databaseを持ち、最初のmigrationで必要になった時点で作成されます。schemaを読むだけでは作成されません。ローカルSQLiteファイルや、[HTML](/ja/docs/html)に自動接続されるbackendではありません。

## 前提と権限

active Projectをcoverageに持つrunning Sessionのagentへ操作を依頼します。SessionのProject roleがViewerならschemaとSELECTを読めます。CollaboratorとAdminはmigration、INSERT、UPDATE、DELETEも実行できます。archived、completed、deletingのProjectは新規Database操作を受け付けません。

`chat db`はSession内のコマンドです。Team、Project、Database ID、Sessionを選ぶflagはありません。`schema`と`query`は現在のディレクトリ、`migrate`はcanonical migration file pathからProjectを解決します。例はProject rootで実行してください。外側の`aachat project database`はcatalogの取得用であり、任意SQLの実行口ではありません。agentへD1のcredentialを直接渡す必要はありません。

## tableを作り、成果を1件保存する

`acme/customer-research`をSessionのcoverageにある実在Projectへ置き換え、まず現在のschemaを読みます。
```sh
cd aachat/projects/acme/customer-research
chat db schema
```

read-onlyの`db/schema.sql`投影は、空のDBなら`-- aachat-schema-version: root`、migration後なら`sha256:...`のversionで始まります。このファイルは編集しません。以下は空のDBであることを確認してから、`db/migrations/add_customer.sql`へそのまま保存する例です。既存schemaがある場合は、parent headerへ**現在のversion全体を正確にコピー**し、既存tableに合わせてSQLを修正してください。`root`を再利用しません。
```sql
-- aachat-parent: root
CREATE TABLE customer (
  id TEXT PRIMARY KEY,
  name TEXT NOT NULL,
  status TEXT NOT NULL
);
CREATE INDEX customer_status ON customer(status);
```

parent headerはbyte 0から始めます。BOMやNULを含まないUTF-8の通常ファイルを使い、symlinkは使いません。migration IDは英小文字・数字・`_`・`-`、先頭は英小文字または数字、最大128文字です。ファイル同期はSQL sourceの保存であり、**DBへの適用ではありません**。明示的に適用してから行を書き、読んで確認します。
```sh
chat db migrate db/migrations/add_customer.sql
chat db schema
chat db query --sql 'INSERT INTO customer (id, name, status) VALUES (?, ?, ?)' --params '["c1", "Ada", "active"]'
chat db query --sql 'SELECT id, name, status FROM customer WHERE status = ? ORDER BY id LIMIT 100' --params '["active"]'
chat db query --sql 'UPDATE customer SET status = ? WHERE id = ?' --params '["reviewed", "c1"]'
chat db query --sql 'SELECT id, name, status FROM customer WHERE id = ?' --params '["c1"]'
```

migration成功後はschemaのversionが変わり、tableとindexが現れます。最初のSELECTは`c1`、`Ada`、`active`を返し、UPDATE後の最後のSELECTは`c1`、`Ada`、`reviewed`を返します。各段階の実際のJSON応答を読んでから次へ進みます。同じ主キーでINSERTを繰り返しても新しい記録にはなりません。捨ててよい練習用の行なら、`chat db query --sql 'DELETE FROM customer WHERE id = ?' --params '["c1"]'`で削除し、同じキーのSELECTで不在を確認します。

## queryとmigrationのSQL規則は異なる

両方ともSQLは最大**100 KiB**です。予約された`_aachat_`、`sqlite_`、`_cf_`、`pragma_`のrelation、database名で修飾したobject名、任意のSQLite拡張は使えません。

| 操作 | 許可する入力 | 主な制限 |
|---|---|---|
| `chat db query` | SELECT、INSERT、UPDATE、DELETEのいずれか1文。RETURNINGとOUTPUTは不可 | placeholderは位置指定`?`のみ。`--params`は最大100個のstringのJSON配列で、placeholder数と一致させる |
| `chat db migrate` | 下記allowlistの1〜100文 | parameter bindingは不可。現在のparent versionが必要 |

queryでDDLやPRAGMAは実行できません。SELECT結果は最大**1,000行かつdecoded 1 MiB**で、超過は部分結果ではなくエラーです。必要なcolumn、WHERE、小さなLIMITを指定します。LIMITを指定するなら静的な非負整数を使い、placeholderや式にしません。queryのUPDATEとDELETEではORDER BYやLIMITを使えません。

migrationのallowlistは、通常のCREATE TABLEとCREATE INDEX、columnの追加・削除・改名またはtable改名のALTER TABLE、単一objectのDROP TABLEまたはDROP INDEX、RETURNINGとOUTPUTを伴わないINSERT・UPDATE・DELETE、そして**`PRAGMA defer_foreign_keys = ON`**です。このPRAGMAはmigrationだけの例外です。SELECT、任意PRAGMA、view、trigger、明示的なtransaction制御scriptはmigration操作ではありません。allowlistに含まれていてもSQLiteの全方言を許可するわけではありません。

## WebUIで結果を見る

Projectの**Database**タブを開き、tableを選んでcolumnと行を確認します。閲覧専用で、SQL editorや更新操作はありません。column見出しをクリックするとsortが切り替わり、1ページ25・50・100行（既定100行）で移動できます。table、page、page size、sortはURLに保持されます。

データ変更時は**Updates available**が表示され、refreshで新しい行を読み込みます。schema変更時はcatalogを再取得します。空文字、NULL、BLOBは別の値として表示されます。1 MiB超過なら25行へ減らし、それでも失敗する場合はagentへcolumnや値を絞ったSELECTを依頼します。

## 書き込みを重複させずに復旧する

応答の`error`、`reason`、次のactionを読みます。timeoutだけでは書き込み失敗と判断できません。

| 症状 | 次の操作 |
|---|---|
| Projectを解決できない・権限がない | Project-rootのcwd、Session coverage、active状態、agentのroleを確認します。ファイルを移しても権限は増えません。 |
| migration sourceが拒否された・見つからない | `db/_errors.md`を読み、path、header、encoding、SQLを修正し、同期が受理してから適用します。 |
| parent versionが古い | current schemaを読み、そのversion向けにmigrationを作り直します。1つのparentを進める子は1つで、force操作はありません。受理済みsourceは変更しません。 |
| migrationがreconciling | 応答のread・復旧案内に従います。serverがoperationとprovider ledgerを照合し、解決までschema読取と次のmigrationを待たせます。SELECT/DMLは通常の受付条件で続行できる場合があります。 |
| DML後に`OPERATION_OUTCOME_UNKNOWN` | 既知の主キーと現在値を読んで、追加書き込みが必要か判断します。INSERT・UPDATE・DELETEを盲目的に再送しません。 |
| queryがbusy | 他のlive queryがProject leaseを保持しています。retry案内に従い、並列書き込みで回避しません。 |
| 結果が大きすぎる | columnと行を絞り、区切って読みます。途中までのデータを全件と見なしません。 |

ローカルのmigrationやschemaファイルを消しても適用済みデータは戻りません。Project削除は受理済みqueryと未解決migration、物理DB削除の完了を待ちます。即時rollbackやbackupの仕組みではありません。[信頼境界](/ja/docs/trust-boundary)と[Projects](/ja/docs/projects)も確認してください。

## 空の状態とセルの全文

WebUIのDatabaseで**No database yet**と表示されたら、最初のmigrationでDBを初期化していません。上の手順でAgentにmigrationの作成と適用を依頼します。**No tables yet**ならuser tableがない状態なので、migrationでtableを作成して表示を更新します。schemaを読むだけではDBは初期化されません。

NULL以外のセルを押すと値の全文を開け、必要なら**Copy**を使えます。空文字はNULLと区別され、詳細を開けますが、NULLのセルには詳細buttonがありません。ここはread-onlyの確認面であり編集画面ではありません。Agentが行やschemaを変更した後は、Refresh/Updates availableで現在の状態を読み直します。
