From 765b8c47988b3c5f96229f0c532481b7f9037bb5 Mon Sep 17 00:00:00 2001 From: yoshikazu Date: Mon, 12 Oct 2026 21:02:49 +0900 Subject: [PATCH] =?UTF-8?q?n8n=20=E3=81=AE=20work=5Flog=20=E3=81=B8?= =?UTF-8?q?=E4=BD=9C=E6=A5=AD=E5=8C=BA=E9=96=93=E3=82=92=E9=80=81=E3=82=8B?= =?UTF-8?q?=E6=A9=9F=E8=83=BD=E3=81=AE=E8=A8=AD=E8=A8=88=E6=9B=B8=E3=82=92?= =?UTF-8?q?=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 --- .../2026-10-12-n8n-worklog-import-design.md | 167 ++++++++++++++++++ 1 file changed, 167 insertions(+) create mode 100644 docs/superpowers/specs/2026-10-12-n8n-worklog-import-design.md diff --git a/docs/superpowers/specs/2026-10-12-n8n-worklog-import-design.md b/docs/superpowers/specs/2026-10-12-n8n-worklog-import-design.md new file mode 100644 index 0000000..ef7efd8 --- /dev/null +++ b/docs/superpowers/specs/2026-10-12-n8n-worklog-import-design.md @@ -0,0 +1,167 @@ +# 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 本だけ。広げるのは別の作業)。