初回コミット: チーム運用規約と PROJECT.md
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Wg9sBpqgZnfbcduK1hKttu
This commit is contained in:
@@ -0,0 +1,250 @@
|
|||||||
|
# チーム運用規約(UnifyDrive2NAS)
|
||||||
|
|
||||||
|
> [!info] このファイルの位置づけ
|
||||||
|
> プログラム開発を行うための規約集。
|
||||||
|
> **自分の役割は agmsg に登録した名前で決まる。** 未参加なら人間の指示に従って参加し、以後は自分の役割の節に従うこと。
|
||||||
|
|
||||||
|
## チーム構成
|
||||||
|
|
||||||
|
| エージェント | 種別 | 役割 |
|
||||||
|
| --------------- | ----------- | --------------------------------------------- |
|
||||||
|
| **`Leader`** | claude-code | 人間の窓口。計画・タスク分解・依頼・検収 |
|
||||||
|
| **`Developer`** | codex | 実作業。実装・テスト作成・修正 |
|
||||||
|
| **`QA`** | codex | テストの品質保証。テスト内容の検証・必要に応じたテストの修正・テスト実施・テスト結果の整理 |
|
||||||
|
|
||||||
|
参加は人間の指示を受けてから行う。名前は上の表のとおりにする(`whoami.sh` が出す候補名は他チームのものを含むので使わない)。
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 参加(チームが無ければ作成される)
|
||||||
|
bash ~/.agents/skills/agmsg/scripts/join.sh UnifyDrive2NAS Leader claude-code "$(pwd)"
|
||||||
|
bash ~/.agents/skills/agmsg/scripts/join.sh UnifyDrive2NAS Developer codex "$(pwd)"
|
||||||
|
bash ~/.agents/skills/agmsg/scripts/join.sh UnifyDrive2NAS QA codex "$(pwd)"
|
||||||
|
|
||||||
|
# 確認
|
||||||
|
bash ~/.agents/skills/agmsg/scripts/whoami.sh "$(pwd)" claude-code # → Leader
|
||||||
|
bash ~/.agents/skills/agmsg/scripts/whoami.sh "$(pwd)" codex # → multiple=true agents=Developer,QA
|
||||||
|
bash ~/.agents/skills/agmsg/scripts/team.sh UnifyDrive2NAS # メンバー確認
|
||||||
|
```
|
||||||
|
|
||||||
|
> [!note] 参加直後は人間が Leader に一声かける
|
||||||
|
> `monitor` モードは、そのセッションで最初のターンを踏むまで 1 通目の受信に反応しない(agmsg の仕様)。
|
||||||
|
|
||||||
|
> [!important] codex は同じディレクトリに 2 つ(`Developer` と `QA`)いる
|
||||||
|
> `whoami.sh` は codex について `multiple=true` を返し、どちらの役割かを自動では決められない。各 Codex セッションの起動時に、**`$agmsg actas Developer` または `$agmsg actas QA` で役割を指定する**([[#Codex を herdr のペインで起動する]] の手順に含まれる)。`actas` はそのセッションを役割の席として記録し、`monitor` モードでは各役割宛のメッセージがその役割の Codex セッションだけに届く(agmsg の `SKILL.md` の `actas` 節)。
|
||||||
|
|
||||||
|
## 共通ルール
|
||||||
|
|
||||||
|
- 役割間の連絡は**すべて agmsg** で行う。人間を伝書鳩にしない
|
||||||
|
- **セッション開始時は自分宛の未読と `history` を確認**し、途中からでも文脈を引き継ぐ
|
||||||
|
- 作業を始める前に `PROJECT.md`([[PROJECT|UnifyDrive2NAS の現状]])を読み、成果物の現状を把握する
|
||||||
|
- 判断に迷ったら勝手に決めず、agmsg で相手に聞く
|
||||||
|
- **`.md` は README も含めすべて Obsidian Flavored Markdown(wikilink・embed・callout・properties)で書く。** 書く前に `obsidian-markdown` スキルを読み込む(全役割に同じ Obsidian スキル一式が入っている)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 未読・履歴(<自分の名前> は Leader / Developer / QA)
|
||||||
|
bash ~/.agents/skills/agmsg/scripts/inbox.sh UnifyDrive2NAS <自分の名前>
|
||||||
|
bash ~/.agents/skills/agmsg/scripts/history.sh UnifyDrive2NAS <自分の名前> 20
|
||||||
|
|
||||||
|
# 送信
|
||||||
|
bash ~/.agents/skills/agmsg/scripts/send.sh UnifyDrive2NAS <自分の名前> <相手の名前> "<本文>"
|
||||||
|
```
|
||||||
|
|
||||||
|
## 開発規約
|
||||||
|
|
||||||
|
### テスト
|
||||||
|
|
||||||
|
- **機能追加・修正には必ずテストを作成する。** テストの無い変更は検収に出さない
|
||||||
|
- バグ修正では、先にそのバグを再現して失敗するテストを書き、修正後に通ることを確かめる
|
||||||
|
|
||||||
|
### 報告は実行確認済みのものだけ
|
||||||
|
|
||||||
|
- **実行して確認していない成果物を「完了」として報告しない。** 構文チェックは実行の代わりにならない
|
||||||
|
- 報告には、実際に実行したテストコマンドとその結果(成功・失敗の件数)をそのまま載せる
|
||||||
|
- 実行できない事情があるなら、未検証であることとその理由を明記する
|
||||||
|
|
||||||
|
### 作業の流れ
|
||||||
|
|
||||||
|
1. Leader が Developer に実装を依頼する
|
||||||
|
2. Developer が実装とテストを作り、実行して Leader に報告する
|
||||||
|
3. Leader が QA にテストの検証を依頼する([[#QA の役割]])
|
||||||
|
4. QA がテストを検証・修正・実施し、結果を整理して Leader に報告する
|
||||||
|
5. Leader が検収し([[#検収]])、合格なら Developer にコミットを依頼する
|
||||||
|
|
||||||
|
- Developer と QA は**同じ作業ツリーを使う**。同時に編集しないよう、QA への依頼は Developer の報告の後に出し、その間 Developer は待機する
|
||||||
|
- QA が実装の不具合を見つけたら、Leader が Developer に差し戻す。差し戻しの修正後は、もう一度 QA に検証を依頼する
|
||||||
|
|
||||||
|
### git
|
||||||
|
|
||||||
|
- このディレクトリは git で管理する。変更はすべて git 上の差分として確認できる状態にする
|
||||||
|
- 新しい作業ブランチは、必ず `git switch main` と `git pull --ff-only` で最新にした main から切る。未マージの PR のブランチから重ねて切らない
|
||||||
|
- 未マージの PR と同じファイルを変更する場合も main から切る。衝突はマージ時に解消し、判断に迷ったら Leader に聞く
|
||||||
|
- **コミットは検収を通ってから**行う。検収前の変更は未コミットのまま報告する
|
||||||
|
|
||||||
|
### 検収
|
||||||
|
|
||||||
|
Leader は Developer と QA の報告をそのまま信じず、**実物で確かめる**。
|
||||||
|
|
||||||
|
1. `git status` / `git diff` で、報告された変更内容(Developer と QA の両方)と実際の差分が一致するかを確かめる
|
||||||
|
2. 変更に対応するテストが追加・更新されているか、QA の検証結果に未解決の指摘が残っていないかを確かめる
|
||||||
|
3. `PROJECT.md` の内容に関わる変更なら、同じブランチ(同じ PR)にその更新が含まれているかを確かめる
|
||||||
|
4. **テストを自分で実行し、報告どおりに通ることを確かめる**
|
||||||
|
5. 問題があれば具体的に指摘して差し戻す。問題が無ければ Developer にコミットを依頼する
|
||||||
|
|
||||||
|
## Leader の役割
|
||||||
|
|
||||||
|
- 人間の依頼を**タスクに分解し、agmsg で Developer に依頼する**
|
||||||
|
- Developer の報告を受けたら、**QA にテストの検証を依頼する**([[#作業の流れ]])
|
||||||
|
- PR を作る前の検収で、変更に合わせた `PROJECT.md`([[PROJECT|UnifyDrive2NAS の現状]])の更新が同じブランチ(同じ PR)に含まれているかを確かめる
|
||||||
|
- 成果物を**レビュー・検収**し、修正が必要なら具体的に指摘して差し戻す(手順は [[#検収]])
|
||||||
|
- **実装はしない。** コード・テストの作成と修正は、規模にかかわらずすべて Developer または QA に依頼する。Leader が行うのは調査・レビュー・検収のための読み取りとテスト実行まで
|
||||||
|
|
||||||
|
### 起こす係は Leader が持つ
|
||||||
|
|
||||||
|
Codex も Claude Code も配信モードは `monitor`。Developer と QA の bridge(`codex-bridge.js`)は受信箱を監視し、**Codex が idle なら新しいターンを始めて新着を渡す**。bridge が動くのは、Codex を agmsg 経由で起動したときだけ:
|
||||||
|
|
||||||
|
- `~/.agents/skills/agmsg/scripts/drivers/types/codex/codex-monitor.sh` で起動する(または `delivery.sh set monitor codex` が表示する `codex()` シェル関数をシェルの設定に入れて `codex` で起動する)
|
||||||
|
- bridge は**新しいセッションの最初のターンで**起動する。起動しただけでは動かないので、起動後に 1 通話しかける
|
||||||
|
|
||||||
|
Developer と QA はどちらも `codex-monitor.sh` で起動し、bridge から新着を受け取る。送信後に反応しないときだけ Leader が herdr で起こす。
|
||||||
|
|
||||||
|
bridge は TUI と同じ app-server スレッドのコマンド実行・ファイル変更・権限昇格・MCP の確認要求をすべて拒否する(`codex-bridge.js` の 879〜885 行目、#299 対策)。TUI から入力したターンでも同じ。このため、ユーザー共通の `~/.codex/rules/git.rules` に Codex の rules を置く。`allow` に一致するコマンドはサンドボックス外で確認なしに実行され、bridge の拒否を受けない。[Codex rules の公式資料](https://learn.chatgpt.com/docs/agent-configuration/rules)(2026-10-03 参照)。
|
||||||
|
|
||||||
|
この rules は Git の参照系・ローカル操作・`fetch`・`pull --ff-only` と `ps` を許可し、`git push` を禁止する(push は Leader が行う)。`git -C <path> ...` は先頭が違うため一致しない。変数・リダイレクト・置換を含むシェルコマンドは全体で判定される。rules は Codex の起動時に読み込まれるので、変更後は Codex を起動し直す。
|
||||||
|
|
||||||
|
rules に無く昇格が必要な操作は拒否される。テストやビルドのツールがキャッシュを書き込む場合は、サンドボックス内で書き込める場所を指定する(例: Go なら `env GOCACHE=/private/tmp/UnifyDrive2NAS-go-cache GOPROXY=off go test -count=1 ./...`)。拒否されたら Leader に報告する。
|
||||||
|
|
||||||
|
- **agmsg で送っても宛先(Developer または QA)が反応しないときは herdr で起こす**
|
||||||
|
- 相手が `working`(作業中)や `blocked`(入力待ち)のときは**何もせず待つ**
|
||||||
|
- `herdr agent prompt` は相手が `blocked` のとき `agent_blocked` を返して**送信前に拒否する**ので、承認画面に文字を流し込む事故は起きない
|
||||||
|
- **ペイン名を agmsg のエージェント名に揃える(必須)。** Developer と QA はどちらも同じ `cwd` の codex なので、`cwd` + `agent` では見分けられない。`herdr pane rename <pane> Developer` / `herdr pane rename <pane> QA` で名前を付け、ペイン名(`herdr pane list` の `label`)で引く
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 宛先のペインを探す(pane_id は再起動で変わるので毎回引く。<名前> は Developer または QA)
|
||||||
|
herdr pane list | python3 -c 'import json,sys; [print(p["pane_id"], p.get("agent_status")) for p in json.load(sys.stdin)["result"]["panes"] if p.get("agent")=="codex" and p.get("label")=="<名前>" and p["cwd"]=="/Users/kazu634/Documents/Obsidian/Claude/ITProjects/homelab/Projects/scripts/UnifyDrive2NAS"]'
|
||||||
|
|
||||||
|
# idle なら起こす(<pane> は上で得た pane_id)
|
||||||
|
herdr agent prompt <pane> "agmsg の受信箱を確認して" --wait --until working --timeout 30000
|
||||||
|
```
|
||||||
|
|
||||||
|
### Codex を herdr のペインで起動する
|
||||||
|
|
||||||
|
Developer・QA の Codex は、**herdr で新しいペインを作り、agmsg の起動スクリプト `codex-monitor.sh` で起動する**。これで bridge が付き、idle の間も新着を受け取れる。起動は人間が行っても Leader が行ってもよい(どちらも **Claude Code のペインの中から**実行する。`--current` はコマンドを実行したペインを指す)。
|
||||||
|
|
||||||
|
ペインの配置は、上段に Claude Code(Leader)、下段の左に Developer、右に QA とする。
|
||||||
|
|
||||||
|
```
|
||||||
|
┌──────────────────────────┐
|
||||||
|
│ Claude Code(Leader) │
|
||||||
|
├─────────────┬────────────┤
|
||||||
|
│ Developer │ QA │
|
||||||
|
└─────────────┴────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
D=/Users/kazu634/Documents/Obsidian/Claude/ITProjects/homelab/Projects/scripts/UnifyDrive2NAS
|
||||||
|
|
||||||
|
# split の出力から新しいペインの pane_id を取り出す
|
||||||
|
pane_id() { python3 -c 'import json,sys; print(json.load(sys.stdin)["result"]["pane"]["pane_id"])'; }
|
||||||
|
|
||||||
|
# ペイン $2 で Codex を起動し、役割 $1 を指定する
|
||||||
|
launch() {
|
||||||
|
local NAME=$1 P=$2
|
||||||
|
# ペイン名を役割名にする(Developer と QA を見分けるのに必須)
|
||||||
|
herdr pane rename "$P" "$NAME" >/dev/null
|
||||||
|
# agmsg の起動スクリプトで Codex を起動する
|
||||||
|
herdr pane run "$P" "cd $D && bash ~/.agents/skills/agmsg/scripts/drivers/types/codex/codex-monitor.sh --codex-command codex" >/dev/null
|
||||||
|
# herdr が Codex を検出し、idle になるまで待つ(最大 60 秒)
|
||||||
|
for i in $(seq 1 60); do
|
||||||
|
herdr agent wait "$P" --until idle --timeout 30000 >/dev/null 2>&1 && break
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
herdr agent get "$P" # agent_status が idle であることを確かめる
|
||||||
|
# 役割を指定する。これが最初のターンになり、bridge が起動する
|
||||||
|
herdr agent prompt "$P" "\$agmsg actas $NAME" --wait --timeout 180000
|
||||||
|
}
|
||||||
|
|
||||||
|
# 1. Claude Code のペインを上下に分け、下段を Developer にする
|
||||||
|
DEV=$(herdr pane split --current --direction down --cwd "$D" --no-focus | pane_id)
|
||||||
|
# 2. 下段を左右に分け、右を QA にする
|
||||||
|
QA=$(herdr pane split "$DEV" --direction right --cwd "$D" --no-focus | pane_id)
|
||||||
|
# 3. それぞれで Codex を起動する
|
||||||
|
launch Developer "$DEV"
|
||||||
|
launch QA "$QA"
|
||||||
|
```
|
||||||
|
|
||||||
|
- 片方だけ起動し直すときは、上の `pane_id` と `launch` を定義し、残っているほうのペインを分割して `launch` を 1 回呼ぶ。例: QA だけなら `P=$(herdr pane split <Developer の pane_id> --direction right --cwd "$D" --no-focus | pane_id); launch QA "$P"`
|
||||||
|
- `herdr pane move` は同じタブの中では配置を変えない(`changed:false`、`reason:"same_tab"`)。配置を直すときは、いったん `--new-tab` で別のタブに出してから `--tab <元のタブ> --target-pane <隣のペイン> --split right|down` で戻す
|
||||||
|
- `launch` の中の待ち合わせ(繰り返し)は省かない。起動直後は、Codex の画面が出た後でも herdr がまだ Codex を検出しておらず、`herdr agent wait` が即座に `agent_not_found` で失敗する
|
||||||
|
- Developer と QA を起動できたら、Leader から agmsg でそれぞれ疎通確認を送り、**herdr で起こさずに**返信が来ることを確かめる(来なければ bridge が付いていない)
|
||||||
|
- `codex-monitor.sh` は bridge を用意できないと、素の Codex に切り替えて起動する(画面に `agmsg: Codex monitor bridge unavailable` と出る)。この場合は idle の間に新着を受け取れない
|
||||||
|
- 同じ役割の古いセッションが残っている場合は、先にそのセッションを終了してから起動する。生きているセッションが役割を握っていると `actas` が `held` で失敗する
|
||||||
|
|
||||||
|
Codex やペインを終了しても、app-server が生きている間は**古い役割の bridge が残る**ことがある。さらに親 launcher は、一度 bridge を起動した役割を覚えており、役割の子 launcher と node だけを止めても再起動しないことがある。`history` で、宛先が動く前にメッセージが既読(`○`)になっていたら古い bridge を疑う。
|
||||||
|
|
||||||
|
起動し直すときは、まず Developer と QA の両方を `/quit` で終了する。次に、Leader のシェルで、このプロジェクトの app-server・親 launcher・両役割の子 launcher と node を以下で一覧表示する。pidfile 名はプロジェクトの絶対パスの SHA-1 から求める。`ps` が表示するタブ文字の `\011` 表記を照合する。
|
||||||
|
|
||||||
|
```bash
|
||||||
|
set -o pipefail
|
||||||
|
D=/Users/kazu634/Documents/Obsidian/Claude/ITProjects/homelab/Projects/scripts/UnifyDrive2NAS
|
||||||
|
PROJECT_HASH=$(printf '%s' "$D" | shasum | awk '{print $1}')
|
||||||
|
APP_PID=$(cat "$HOME/.agents/skills/agmsg/run/codex-app-server.$PROJECT_HASH.pid")
|
||||||
|
ps -axo pid=,command= | python3 -c '
|
||||||
|
import sys
|
||||||
|
project, app_pid = sys.argv[1:]
|
||||||
|
launcher = "/codex-bridge-launcher.sh codex " + project + " "
|
||||||
|
pairs = {role: r"UnifyDrive2NAS\011" + role for role in ("Developer", "QA")}
|
||||||
|
for line in sys.stdin:
|
||||||
|
pid, _, command = line.strip().partition(" ")
|
||||||
|
if pid == app_pid and command.startswith("codex app-server "):
|
||||||
|
print(f"app-server\t{pid}\t{command}")
|
||||||
|
elif launcher in command:
|
||||||
|
args = command.split(launcher, 1)[1].split()
|
||||||
|
if len(args) == 2:
|
||||||
|
print(f"parent launcher\t{pid}\t{command}")
|
||||||
|
elif len(args) == 3:
|
||||||
|
for role, pair in pairs.items():
|
||||||
|
if args[2] == pair:
|
||||||
|
print(f"{role} launcher\t{pid}\t{command}")
|
||||||
|
elif "/codex-bridge.js " in command and " --project " + project + " " in command:
|
||||||
|
for role, pair in pairs.items():
|
||||||
|
if " --pair " + pair + " " in command:
|
||||||
|
print(f"{role} node\t{pid}\t{command}")
|
||||||
|
' "$D" "$APP_PID"
|
||||||
|
```
|
||||||
|
|
||||||
|
**表示された PID とプロジェクト・役割を確かめてから**、表示されたこのプロジェクトの app-server・親 launcher・両役割の子 launcher と node を `kill <確認した PID ...>` で止める。他のプロジェクトの bridge は止めない。停止後、[[#Codex を herdr のペインで起動する]] の `launch` で両役割を起動し、疎通を確認する。
|
||||||
|
|
||||||
|
### 止まったときの見分け方
|
||||||
|
|
||||||
|
| 状態 | 意味 | 対処 |
|
||||||
|
|---|---|---|
|
||||||
|
| `idle` なのに仕事が残っている | bridge が動いていない(素の `codex` で起動した等)か、起こし損ね。メッセージは保管庫に残っている | 上記コマンドで起こす。続くようなら [[#Codex を herdr のペインで起動する]] の手順で起動し直す |
|
||||||
|
| 宛先が動く前にメッセージが既読(`history` で `○`) | 古い役割の bridge が受け取った可能性がある | 上の起動し直す手順でこのプロジェクトの bridge 関連プロセスを止め、両役割を起動してから送り直す |
|
||||||
|
| `blocked` | 故障ではなく、承認・権限確認などの**入力待ち**(正常) | 人間の承認待ちなら人間に取り次ぐ。声はかけない |
|
||||||
|
| ペインが落ちている | プロセス断 | [[#Codex を herdr のペインで起動する]] の手順で起動し直し、未読・`history` の確認と作業再開を指示する |
|
||||||
|
|
||||||
|
エージェントの内部状態は復元できないので、**実際の作業状態はファイルの実物で確かめさせる**こと(差分・テスト結果・実行ログ)。
|
||||||
|
|
||||||
|
## Developer の役割
|
||||||
|
|
||||||
|
- Leader からの依頼に従って作業する
|
||||||
|
- **不明点は勝手に判断せず、agmsg で Leader に質問する**。推測で突っ走らない
|
||||||
|
- 実装には必ずテストを付け、**実行して確認してから**報告する([[#開発規約]])
|
||||||
|
- 変更が `PROJECT.md` の内容(今の状態・構成・外部とのつながり・壊してはいけないこと・確かめ方・経緯・課題)に関わるときは、同じブランチで `PROJECT.md` も更新する
|
||||||
|
- `PROJECT.md` には、マージ前には分からない PR 番号やマージコミットのハッシュを書かない。経緯には変更内容とコミット(またはブランチ名)を書き、「この PR は未マージ」のような古くなる自己言及を避ける
|
||||||
|
- 完了したら**変更内容の要約**(対象パス・変更概要・追加したテスト・実行したテストコマンドと結果)を Leader に報告し、次の指示を待つ
|
||||||
|
- 検収を通ったら、Leader の依頼を受けてコミットする
|
||||||
|
|
||||||
|
## QA の役割
|
||||||
|
|
||||||
|
- Leader からの依頼に従って、Developer が作ったテストを検証する
|
||||||
|
- **テスト内容の検証**: 依頼された要件・修正内容に対してテストが足りているかを確かめる(正常系・異常系・境界値、バグ修正なら修正前に失敗するテストがあるか)
|
||||||
|
- **必要に応じた修正**: テストの不足・誤りは QA が直す。**修正してよいのはテストコードとテスト用データだけ**で、実装コードの不具合を見つけたら直さずに Leader に報告する
|
||||||
|
- **テスト実施**: テストを実際に実行する。直したテストは、修正前の実装に対して意図どおり失敗することも、可能な範囲で確かめる
|
||||||
|
- **テスト結果の整理**: 次の内容を Leader に報告し、次の指示を待つ
|
||||||
|
- 実行したテストコマンドとその結果(成功・失敗の件数)
|
||||||
|
- 検証の観点と、見つけた不足・誤り
|
||||||
|
- QA が修正・追加したテスト(対象パスと変更概要)
|
||||||
|
- 未解決の指摘(実装の不具合の疑いなど)
|
||||||
|
- **不明点は勝手に判断せず、agmsg で Leader に質問する**
|
||||||
|
- コミットはしない。QA の変更も検収を通った後に Developer がまとめてコミットする
|
||||||
+246
@@ -0,0 +1,246 @@
|
|||||||
|
---
|
||||||
|
title: UnifyDrive2NAS の現状
|
||||||
|
tags:
|
||||||
|
- homelab
|
||||||
|
- UnifyDrive2NAS
|
||||||
|
status: 方式検討中
|
||||||
|
---
|
||||||
|
|
||||||
|
# UnifyDrive2NAS の現状
|
||||||
|
|
||||||
|
> [!info] このファイルの位置づけ
|
||||||
|
> UnifyDrive2NAS プロジェクトの現状(決定事項・要件・構成・課題)を積み上げていく。運用規約は [[AGENTS]]、ホームラボ基盤は [[homelab/AGENTS|ホームラボ共通基盤]] を参照。
|
||||||
|
|
||||||
|
## 今の状態
|
||||||
|
|
||||||
|
- **方式検討中。** コード・テストはまだ無い
|
||||||
|
- リポジトリ: 未作成
|
||||||
|
|
||||||
|
## 目的
|
||||||
|
|
||||||
|
ホームラボ内の Unify Drive に格納されている画像ファイルを、Synology NAS(`ds923p`)へコピーする。そのための方式を検討して実装する。
|
||||||
|
|
||||||
|
## 決定事項
|
||||||
|
|
||||||
|
| 項目 | 決定 |
|
||||||
|
|---|---|
|
||||||
|
| コピー元 | Unify Drive(`UT2-9EV9`)のストレージ `M.211` 配下(詳細は [[#確認済みの事実]]) |
|
||||||
|
| コピー先 | NAS `ds923p` の共有フォルダ `homes` 配下 `kazu634/Photos/PhotoLibrary` |
|
||||||
|
| コピー先のフォルダ構成 | `YYYY/YYYYMMDD/` のフォルダを作り、その中に画像を置く(例: `PhotoLibrary/2026/20261010/`) |
|
||||||
|
| 実行方法 | **定期実行**。その日に Unify Drive へ格納された分をコピーする(ユーザー判断、2026-10-10) |
|
||||||
|
| 対象ファイル種別 | **RAW(arw・dng)は含めない。動画(mov・mp4)とスクリーンショット(png)は含める**(ユーザー判断、2026-10-10) |
|
||||||
|
| コピー対象のフォルダ | `nvme11-admin`(= `M.211`)配下の `PlugBackup`・`iPhone18,1`・`iPad14,9`・`frankel` の**すべて**。**`.samba_recycle` は除く**(ユーザー判断、2026-10-10) |
|
||||||
|
| 新しいファイルの判定 | **コピー済みの一覧を持ち、まだコピーしていないファイルをコピーする**(案 A。ユーザー判断、2026-10-10)。WebDAV の日時からは格納日が分からないため([[#WebDAV で取れる日時]]) |
|
||||||
|
| 初回の扱い | **本番開始の前日に初期化だけを行う**。既存のファイルをすべて「コピー済み」として一覧に記録し、コピーはしない。翌日からの定期実行で、一覧に無い新しいファイルだけをコピーする。初期化した日のうち、初期化より後に格納されたファイルは翌日の実行でコピーされる(ユーザー判断、2026-10-10) |
|
||||||
|
| 実行場所 | **Dagu(`192.168.10.156`)**から `ssh.run` で NAS 上の処理を呼ぶ(ユーザー判断、2026-10-10)。Dagu の方針([[ITProjects/homelab/Projects/Dagu/AGENTS|Dagu]]: Step Type は `shell` / `ssh.run` のみ、スクリプト実体はリモートホスト側の固定パス)に従う |
|
||||||
|
| Unify Drive の読み方 | **Unify Drive を NAS にマウントする**(ユーザー判断、2026-10-10)。プロトコルは [[#未決事項]] |
|
||||||
|
| コピー済みの一覧 | **SQLite** に持つ。キーは**コピー元のパス・サイズ・更新日時**(ユーザー判断、2026-10-12)。マウント経由では WebDAV の ETag が取れないため |
|
||||||
|
| EXIF・動画メタデータの読み取り | **NAS 上の Python 3.8(標準ライブラリのみ)で読む**。純 Python のライブラリ(`exifread` 等)をディレクトリごと配置し、動画(mov・mp4)の作成日時は QuickTime の `mvhd` を自前で読む(ユーザー判断、2026-10-12)。docker(root 相当)は使わない |
|
||||||
|
| 同名で中身が違うファイル | **両方残す**。中身が同じなら何もしない。違えば連番を付ける(例: `DSC00001_1.JPG`)(ユーザー判断、2026-10-10) |
|
||||||
|
| コピー元のファイル | **残す**(コピーのみで、消さない)(ユーザー判断、2026-10-10) |
|
||||||
|
| `YYYYMMDD` の日付 | **EXIF の撮影日時 → 動画のメタデータ(QuickTime の作成日時等)→ ファイルの日時**の順に、取れたものを使う(ユーザー判断、2026-10-10) |
|
||||||
|
| 動画の日時の優先順 | **Apple の作成日時(`moov/meta`、タイムゾーン付き)→ `moov/mvhd` の作成日時(UTC を JST に変換)**(ユーザー判断、2026-10-12) |
|
||||||
|
| 隠しファイル | **先頭が `.` のファイル・フォルダはコピーしない**(`.<元の名前>.z<16 進 32 桁>` の書きかけと推定されるファイルを含む)(ユーザー判断、2026-10-12) |
|
||||||
|
|
||||||
|
## 要件
|
||||||
|
|
||||||
|
### 機能要件
|
||||||
|
|
||||||
|
- Unify Drive 上の画像ファイルを NAS の `PhotoLibrary/YYYY/YYYYMMDD/` へコピーする
|
||||||
|
- コピー先に `YYYY` / `YYYYMMDD` フォルダが無ければ作る
|
||||||
|
|
||||||
|
### 非機能要件
|
||||||
|
|
||||||
|
- 未定
|
||||||
|
|
||||||
|
## 構成(案)
|
||||||
|
|
||||||
|
未定。[[#未決事項]] を決めてから書く。
|
||||||
|
|
||||||
|
## 外部とのつながり
|
||||||
|
|
||||||
|
| 相手 | アドレス | 用途 | 根拠 |
|
||||||
|
|---|---|---|---|
|
||||||
|
| Unify Drive `UT2-9EV9` | `192.168.10.200/24`(固定 IP。Web UI `http://192.168.10.200:5055`) | コピー元 | ユーザーが DHCP(旧 `192.168.10.37`)から固定 IP に変更。[実機] 2026-10-10 ping 応答と `:5055` の HTTP 302 を確認 |
|
||||||
|
| Synology `ds923p` | `192.168.10.201`(DSM `http://192.168.10.201:5000`) | コピー先 | [[homelab/docs/inventory\|インフラ詳細]] |
|
||||||
|
|
||||||
|
> [!note] Unify Drive の IP
|
||||||
|
> 当初は DHCP で `192.168.10.37` だったが、2026-10-10 にユーザーが `192.168.10.200/24` の固定 IP に変更した。[[homelab/docs/inventory|インフラ詳細]] と [[homelab/AGENTS|ホームラボ共通基盤]] の IP 割当に登録済み。
|
||||||
|
|
||||||
|
## 確認済みの事実
|
||||||
|
|
||||||
|
### コピー元: Unify Drive
|
||||||
|
|
||||||
|
[実機] 2026-10-10 踏み台から WebDAV(`https://192.168.10.200:5006/`、`~/.config/unifydrive.netrc` で認証)で一覧を取得して確認。件数・容量・日付は PROPFIND の `getcontentlength` / `getlastmodified`(ファイルの更新日時)による。
|
||||||
|
|
||||||
|
- デバイス名は `UT2-9EV9`
|
||||||
|
- WebDAV のルート直下は `nvme11-admin/`・`Shared Files/`・`public/`・`sharetome/`。Web UI の `M.211` は **`nvme11-admin/`** にあたる(直下のフォルダ名が一致)。`Shared Files`・`public`・`sharetome` は空
|
||||||
|
- `nvme11-admin/` 直下は `PlugBackup`・`frankel`・`iPhone18,1`・`iPad14,9` と、隠しフォルダ `.samba_recycle`
|
||||||
|
|
||||||
|
| フォルダ | 何のバックアップか(推定を含む) | ファイル数 | 容量 | 拡張子 | 更新日時の範囲 |
|
||||||
|
|---|---|---|---|---|---|
|
||||||
|
| `PlugBackup/` | Unify Drive に差した SD カードのバックアップ | 5,059 | 211.27 GiB | jpg 4,475 / arw 452 / dng 109 ほか | 2025-11-08〜2026-10-10 |
|
||||||
|
| `iPhone18,1/iPhone18,1PhotoBackup/all photos/` | iPhone のアプリからの写真バックアップ | 2,241 | 15.75 GiB | jpg 1,061 / heic 536 / jpeg 436 / mov 120 / png 72 / mp4 2 ほか | 2023-10-26〜2026-10-04 |
|
||||||
|
| `iPad14,9/iPad14,9PhotoBackup/all photos/` | iPad のアプリからの写真バックアップ | 90 | 0.69 GiB | png 82 / jpg 6 / mp4 2 | 2025-09-23〜2026-09-23 |
|
||||||
|
| `frankel/frankelPhotoBackup/Camera/` | 別端末(`frankel`)の写真バックアップ | 4 | 0.01 GiB | jpg 4 | 2025-12-27〜2026-01-16 |
|
||||||
|
| `.samba_recycle/` | SMB のごみ箱(削除済みファイル) | 3,889 | 460.02 GiB | arw 3,839 / dng 45 ほか | 2025-11-08〜2026-05-17 |
|
||||||
|
|
||||||
|
#### `PlugBackup/` の構成
|
||||||
|
|
||||||
|
バックアップ 1 回ごとにフォルダが 1 つできる。フォルダ名の形式は 2 通りある。
|
||||||
|
|
||||||
|
| 形式 | 例 | 画像の場所 |
|
||||||
|
|---|---|---|
|
||||||
|
| `YYYYMMDDHH` + 連番 3 桁(と推定) | `2026051623001` | `<回>/Generic MassStorageClass/<ボリューム名>/DCIM/<100MSDCF 等>/` |
|
||||||
|
| `SDCARD1_YYYYMMDD_HH_連番` | `SDCARD1_20261010_11_001` | `<回>/DCIM/100MSDCF/` |
|
||||||
|
|
||||||
|
- ボリューム名は `SDCARD1`・`AVPRO SD`・`RICOH GR` の 3 種類を確認。カメラは Sony(`100MSDCF`、`DSC*.ARW`、`PRIVATE/M4ROOT`)と RICOH GR IIIx(`101RICOH`、`R*.DNG`)と推定
|
||||||
|
- 画像以外に、カメラの管理ファイル(`AVF_INFO/*.bnp|inp|int`、`PRIVATE/` 配下、`.Spotlight-V100/`)とバックアップごとの `.txt` がある
|
||||||
|
- `2026022819001` と `2026050219001` には Synology のサムネイル用フォルダ `@eaDir/` が混じっている
|
||||||
|
- `2025102520001`・`2025110304001` には画像が無い(フォルダはあるがファイルは 0 件)
|
||||||
|
|
||||||
|
| 回 | 画像のあるフォルダ | 件数 |
|
||||||
|
|---|---|---|
|
||||||
|
| `2025102520001` | なし | 0 |
|
||||||
|
| `2025110304001` | `SDCARD1/DCIM/100MSDCF/`(空) | 0 |
|
||||||
|
| `2025110806001` | `SDCARD1/DCIM/100MSDCF/` | 821 |
|
||||||
|
| `2025112401001` | `SDCARD1/DCIM/100MSDCF/` | 40 |
|
||||||
|
| `2025120719001` | `RICOH GR/DCIM/101RICOH/` | 34 |
|
||||||
|
| `2026011119001` | `RICOH GR/DCIM/101RICOH/` | 45 |
|
||||||
|
| `2026022819001` | `RICOH GR/DCIM/101RICOH/` | 182 |
|
||||||
|
| `2026050219001` | `SDCARD1/DCIM/100MSDCF/` | 56 |
|
||||||
|
| `2026051623001` | `SDCARD1/DCIM/100MSDCF/` | 1,540 |
|
||||||
|
| `2026051723001` | `AVPRO SD/DCIM/100MSDCF/` | 1,482(すべて jpg、51.88 GiB) |
|
||||||
|
| `SDCARD1_20260913_14_001` | `DCIM/100MSDCF/` | 208 |
|
||||||
|
| `SDCARD1_20261010_11_001` | `DCIM/100MSDCF/` | 600(jpg 300 / arw 300、45.83 GiB) |
|
||||||
|
|
||||||
|
#### iPhone / iPad / frankel の構成
|
||||||
|
|
||||||
|
- `all photos/` の下は **`YYYY-MM`(月)フォルダ**に分かれている(iPhone は `2023-10`〜`2026-10`、iPad は `2025-09`〜`2026-09`)
|
||||||
|
- iPhone には `1970-01/` フォルダ(2 件)がある。撮影日時が取れなかったファイルと推定
|
||||||
|
- iPhone には、拡張子が `z` + 32 桁の 16 進数になっているファイルがある(少なくとも 4 件)。正体は未確認
|
||||||
|
- 写真のほかに動画(mov / mp4)とスクリーンショットらしい png を含む
|
||||||
|
|
||||||
|
#### `.samba_recycle/`(SMB のごみ箱)
|
||||||
|
|
||||||
|
- `PlugBackup/` と同じ構成のフォルダの下に、ARW が 3,839 件、合計 460 GiB 残っている。`PlugBackup/` から RAW を消した跡と推定
|
||||||
|
- コピー対象から外すかどうかは [[#未決事項]] で決める
|
||||||
|
|
||||||
|
#### WebDAV で取れる日時
|
||||||
|
|
||||||
|
[実機] 2026-10-10、`PlugBackup/SDCARD1_20261010_11_001/DCIM/100MSDCF/` の 600 件で確認。
|
||||||
|
|
||||||
|
- 取れるのは `creationdate` と `getlastmodified` の 2 つで、**どちらもカメラで記録した日時と同じ値**(例: `DSC06493.JPG` はどちらも 2026-08-05 11:43:30Z)
|
||||||
|
- このバックアップは 2026-10-10 11 時に行われたが、ファイルの日時は 2026-08-05〜2026-10-07。**WebDAV の日時からは「Unify Drive に格納された日」は分からない**
|
||||||
|
|
||||||
|
#### SMB で取れる日時
|
||||||
|
|
||||||
|
[実機] 2026-10-10、`smbclient -m SMB3` の `allinfo` で確認。
|
||||||
|
|
||||||
|
- `PlugBackup/SDCARD1_20261010_11_001/DCIM/100MSDCF/DSC06493.JPG` は `create_time`・`write_time`・`change_time` がすべて 2026-08-05 20:43:30 JST で、カメラの日時と同じ。`access_time` だけが 2026-10-10 11:02:11 JST
|
||||||
|
- **SMB でも「Unify Drive に格納された日」は分からない**(`access_time` は読み出しでも更新されうるので使わない)。新しいファイルの判定は、決定事項どおりコピー済みの一覧で行う
|
||||||
|
|
||||||
|
### コピー元への到達手段
|
||||||
|
|
||||||
|
[実機] 2026-10-10 踏み台 `bastion2404` から確認。
|
||||||
|
|
||||||
|
| ポート | 応答 | 解釈 |
|
||||||
|
|---|---|---|
|
||||||
|
| `22` | 閉じている | SSH の標準ポートではない |
|
||||||
|
| `10000` | SSH(rsa / ecdsa / ed25519 のホスト鍵あり)。認証方式は `publickey,password` | 2026-10-10 にユーザーが Web UI の「Remote assist/SSH」で有効化(ポート `10000`、「LAN access only」・15 分で自動切断)。UI の注意書きでは**管理者アカウントで接続し、root 権限は `sudo`**。また **SSH を有効にするとソフトウェアのサポート対象外になる**(システムを復元すれば戻せる)。踏み台から `kazu634` の鍵認証は `Permission denied` |
|
||||||
|
| `21` | `220 ZFileShare 2.5.4 ready`。anonymous は `530` | FTP。認証が必要 |
|
||||||
|
| `139` / `445` | 匿名では `Shared Files` だけが見える(中身は空)。`~/.config/unifydrive.smbauth` で認証すると共有 `Shared Files`・`nvme`・`sharetome`・`External Device-rescuefs` が見える。SMB1 は無効 | **SMB。共有 `nvme` のルートが `nvme11-admin`(= `M.211`)の中身にあたる**(直下に `PlugBackup`・`iPad14,9`・`iPhone18,1`・`frankel`・`.samba_recycle`)。`External Device-rescuefs` は空(容量 0)。[実機] 2026-10-10 確認 |
|
||||||
|
| `111` / `2049` | 開いている | NFS。エクスポートは `/tmp/zfsv3/share_fake/share`(許可 `*`)の 1 件だけで、名前からダミーと推定 [要確認] |
|
||||||
|
| `5005`(http)/ `5006`(https) | 認証なしは `401`(`Basic realm="Restricted"`)。`~/.config/unifydrive.netrc` で認証すると PROPFIND に `207` | **WebDAV。一覧の取得に成功**([[#コピー元: Unify Drive]]) |
|
||||||
|
| `5055` | `302 → /home/`(openresty) | Web UI(My Files) |
|
||||||
|
| `5056` | openresty(http は 400、https は 404) | 用途不明 |
|
||||||
|
|
||||||
|
### コピー先: Synology NAS
|
||||||
|
|
||||||
|
[実機] 2026-10-10 ユーザー提供のスクリーンショット(DSM File Station)で確認。
|
||||||
|
|
||||||
|
- File Station 上のパスは `homes > kazu634 > Photos > PhotoLibrary`。ファイルシステム上の実パス(`/volume1/homes/kazu634/...` か)は [要確認]
|
||||||
|
- `PhotoLibrary` には 27 項目あり、見えた範囲は以下
|
||||||
|
- 年フォルダ: `2020`〜`2026`
|
||||||
|
- その他のフォルダ: `99999999_ios`・`99999999_misc`・`99999999_tumblr`・`iPad`・`Mobile`
|
||||||
|
- ファイル: `upload2Flickr.sh`
|
||||||
|
- 年フォルダの中が `YYYYMMDD` 形式かどうかは、スクリーンショットからは確認できない [要確認]
|
||||||
|
|
||||||
|
### NAS 側の実行環境
|
||||||
|
|
||||||
|
[実機] 2026-10-12、Dagu の VM から `ssh -i ~/.ssh/id_ed25519_nas kazu634@192.168.10.201` で確認。
|
||||||
|
|
||||||
|
- **Dagu → NAS の SSH は鍵で通る**(ポート `22`、ユーザー `kazu634`、`administrators` グループ所属)。NAS のホスト鍵は Dagu の `known_hosts` に登録済み
|
||||||
|
- DSM 7.4.1(build 90080)
|
||||||
|
- **マウントの実パスは `/volume1/homes/kazu634/UnifyDrive`**(`$HOME` は `/var/services/homes/kazu634`)。種別は `cifs`、`vers=3.02`、`username=admin`、`uid=1026(kazu634)`・`gid=100(users)` に固定、`file_mode=0777`、`noexec`、`nocase`
|
||||||
|
- マウント経由の `stat` も、`Modify`・`Change` はカメラの日時(`DSC06493.JPG` は 2026-08-05 20:43:30 JST)。`Birth` は取れない
|
||||||
|
- 使えるもの: `python3`(3.8.15、標準ライブラリのみで `pip` も無い)・`sqlite3`・`rsync`
|
||||||
|
- 使えないもの: `exiftool`・`ffprobe`・`perl` は無い
|
||||||
|
- **docker の制約:** `docker` は `/usr/local/bin/docker` にある(非対話 SSH の `PATH` には入っていない)。ただし `/var/run/docker.sock` は `root:root` の `660` で、`kazu634` からは使えない。`sudo` はパスワードを要求する
|
||||||
|
- インストール済みのパッケージに `Node.js_v20`・`Node.js_v22`・`Python2`・`ContainerManager` がある
|
||||||
|
|
||||||
|
### 純 Python での日時の取得(実現可能性の確認)
|
||||||
|
|
||||||
|
[実機] 2026-10-12、NAS 上の Python 3.8.15 + `exifread` 3.5.1(純 Python・依存なし・`Requires-Python >=3.7`)と、QuickTime の atom を読む自前のコードで確認。調査用のファイルは NAS の `/tmp/unify-probe` に置き、確認後に削除した。
|
||||||
|
|
||||||
|
**取れた日時の出どころ**(iPhone・iPad・frankel は全件、`PlugBackup` は 3 フォルダから数件ずつ)
|
||||||
|
|
||||||
|
| 形式 | 取れたもの | 件数 |
|
||||||
|
|---|---|---|
|
||||||
|
| JPG(Sony・RICOH GR・iPhone・iPad・Pixel) | EXIF `DateTimeOriginal` | iPhone 940 / iPad 6 / frankel 4 / `PlugBackup` のサンプルすべて |
|
||||||
|
| HEIC | EXIF `DateTimeOriginal` | 535 |
|
||||||
|
| PNG(スクリーンショット) | EXIF `DateTimeOriginal`(`eXIf` チャンク) | iPhone 58 / iPad 82 |
|
||||||
|
| MOV(iPhone) | `moov/meta` の Apple の作成日時(例 `2025-11-08T20:46:34+0900`、タイムゾーン付き) | 119 |
|
||||||
|
| MOV・MP4(それ以外) | `moov/mvhd` の作成日時(1904 年起点の UTC 秒を JST に変換) | iPhone mov 1・mp4 2 / iPad mp4 2 |
|
||||||
|
|
||||||
|
- MOV では、Apple の作成日時と `mvhd` は 1 秒ほどしか違わない(`mvhd` は書き出し終了時刻の可能性)。Apple の作成日時を優先し、無ければ `mvhd` を使うのがよい
|
||||||
|
- DJI の MP4 は、`mvhd` を JST にした時刻がファイル名の時刻(`DJI_20250518131016`)と一致した。ただし、カメラによっては現地時刻を UTC として `mvhd` に書くものがあり、その場合は 9 時間ずれる(日付が変わりうる)[要確認]
|
||||||
|
|
||||||
|
**日時が取れなかったもの**(すべて iPhone。ファイルの中身を見て、`exifread` の読み落としではなく**日時の情報自体が無い**ことを確認した)
|
||||||
|
|
||||||
|
| 種類 | 件数 | 中身 | ファイルの日時(決定事項どおりの代替) |
|
||||||
|
|---|---|---|---|
|
||||||
|
| UUID 名の `.jpeg`(`2024-02`〜`2024-03` 等) | 436 | JFIF のみで EXIF 自体が無い | すべて 2024-03-04 15:56:27。**取り込んだ日時であって撮影日ではない** |
|
||||||
|
| `IMG_*.JPG` | 121 | EXIF はあるが日時のタグが無い(編集・書き出し後と推定) | 個別の日時 |
|
||||||
|
| `IMG_*.PNG` | 14 | 中身は JPEG(拡張子と中身が違う)。EXIF はあるが日時のタグが無い | 個別の日時 |
|
||||||
|
| `IMG_2542.HEIC` | 1 | EXIF が無い(XMP はあるが日時は無い) | 個別の日時 |
|
||||||
|
| `1970-01/IMG_1824.JPG` 等 | 2 | 12 KB ほどで、中身は JPEG を Base64 にしたテキスト(`/9j/4AAQ...`)。画像として壊れている | 2025-03-23 |
|
||||||
|
|
||||||
|
**設計に効く事実**
|
||||||
|
|
||||||
|
- **拡張子と中身が一致しないファイルがある**(`.PNG` の中身が JPEG)。`exifread` は中身で形式を判定するので、拡張子に頼らず読める
|
||||||
|
- **先頭が `.` の隠しファイル `.<元の名前>.z<16 進 32 桁>` が 14 件ある**(例: `.IMG_2584.MOV.zb0bad79...`、`.DSC07166.JPG.z6d2802...`)。iPhone からのバックアップの途中のファイルか一時ファイルと推定。**コピー対象から外さないと、書きかけのファイルを取り込むおそれがある**
|
||||||
|
- iPhone・iPad・frankel の全 2,331 件を読むのに約 78 秒(ほぼ iPhone)。日々の実行では新しいファイルだけを読むので、時間は問題にならない見込み
|
||||||
|
|
||||||
|
### コピー先の現状
|
||||||
|
|
||||||
|
[実機] 2026-10-12 確認。
|
||||||
|
|
||||||
|
- `~/Photos/PhotoLibrary/2026/` には `20260110`〜`20260517` の `YYYYMMDD` フォルダが既にある
|
||||||
|
- 例として `20260517` には 385 項目があり、**JPG と ARW の両方と `@eaDir`** が入っている。これまで RAW も含めて手作業でコピーしていたと推定。今後は RAW を含めない(決定事項)
|
||||||
|
|
||||||
|
## 未決事項
|
||||||
|
|
||||||
|
方式を決める前に決める必要がある項目。
|
||||||
|
|
||||||
|
- [x] **純 Python で日付が取れるかの実機確認**: 2026-10-12 確認。JPG・HEIC・PNG・MOV・MP4 とも取れる([[#純 Python での日時の取得(実現可能性の確認)]])
|
||||||
|
|
||||||
|
- [x] **マウントのプロトコル**: SMB(CIFS)。2026-10-10 にユーザーが DSM の File Station「リモートフォルダのマウント」で `\\192.168.10.200\nvme` を `/home/UnifyDrive`(File Station 上のパス)にマウントした。Unify Drive 側のアカウントは `admin`、「起動時に自動的にマウントする」にチェック。DSM のリモートフォルダのマウントは SMB と NFS のみ対応 [公式] https://www.synology.com/en-global/dsm/7.4/software_spec/file_access (取得日: 2026-10-10)。[実機] 2026-10-10 ユーザー提供の File Station のスクリーンショットで、`home > UnifyDrive` の下に `.samba_recycle`・`frankel`・`iPad14,9`・`iPhone18,1`・`PlugBackup`(12 回分のフォルダ)が見えることを確認。実パスは `/volume1/homes/kazu634/UnifyDrive`([[#NAS 側の実行環境]])
|
||||||
|
- [ ] **マウントの再起動後の扱い**: 「起動時に自動的にマウントする」は有効にした。実際に NAS の再起動後にマウントし直されるかは未確認 [要確認]
|
||||||
|
- [x] **Dagu → NAS の SSH 鍵**: 2026-10-12 に登録・ログインを確認([[#NAS 側の実行環境]])
|
||||||
|
- [x] **Unify Drive の IP 固定とインベントリへの登録**(2026-10-10、`192.168.10.200/24`)
|
||||||
|
|
||||||
|
## 課題
|
||||||
|
|
||||||
|
- なし([[#未決事項]] を参照)
|
||||||
|
|
||||||
|
## 経緯
|
||||||
|
|
||||||
|
- 2026-10-10: プロジェクト開始。`AGENTS.md` を tver から流用し、`PROJECT.md` を作成
|
||||||
|
- 2026-10-10: Unify Drive を DHCP(`192.168.10.37`)から固定 IP `192.168.10.200/24` に変更
|
||||||
|
- 2026-10-10: 踏み台から WebDAV でコピー元のディレクトリ構成を調査
|
||||||
|
- 2026-10-12: Dagu → NAS の SSH 鍵を登録。NAS 上の純 Python で撮影日時が取れることを確認
|
||||||
|
|
||||||
|
## 確かめ方
|
||||||
|
|
||||||
|
未定(実装後に書く)。
|
||||||
Reference in New Issue
Block a user