11 KiB
chktime から n8n の work_log へ作業区間を送る — 設計
日付: 2026-10-12 対象: chktime (このリポジトリ) と n8n (https://n8n.yfox.jp) の work-tracker
目的
Claude Code での作業を、手で「作業開始・終了」を記録しなくても n8n の work_log に入るようにする。
入った記録は、手で記録したものと同じく、日報・月報・ガント・ダッシュボード・Redmine 工数同期
(work-tracker--redmine-sync) で使われる。
成功の条件
- chktime の画面で期間を選ぶと、Claude のセッションから作った「作業区間」が一覧で出る。
- 区間ごとにタスク名 (Redmine のチケットを選べる) を決め、選んだものだけを
work_logに送れる。 - 同じ区間を二度送っても、
work_logに二重に入らない。 - すでにある記録 (手で記録したもの・他の区間) と時間が重なる区間は、画面で分かり、最初は送らない状態になっている。
- chktime に渡す権限は「work_log への取り込み」だけ。n8n の API キーは chktime に持たせない。
前提と決めたこと
| 項目 | 決定 |
|---|---|
| 目的 | 記録の手間を減らす |
| 時間の数え方 | 連続した区間ごと (空き 30 分以上で切る) |
| タスク名 | 送る前に chktime の画面で決める |
| 重なり | 画面で知らせ、最初は送らない。送るかは利用者が決める |
| 送り方 | 案 A: n8n に受け口のワークフローを作り、chktime から呼ぶ |
1. 区間の作り方 (chktime)
材料は chktime の既存の「ターン」(人間の発言 1 回と、それに対する応答一式)。
- 同じセッションの中で、前のターンの応答完了から次の発言までの空きが 30 分未満 ならつなぐ。
30 分以上なら区間を切る。
- 開始 = 区間の最初の発言の時刻、終了 = 区間の最後のターンの応答完了の時刻。
- 5 分未満 の区間は一覧から外す。
- ターンの起点は既存の規則どおり (人間の発言だけ。サブエージェント・再開時のメタ行は起点にしない)。
dateは区間の開始時刻のローカル日付 (日本時間)。日をまたぐ区間も開始日の記録。- 30 分と 5 分は設定 (
n8n.jsonのgapMinutes/minMinutes) で変えられる。画面から変更できる。 - セッションの最後の区間で、最後のターンがまだ応答中 (ログの最終更新から
gapMinutes経っていない、 または最後のターンに応答完了が無い) のものは「進行中」とし、送れない。 source=chktime:<セッションID>:<区間開始の epoch ミリ秒>。同じログからは毎回同じ値になる。
区間の計算は、HTTP やファイル読み込みから切り離した純粋な関数 (ターンの列と設定値 → 区間の列) にする。
2. n8n 側
データテーブル
work_logにsource(string) の列を足す。chktime から入れた行だけ値が入り、手で記録した行は空。 既存のワークフローはこの列を書かないので影響しない。work_log_deletedにもsourceを足し、work-tracker--delete のBackup Rowで一緒にコピーする。
ワークフロー work-tracker--import
両方の Webhook に Header Auth の認証情報を付ける (ヘッダー X-WT-Token)。違えば n8n が 403 を返す。
GET /webhook/wt-import-rows?from=YYYY-MM-DD&to=YYYY-MM-DD
from/toを確かめる (形式、from <= to、最大 62 日)。不正なら{ok:false, error}。work_logからdateがfrom の前日〜toの行を返す (前日を含めるのは、日をまたぐ記録との重なりを見るため)。- 返す値:
{ok:true, rows:[{id, date, start_time, end_time, task, status, source}], deletedSources:[...]}deletedSourcesは同じ期間のwork_log_deletedのsource(空でないもの)。
POST /webhook/wt-import
- 本文:
{rows:[{source, date, start_time, end_time, task, memo}]}、1 回に最大 200 件。 - 1 行ずつ確かめる:
sourceがchktime:で始まる (500 文字以内)dateがYYYY-MM-DD、start_time/end_timeが ISO 8601 で、終了 > 開始、長さ 24 時間以内taskが空でない (500 文字以内)、memoは 2000 文字以内
work_logに同じsourceがある行はスキップ。- 新しい行は
status: "done"、duration_min= 終了 − 開始 (分、四捨五入、丸めなし) で追加。 - 返す値:
{ok, inserted:[{source, id}], skipped:[{source, reason}], errors:[{source, error}]}。okは errors が 0 件のとき true。
認証情報
合言葉は利用者が決める。利用者が n8n の画面で Header Auth の認証情報 (例: 名前 wt-import token、
Name X-WT-Token) を作り、chktime の設定画面にも同じ値を入れる。Claude は合言葉を作らず、入力もしない。
認証情報の id は一覧 (中身を含まない) から取ってワークフローに設定する。
3. chktime 側
設定
n8n.json(git 管理外、.gitignoreに追加。場所は--n8n-configで変更可):{"url": "https://n8n.yfox.jp", "token": "...", "verifySsl": false, "gapMinutes": 30, "minMinutes": 5}GET /api/n8n/configはtokenの中身を返さず、hasTokenだけ返す。POST /api/n8n/configで保存。 Redmine 設定 (RedmineHolder) と同じ作り。verifySslの既定は false (LAN からは n8n.yfox.jp の証明書名が一致しないため)。
サーバーの API (chktime 内)
| ルート | 内容 |
|---|---|
GET/POST /api/n8n/config |
設定の取得 (合言葉は返さない) / 保存 |
GET /api/n8n/blocks?from=&to=&project= |
期間の区間を計算し、n8n の wt-import-rows の結果で「送信済み / 削除済み / 重なり」を付けて返す。n8n に届かなくても区間は返し、n8nError を付ける |
POST /api/n8n/send |
{items:[{source, task, memo}]} を受け、時刻はサーバーが source から区間を計算し直して n8n の wt-import に送る。結果をそのまま返す |
GET /api/redmine/issues?project_id=&q= |
既存の Redmine 連携で未完了チケットを検索 (チケット選択用) |
重なりの判定: 区間 [開始, 終了) と、work_log の行 (進行中は終了 = 今) または一覧の他の区間が
1 秒でも重なれば「重なりあり」。同じ source の行 (送信済みの自分) は除く。
画面
- 右ペインのタブに「n8n へ送る」を足す。左で選んでいるプロジェクト (または すべて) が対象。
- 期間: 初期値は直近 7 日。日付入力で指定もできる。
- 一覧の 1 行 = 1 区間。列: 送る (チェック) / 日付 / 開始〜終了 / 時間 / プロジェクト / タスク名 (編集可) / チケット (選ぶ) / 状態。
- タスク名の初期値は
[作業フォルダ名] セッションのタイトル。チケットを選ぶと[Redmine のプロジェクト名] #番号 題名にする (rich-start と同じ形。工数同期とガントの実績に載る)。 - タスク名・チケットは、同じセッションの他の区間にも入れる (切り替え可)。セッションごとに ブラウザの localStorage に覚える (読み書きは try/catch で囲み、使えなくても動く)。
- 状態の印: 送信済み / 削除済み / 重なりあり (カーソルで相手を表示) / 進行中。
- 送るチェックの初期値: 問題の無い区間だけ on。送信済み・削除済み・重なりあり・進行中は off (送信済みと進行中はチェックできない)。
- 「選んだ区間を送る」で送信し、追加・スキップ・エラーの件数と内訳を表示して一覧を読み込み直す。
- 設定 (URL・合言葉・証明書の検証・30 分・5 分) は Redmine 設定と同じ形のダイアログ。
4. エラーの扱い
- n8n に届かない / 403 / 5xx: 一覧は出し、上部に原因を表示して送信ボタンを無効にする。送信時の失敗は n8n の応答をそのまま表示。
- 一部の行だけ失敗: 行ごとの結果を表示。
sourceで二重登録を防ぐので、再送で失敗分だけ入る。 - 同時に 2 回送った場合: n8n 側の「確認 → 追加」は一括の処理ではないので、二重に入る可能性がある。 使用者は 1 人なので許容し、README に書く。
- 合言葉はログにも画面にも出さない。
n8n.jsonは git に入れない。
5. テスト
chktime
- 区間の計算を標準ライブラリの
unittestで確かめる (tests/test_blocks.py)。 入力は手で組んだターンの列、またはスクラッチパッドに作ったテスト用 jsonl (実ログは書き換えない)。- 空き 29 分はつながり、30 分で切れる
- 5 分未満は除外、5 分ちょうどは残る
- 進行中の区間
- サブエージェントの行を起点にしない
- 日をまたぐ区間の
dateは開始日 sourceが同じ入力で毎回同じ- 重なりの判定 (接しているだけは重ならない、1 秒重なれば重なる、自分の
sourceは除く)
- サーバーは検証用に
python chktime.py --port 8799 --no-browserで起動する (常用の 8787 には触れない)。
n8n
- 2000-01-01 付けのダミー (
source: chktime:test:...) で: 追加される / 再送でスキップ / 合言葉違いで 403 / 不正値でエラー /wt-import-rowsに出る / 削除するとdeletedSourcesに出る。 - 終わったらダミーを
work_logとwork_log_deletedから直接削除する (削除 Webhook は使わない)。
全体
- 8799 の chktime から、利用者が選んだ実際の区間を 1 件送り、ダッシュボードと日報に出ることを確かめる。
6. 文書
CLAUDE.md: 「外部通信は Redmine 連携だけ」を「Redmine 連携と n8n への送信だけ」に。API の表に n8n の系統を足す。README.md: 「n8n へ送る」の使い方、設定、同時送信の注意。.gitignore:n8n.json。- 保管庫: chktime と n8n のノートに追記。
対象外
- 自動で定期的に送ること (送るのは利用者が画面で選んだときだけ)。
- 送信後に chktime から
work_logの行を編集・削除すること (編集・削除は既存の work-tracker の画面で行う)。 - 他の Webhook への認証の追加 (今回の 2 本だけ。広げるのは別の作業)。