Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,27 @@ jobs:
- name: Lint
run: pnpm -r --if-present lint

# ランナーイメージ同梱の python3 に依存すると、GitHub がイメージを更新した際に
# リポジトリ側の変更なしに ci-green(必須チェック)が壊れうる。版を固定する。
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: "3.12"

# 顔モザイクのテスト(tests/face-mosaic-check.py)が cv2 を使う。
# requirements.txt 全体は文字起こしの重い依存を含み CI では不要なので opencv だけを入れる。
# 版の指定は requirements.txt を唯一の正とし、そこから抜き出して使う(二重管理で食い違わせない)。
# 入れずにテストをスキップさせるのは AGENTS.md が禁じる「偽の緑」にあたるため行わない。
- name: Install Python deps for face tests (opencv only)
run: |
spec=$(grep -E '^opencv-python-headless' video-shorts/requirements.txt)
if [ -z "$spec" ]; then
echo "FAIL: opencv-python-headless が video-shorts/requirements.txt に見つかりません"
exit 1
fi
echo "installing: $spec"
python -m pip install "$spec"

- name: Test
run: pnpm -r test

Expand Down
26 changes: 13 additions & 13 deletions docs/roadmap.html
Original file line number Diff line number Diff line change
Expand Up @@ -118,11 +118,11 @@
{
"meta": {
"title": "kosespark video-shorts ロードマップ",
"active": "M-1-A",
"next": "M-1-A(動画に写っている顔の位置が分かる)から着手する。video-shorts/src/ に顔検出モジュールを追加し、tests/ に固定素材でのユニットテストを置く。実装順は M-1 → M-2 → M-3/M-4 → M-5(deps参照)。M-5-C(実素材での見落とし率)はマスターから実素材の動画提供を受け次第に実施する。P1-10/P1-11/P1-12 等の既存todoは G-MOSAIC 完了後に再開する。",
"active": "M-2-A",
"next": "M-2(特定の人だけ隠し方を変えられる)へ進む。SFace(Apache2.0)のモデルとライセンスを src/models/ へ同梱し、face_mosaic.py の mosaic_frames(block_for=...) に参照顔の照合を接続する。M-5-C(実素材での見落とし率)はマスターから実素材の動画提供を受け次第に実施する。P1-10/P1-11/P1-12 等の既存todoは G-MOSAIC 完了後に再開する。",
"handoff": {
"done": "マスター指示により顔モザイク機能(G-MOSAIC)の調査と原子ツリー作成を実施。着手前の実機検証で技術選定を確定した(顔検出=YuNet/MIT、顔識別=SFace/Apache2.0、いずれもopencv-python同梱APIで動きPyTorch等は不要)。GUIは作らない方針を確定: build-dist.mjsがserver/・webapp-mockup/・ui/を配布除外しstart-here.mdが「すべてチャットとフォルダ操作で完結」と定めているため、対象者の指定は顔画像のファイル書き出し+チャットでの番号選択で行う。ツリーはG-MOSAIC配下にM-1〜M-5の5系統・葉17件として作成した。実装は未着手(AGENTS.md「ツリーが出来るまで実装に入らない」に従う)。",
"trouble": "着手前検証の過程で、実機確認を伴わない机上の技術提案と、検証コード自体の欠陥による誤った計測を複数回出した(ffmpegのpixelizeで位置を動かす案は当該フィルタにx/yが無く不成立、顔の最小サイズ閾値が過大で識別処理が一度も走らないまま「識別は0.01秒」と報告しかけた、出力検証でアスペクト比を無視して縮小し素顔漏れと誤判定した)。詳細と教訓はdocs/failures.md 2026-08-04の2件に追記。"
"done": "G-MOSAIC の原子ツリーを作成し(PR #27 マージ済み)、続けて M-1 の4葉を実装した。src/face_mosaic.py を新設し、YuNetモデルとMITライセンス本文を src/models/ へ同梱、tests/face-mosaic-check.py(16件)と固定素材を追加、requirements.txt と CI に opencv を配線した。技術選定・設計上の必須事項・実測値は G-MOSAIC / M-1-B / M-1-D の detail を参照。",
"trouble": "M-1-D の実装当初、モザイクの粗さを顔サイズ比のみで決めたところテストが落ちた。実測すると閾値は比率だけでも絶対値だけでも決まらず(小さい顔ほど必要な比が大きく、大きい顔では絶対値が10px前後で頭打ち)、両方の max を取る形に是正した。実測表は M-1-D の detail に記録。"
},
"updated": "2026-08-04"
},
Expand Down Expand Up @@ -963,12 +963,12 @@
"kind": "state",
"theme": "動画に写っている顔の位置が分かる",
"meaning": "隠す対象がどこにいるかを機械が把握できる",
"status": "todo",
"status": "done",
"criteria": [
{
"text": "顔が写った固定テスト素材を処理すると、写っている人数と同じ数の顔位置が返る",
"verify": "既知の人数の顔を含む固定テスト素材で顔検出を実行し、検出数が期待値と一致することをテストで確認する",
"evidence": ""
"evidence": "https://github.com/rahiseko-alt/ai-editer/actions/runs/30875193419"
Comment on lines +966 to +971

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Inspect the cited CI run and the roadmap evidence verifier.
set -euo pipefail

# Show the run metadata: status, conclusion, head SHA, workflow.
gh api repos/rahiseko-alt/ai-editer/actions/runs/30875193419 \
  --jq '{status, conclusion, head_sha, head_branch, event, workflow_id, created_at}' || true

# List the jobs and their steps to confirm the face-mosaic test ran.
gh api repos/rahiseko-alt/ai-editer/actions/runs/30875193419/jobs \
  --jq '.jobs[] | {name, conclusion, steps: [.steps[] | {name, conclusion}]}' || true

# Show what the evidence verifier enforces.
fd -t f 'verify-roadmap-evidence.mjs' | xargs -r cat -n

Repository: rahiseko-alt/ai-editer

Length of output: 11402


🏁 Script executed:

#!/bin/bash
set -euo pipefail

sha=9be409128567299b91ef23bbaf901853ca9be658
repo=rahiseko-alt/ai-editer

printf '%s\n' '--- commit files ---'
gh api "repos/$repo/commits/$sha" \
  --jq '{sha: .sha, parents: [.parents[].sha], files: [.files[].filename]}'

printf '%s\n' '--- workflow test commands at the run commit ---'
gh api "repos/$repo/contents/.github/workflows/ci.yml?ref=$sha" \
  --jq '.content' | base64 -d | sed -n '1,180p'

printf '%s\n' '--- face test source at the run commit ---'
gh api "repos/$repo/contents/video-shorts/tests/face-mosaic-check.py?ref=$sha" \
  --jq '.content' | base64 -d | cat -n

printf '%s\n' '--- roadmap M-1 evidence at the run commit ---'
gh api "repos/$repo/contents/docs/roadmap.html?ref=$sha" \
  --jq '.content' | base64 -d |
  rg -n -A8 -B2 '30875193419|M-1|顔が写った固定|顔が移動する固定|顔検出が途切れた|出力動画の顔部分'

Repository: rahiseko-alt/ai-editer

Length of output: 19889


🏁 Script executed:

#!/bin/bash
set -euo pipefail

gh run view 30875193419 --repo rahiseko-alt/ai-editer --log |
  rg -n 'face-mosaic|16|顔|mosaic|Test|Verify roadmap evidence' || true

gh api repos/rahiseko-alt/ai-editer/commits/9be409128567299b91ef23bbaf901853ca9be658 \
  --jq '{sha: .sha, files: [.files[].filename]}'

Repository: rahiseko-alt/ai-editer

Length of output: 26698


🏁 Script executed:

#!/bin/bash
set -euo pipefail

repo=rahiseko-alt/ai-editer
run=30875193419
head=9be409128567299b91ef23bbaf901853ca9be658
merge=ab38f9bc9bd3c49f59c0a92da3e3799febf1d673

printf '%s\n' '--- run and pull-request linkage ---'
gh api "repos/$repo/actions/runs/$run" \
  --jq '{head_sha, head_branch, event, pull_requests, head_repository: .head_repository.full_name, base_ref}'

gh api "repos/$repo/pulls/28" \
  --jq '{number, state, head: .head.sha, base: .base.sha, merge_commit_sha, merged_at}'

printf '%s\n' '--- roadmap states at head and merge commits ---'
for ref in "$head" "$merge"; do
  echo "REF=$ref"
  gh api "repos/$repo/contents/docs/roadmap.html?ref=$ref" --jq '.content' |
    base64 -d |
    rg -n -A6 '"id": "M-1-[ABCD]"|"status":|"evidence":' |
    sed -n '1,100p'
done

printf '%s\n' '--- merge commit metadata and changed files ---'
gh api "repos/$repo/commits/$merge" \
  --jq '{sha: .sha, parents: [.parents[].sha], files: [.files[].filename]}'

Repository: rahiseko-alt/ai-editer

Length of output: 11176


🏁 Script executed:

#!/bin/bash
set -euo pipefail

repo=rahiseko-alt/ai-editer
for ref in 9be409128567299b91ef23bbaf901853ca9be658 ab38f9bc9bd3c49f59c0a92da3e3799febf1d673; do
  echo "REF=$ref"
  gh api "repos/$repo/contents/docs/roadmap.html?ref=$ref" --jq '.content' |
    base64 -d |
    rg -n -A6 '"id": "M-1-[ABCD]"|"status":|"evidence":' |
    sed -n '1,100p'
done

Repository: rahiseko-alt/ai-editer

Length of output: 9602


🏁 Script executed:

#!/bin/bash
set -euo pipefail

repo=rahiseko-alt/ai-editer
head=1fe0a8b4df4e810e4df057c91f50d1cb6c1f544a
run=30875193419

printf '%s\n' '--- current PR head commit ---'
gh api "repos/$repo/commits/$head" \
  --jq '{sha: .sha, parents: [.parents[].sha], files: [.files[].filename]}'

printf '%s\n' '--- M-1 states at the current PR head ---'
gh api "repos/$repo/contents/docs/roadmap.html?ref=$head" --jq '.content' |
  base64 -d |
  python3 -c '
import sys, json, re
s=sys.stdin.read()
m=re.search(r"<script type=\"application/json\" id=\"roadmap-data\">(.*?)</script>", s, re.S)
data=json.loads(m.group(1))
def walk(n):
    if n.get("id") in {"M-1-A","M-1-B","M-1-C","M-1-D"}:
        print(json.dumps({"id": n["id"], "status": n.get("status"),
                          "evidence": [c.get("evidence") for c in n.get("criteria", [])]},
                         ensure_ascii=False))
    for c in n.get("children", []):
        walk(c)
for n in data["nodes"]:
    walk(n)
'

printf '%s\n' '--- cited run versus current PR head ---'
gh api "repos/$repo/actions/runs/$run" \
  --jq '{head_sha, event, status, conclusion, created_at}'

Repository: rahiseko-alt/ai-editer

Length of output: 1067


🏁 Script executed:

#!/bin/bash
set -euo pipefail

repo=rahiseko-alt/ai-editer
head=1fe0a8b4df4e810e4df057c91f50d1cb6c1f544a

gh api "repos/$repo/commits/$head" \
  --jq '{sha: .sha, parents: [.parents[].sha], files: [.files[].filename]}'

gh api "repos/$repo/contents/docs/roadmap.html?ref=$head" --jq '.content' |
  base64 -d |
  rg -n -A8 -B2 '"id": "M-1-[ABCD]"' |
  tail -n 80

Repository: rahiseko-alt/ai-editer

Length of output: 2756


Run CI for commit 1fe0a8b4df4e810e4df057c91f50d1cb6c1f544a before marking M-1 leaves done.

Run 30875193419 passed all 16 face-mosaic checks, but its head_sha is the parent commit 9be409128567299b91ef23bbaf901853ca9be658. The four done transitions were added in 1fe0a8b4df4e810e4df057c91f50d1cb6c1f544a. Use a CI run for the current commit as evidence.

📍 Affects 1 file
  • docs/roadmap.html#L966-L971 (this comment)
  • docs/roadmap.html#L981-L986
  • docs/roadmap.html#L995-L1000
  • docs/roadmap.html#L1009-L1015
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/roadmap.html` around lines 966 - 971, Replace the evidence at
docs/roadmap.html lines 966-971, 981-986, 995-1000, and 1009-1015 with CI run
evidence whose head_sha is commit 1fe0a8b4df4e810e4df057c91f50d1cb6c1f544a, then
keep the corresponding status fields marked done.

Source: Coding guidelines

}
]
},
Expand All @@ -978,12 +978,12 @@
"theme": "顔が動いても隠したまま追いかける",
"meaning": "人が動くとモザイクが置いていかれる、が起きない",
"detail": "検出は間引き(数フレームおき)で行い、間はトラックIDで対応付けて補間する。事前検証で、順序で補間するとキーフレーム間で人物の並びが入れ替わった瞬間に枠が人物間を飛び素顔が露出するバグを実際に踏んだため、対応付けはトラックIDで行うことを設計上の必須事項とする。",
"status": "todo",
"status": "done",
"criteria": [
{
"text": "顔が移動する固定テスト動画で、全フレームの隠し枠が実際の顔位置に重なっている",
"verify": "顔が移動する固定テスト動画を処理し、全フレームで隠し枠と実際の顔位置の重なり(IoU)が0.5以上であることをテストで確認する",
"evidence": ""
"evidence": "https://github.com/rahiseko-alt/ai-editer/actions/runs/30875193419"
}
]
},
Expand All @@ -992,12 +992,12 @@
"kind": "state",
"theme": "一瞬顔を見失っても素顔が出ない",
"meaning": "横を向いた瞬間だけモザイクが消える、が起きない",
"status": "todo",
"status": "done",
"criteria": [
{
"text": "顔検出が途切れたコマでも、直前の位置で隠しが継続する",
"verify": "検出結果を人為的に欠落させた入力で処理し、欠落コマにも隠し枠が出力されることをテストで確認する",
"evidence": ""
"evidence": "https://github.com/rahiseko-alt/ai-editer/actions/runs/30875193419"
}
]
},
Expand All @@ -1006,13 +1006,13 @@
"kind": "state",
"theme": "隠した顔は本当に判別できなくなっている",
"meaning": "モザイクをかけた気になっているだけ、が起きない",
"detail": "事前検証で、顔の高さ250pxに対しブロック8px以下では顔検出も本人識別も通ってしまい(類似度0.76〜0.95)、10px以上で顔として検出されなくなることを実測した。粒度は固定pxではなく顔サイズに対する比で持つ設計とする(目安: ブロック >= 顔の高さの約1/14)。",
"status": "todo",
"detail": "事前検証で、顔の高さ250pxに対しブロック8px以下では顔検出も本人識別も通ってしまう(類似度0.76〜0.95)ことを実測した。実装時に顔サイズを変えて閾値を測り直したところ、必要なブロックサイズは比率だけでも絶対値だけでも決まらないと判明: 顔高さ65px->最小7px(1/9)、111px->9px(1/12)、181px->10px(1/18)、290px->10px(1/29)。小さい顔ほど必要な比が大きく、大きい顔では絶対値が10px前後で頭打ちになる。よって block = max(顔高さ x 1/8, 12px) と両方を効かせる実装にした(face_mosaic.py の BLOCK_RATIO_DEFAULT / BLOCK_MIN_PX)。なおこの表は「機械が顔として検出できなくなる」境界であって「人が本人と分からなくなる」境界ではないため、大きく写った顔ほど比を効かせて粗くしている。",
"status": "done",
"criteria": [
{
"text": "出力動画の顔部分を顔検出器にかけても顔として検出されない",
"verify": "出力動画に対して顔検出を再実行し、検出数が0であることをテストで確認する",
"evidence": ""
"evidence": "https://github.com/rahiseko-alt/ai-editer/actions/runs/30875193419"
}
]
}
Expand Down
4 changes: 4 additions & 0 deletions video-shorts/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ output/
work/
samples/
*.mp4
*.webm
*.mov
*.mkv
*.m4a
*.wav
*.ass
*.srt
Expand Down
2 changes: 1 addition & 1 deletion video-shorts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@
"name": "video-shorts",
"private": true,
"scripts": {
"test": "node tests/smoke.mjs && python3 tests/transcribe-corrections-check.py && node tests/restart-reconnect-check.mjs && node tests/cli-job-isolation-check.mjs"
"test": "node tests/smoke.mjs && python3 tests/transcribe-corrections-check.py && python3 tests/face-mosaic-check.py && node tests/restart-reconnect-check.mjs && node tests/cli-job-isolation-check.mjs"
}
}
5 changes: 5 additions & 0 deletions video-shorts/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,8 @@
# 文字起こし: ローカル(faster-whisper) と クラウド(groq) の二段構え
faster-whisper>=0.10.0
groq>=1.5.0

# 顔モザイク: 顔検出(YuNet)は opencv 同梱の cv2.FaceDetectorYN を使う。
# モデル本体は src/models/ に同梱しているので追加ダウンロードは発生しない。
# headless 版はGUI依存を持たないぶん軽い(本ツールはプレビュー画面を出さない)。
opencv-python-headless>=4.9.0
Loading
Loading