Claude Media
Claude CodeでQuartoレポートを作る — quarto renderで失敗を拾わせる

Claude CodeでQuartoレポートを作る — quarto renderで失敗を拾わせる

Claude CodeにQuartoのレポートを書かせるとき、quarto renderの成否を完了条件にする手順です。CLAUDE.mdの規約とStop hookの例を載せます。

Quartoのレポートは「書けた」ではなく「renderが通った」で判定する

Quarto(.qmd)は、Markdownの中にPythonやRのコードチャンクを埋め込み、quarto render のたびにそのコードを実行してHTMLやPDFを作ります。つまりレポートの正しさは、文章よりも先に「全チャンクが最後まで実行できるか」で決まります。

Claude Codeにレポートを書かせると、文章と図の体裁は整った .qmd がすぐ出てきます。ただし、書いた本人であるClaudeが実行していなければ、列名の打ち間違いやライブラリ未導入のような失敗は、あなたが初めてrenderした瞬間に出ます。

そこで、「renderが成功するまで終わりにしない」という条件をClaudeに持たせます。この記事の進め方は次の3段階です。

手順

render成功を完了条件にする3段階

  1. 1

    CLAUDE.mdに規約を書く

    変更後に quarto render を実行し、エラーが消えるまで直すよう明文化します。

  2. 2

    renderの引数を決める

    対象ファイルと出力形式を固定し、必要ならキャッシュ更新とデバッグ出力のフラグを足します。

  3. 3

    Stop hookで強制する

    指示を忘れても、renderが失敗している間はClaudeが止まれないようにします。

失敗を拾えるかどうかはQuartoの既定動作で決まる

Quartoの実行オプションには error があります。error: true を指定すると、コードの実行エラーを出力に含めたうえで、文書の処理を止めません。逆に言えば、既定ではチャンクのエラーでrenderが止まります。Claudeに失敗を拾わせるには、この既定のままにしておくのが前提です。

「renderが通った」と言えるのは、コードが実際に実行されたときだけです。実行されない、または前回の結果を使い回す条件が3つあります。

落とし穴

renderが通っても実行されていない3つの条件

  • ipynbは既定で実行されない

    .ipynb をrenderするとき、セルは既定では実行されません。実行するには --execute を付けます。ノートブックを入力にするなら、この指定が要ります。

  • freezeが有効

    execute: freeze: true を指定した文書は、プロジェクト全体のrenderで再実行されません。freeze: auto は、ソースが変わったときだけ再実行します。ファイル単体のrenderでは常に実行されます。

  • cacheが有効

    execute: cache: true では、チャンクのソースが変わらない限り前回の結果が使われます。入力データだけが変わったときは --cache-refresh で更新します。

Claudeにチェックさせるときは、ファイルを1つずつ指定して quarto render を呼ばせます。プロジェクト全体の再renderでは freeze に守られた文書を素通りするので、検証として弱くなります。

CLAUDE.mdに書く規約

次のような断片をプロジェクトの CLAUDE.md に置きます。例として、Python版のレポートを想定した書き方です。

## Quarto レポート
 
- `.qmd` を変更したら、完了前に必ず `quarto render <file>.qmd --to html` を実行する
- render が失敗したら、エラー出力の最終行とチャンクの位置を読み、原因を直して再実行する
- 失敗を隠すために `error: true` や `eval: false` をチャンクに足さない
- 図や表の数値を文章に書くときは、render した出力の値を転記する(推測で書かない)

最後の項目は、文章と計算結果のずれを防ぐためのものです。Quartoにはコードの値を文中に埋め込むインラインコードもありますが、まず転記ルールを置くだけでも、Claudeが記憶で数値を書く場面は減ります。

renderの引数と、失敗時に読む出力

quarto render の基本形は次のとおりです。対象ファイルは、コマンドラインの最初の引数に置きます。

quarto render report.qmd --to html
quarto render report.qmd --to html --cache-refresh
quarto render report.qmd --to html --execute-debug

形式を省くと、文書のYAMLに書かれた全形式をrenderし、何も書かれていなければHTMLを作ります。--to pdf のように指定すれば形式を絞れます。チェック用途なら、最も軽い形式を1つ決めておくと速く回ります。

失敗の調査には次のフラグが使えます。

フラグ役割
--execute-debug役割コード実行時のデバッグ出力を表示する
--cache-refresh役割実行キャッシュを強制的に更新する
--no-cache役割キャッシュを使わない
--debug役割render後も中間ファイルを残す
--log <path> / --log-level役割ログをファイルへ書き、レベルを debug 〜 critical から選ぶ
--quiet役割コンソール出力を抑える

Claudeに読ませたいのは失敗の原因なので、--quiet は付けません。ログが長いときは、末尾を読むよう指示するか、--log でファイルに逃がします。

Stop hookでrender成功を強制する

CLAUDE.mdは「お願い」です。確実にしたいときは、Claude Codeのhooksで Stop イベントにrenderを掛けます。Stop hookの終了コードが2だと、Claudeは止まれず作業を続けます。標準エラー出力に書いた内容が、続行の理由としてClaudeに渡されます。

.claude/hooks/quarto-render-check.sh の例です。変更された .qmd だけをrenderします。

#!/bin/bash
# 変更・追加された .qmd だけを render して、失敗したら Claude に戻す
files=$(git status --porcelain -- '*.qmd' | awk '{print $2}')
[ -z "$files" ] && exit 0
 
for f in $files; do
  if ! out=$(quarto render "$f" --to html 2>&1); then
    echo "quarto render に失敗しました: $f" >&2
    echo "$out" | tail -n 30 >&2
    exit 2
  fi
done
exit 0

chmod +x で実行権限を付け、.claude/settings.json に登録します。

{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/quarto-render-check.sh"
          }
        ]
      }
    ]
  }
}

このスクリプトは、renderが失敗したときに終了コードが0以外になることを前提にしています。登録する前に、わざとチャンクを壊した .qmd で quarto render を実行し、echo $? で終了コードを確かめておくと安全です。

無限に続けさせないための上限

Stop hookが続行を指示し続けると、ループになりかねません。Claude Codeには、Stop hookによる続行が連続で8回に達すると次のブロックを無視して終了させる上限があります。ツールを呼ぶたびに連続回数はリセットされます。上限は環境変数 CLAUDE_CODE_STOP_HOOK_BLOCK_CAP で変更できます。

Stop hookの入力には stop_hook_active というフラグもあり、すでにhookによる続行中のときは true になります。「2回目以降は検証しない」と割り切るならこの値で分岐できます。ただし、直した結果を再度renderで確かめたいレポート用途では、分岐を入れずに上限に任せるほうが目的に合います。

編集のたびではなく、止まる直前に回す

編集直後に走る PostToolUse で .qmd ごとにrenderすることもできます。ただしデータの読み込みが重いレポートでは、1回の作業中に何度もrenderが走り、待ち時間が積み上がります。PostToolUse でのexit 2は、すでに実行済みのツールの結果を変えず、標準エラー出力をClaudeに見せるだけです。作業の区切りで一度だけ確かめたいレポートには、Stop が向いています。

テストを回す用途との使い分けはhooksでテストを自動実行する記事で扱っています。Lintを編集直後に直す構成はPostToolUse hookのLint自動修正が参考になります。

renderコマンドの許可を先に出しておく

Claudeが作業中に自分で quarto render を呼ぶときは、通常のBashコマンドとして許可を求められます。確認を省きたい場合は、.claude/settings.json の許可ルールでコマンドを限定します。

{
  "permissions": {
    "allow": ["Bash(quarto render *)"]
  }
}

許可は quarto render に限り、任意のシェルコマンドまで広げないのが要点です。

つまずいたときの切り分け

renderが失敗する、あるいは成功しているのに結果がおかしいときは、上から順に確かめます。

切り分け

renderが通らない・結果が古いときの切り分け

  1. Qエラー出力にチャンクの位置が出ていますか?
    答えが出ていない

    --execute-debug を付けて再実行し、コード実行時のデバッグ出力を出させます。

    答えがはい
  2. Q同じ環境でPythonやRのパッケージが読み込めますか?
    答えが読み込めない

    PythonのチャンクをrenderするにはPython 3と jupyter パッケージが要ります。ClaudeにはCLAUDE.mdで、使う仮想環境を有効化してからrenderするよう伝えます。

    答えがはい
  3. Q図や表が古いままではありませんか?
    答えが古いまま

    cache: true なら --cache-refresh を、freeze を使っているなら _freeze ディレクトリの削除を検討します。

    答えがはい

render成功を再現できる状態

同じコマンドを誰が実行しても、同じ成否になります。

{python} と {r} のチャンクが同じ文書に混在していると、QuartoはKnitrエンジンとreticulate Rパッケージで両方を実行します。意図せず別のエンジンが選ばれて失敗する場合は、YAMLに engine: jupyter か engine: knitr を書いて固定できます。

作業ディレクトリにも注意が要ります。プロジェクト内の文書をrenderするとき、作業ディレクトリは既定でその文書のあるディレクトリです。data/input.csv のような相対パスは、.qmd からの位置で書きます。プロジェクトのルート基準にしたいときは、_quarto.yml に project: execute-dir: project を書きます。Claudeがルートから quarto render sub/report.qmd を呼んでも、相対パスの解決先が変わらないよう、先にここを決めておきます。

まとめ

ここまでの内容を一言でいうと、Quartoは「実行できたかどうか」をrenderの成否に集約できる形式です。その成否をClaudeの完了条件にする仕組みが、CLAUDE.mdの規約とStop hookの組み合わせです。error: true・freeze・cache が検証をすり抜ける3つの経路なので、規約にはこの3点を先に書いておくと、後から「通ったのに中身が古い」という手戻りを減らせます。

Jupyterノートブックを直接編集させる場合の仕組みは、Claude CodeでJupyterノートブックを編集する仕組みにまとめています。

この記事を共有:XはてブLinkedIn