akari-video 0.1.8 → 0.1.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (144) hide show
  1. package/package.json +1 -1
  2. package/src/capability-sources.mjs +5 -2
  3. package/src/status-command.mjs +8 -1
  4. package/vendor/.akari-capability-sources.json +11 -19
  5. package/vendor/docs/contract-2026-08-02-preview-parity.md +1 -0
  6. package/vendor/docs/contract-2026-08-02-setup-remote-v0.md +13 -1
  7. package/vendor/docs/contract-2026-08-11-analysis-vision-tracks-v0.md +170 -13
  8. package/vendor/docs/contract-2026-08-11-review-session-ui-events.md +19 -7
  9. package/vendor/docs/contract-2026-08-12-chat-approval-v0.md +99 -0
  10. package/vendor/docs/contract-2026-08-12-color-range-normalization-v0.md +59 -0
  11. package/vendor/docs/contract-2026-08-12-region-filter-layer-v0.md +98 -0
  12. package/vendor/docs/contract-2026-08-12-still-image-cut-source-v0.md +163 -0
  13. package/vendor/docs/contract-2026-08-13-avatar-drive-v0.md +258 -0
  14. package/vendor/docs/contract-2026-08-14-avatar-vrm-v0.md +336 -0
  15. package/vendor/packages/akari-launcher/package.json +9 -5
  16. package/vendor/packages/analysis-report/package.json +8 -4
  17. package/vendor/packages/asset-resolver/src/entitlements.mjs +23 -7
  18. package/vendor/packages/asset-resolver/src/resolve.mjs +1 -1
  19. package/vendor/packages/asset-resolver/src/state.mjs +13 -5
  20. package/vendor/packages/asset-resolver/test/catalog-and-state.test.mjs +16 -1
  21. package/vendor/packages/asset-resolver/test/entitlements.test.mjs +37 -9
  22. package/vendor/packages/asset-resolver/test/resolve-paid-zip.test.mjs +4 -1
  23. package/vendor/packages/audio-library-setup/bin/fetch-akari-sounds.mjs +96 -13
  24. package/vendor/packages/audio-library-setup/test/fetch-akari-sounds.test.mjs +269 -0
  25. package/vendor/packages/bake-layer/package.json +8 -4
  26. package/vendor/packages/chat-bridge/package.json +18 -0
  27. package/vendor/packages/decision-cards/package.json +8 -4
  28. package/vendor/packages/edit-lint/src/derive-tracks.mjs +42 -0
  29. package/vendor/packages/edit-lint/src/edit-lint.mjs +4202 -0
  30. package/vendor/packages/edit-lint/src/report.mjs +100 -0
  31. package/vendor/packages/edit-store/lib/caption-display.d.ts +107 -0
  32. package/vendor/packages/edit-store/lib/caption-display.js +812 -0
  33. package/vendor/packages/edit-store/lib/caption-store.d.ts +127 -0
  34. package/vendor/packages/edit-store/lib/caption-store.js +1000 -0
  35. package/vendor/packages/edit-store/lib/caption-window.d.ts +23 -0
  36. package/vendor/packages/edit-store/lib/caption-window.js +29 -0
  37. package/vendor/packages/edit-store/lib/edit-store.d.ts +219 -0
  38. package/vendor/packages/edit-store/lib/edit-store.js +1753 -0
  39. package/vendor/packages/edit-store/lib/index.d.ts +10 -0
  40. package/vendor/packages/edit-store/lib/index.js +26 -0
  41. package/vendor/packages/edit-store/lib/timeline-map.d.ts +59 -0
  42. package/vendor/packages/edit-store/lib/timeline-map.js +160 -0
  43. package/vendor/packages/edit-store/lib/webview-kernel.d.ts +15 -0
  44. package/vendor/packages/edit-store/lib/webview-kernel.js +194 -0
  45. package/vendor/packages/edit-store/lib/write-gate.d.ts +43 -0
  46. package/vendor/packages/edit-store/lib/write-gate.js +196 -0
  47. package/vendor/packages/export-nle/package.json +8 -4
  48. package/vendor/packages/intake-form/package.json +8 -4
  49. package/vendor/packages/media-bin/src/index.mjs +27 -8
  50. package/vendor/packages/media-bin/test/media-bin.test.mjs +71 -3
  51. package/vendor/packages/overlay-runtime/README.md +48 -16
  52. package/vendor/packages/preview-server/package.json +8 -3
  53. package/vendor/packages/render-cut/package.json +9 -5
  54. package/vendor/packages/schemas/analysis.schema.json +16 -1
  55. package/vendor/packages/schemas/bin/validate-asset.mjs +51 -1
  56. package/vendor/packages/schemas/bin/validate-connections.mjs +1 -1
  57. package/vendor/packages/schemas/bin/validate-edit.mjs +41 -3
  58. package/vendor/packages/schemas/captions.schema.json +2 -1
  59. package/vendor/packages/schemas/connections.schema.json +1 -1
  60. package/vendor/packages/schemas/edit.schema.json +63 -5
  61. package/vendor/packages/schemas/examples/connections-v0-invalid-kind/connections.json +33 -0
  62. package/vendor/packages/schemas/examples/connections-v0-notify-valid/connections.json +36 -0
  63. package/vendor/packages/schemas/examples/edit-cuts-still-image-source-valid/edit.json +14 -0
  64. package/vendor/packages/schemas/examples/edit-layers-filter-invalid-lut-missing-id/edit.json +15 -0
  65. package/vendor/packages/schemas/examples/edit-layers-filter-invalid-missing-filter/edit.json +14 -0
  66. package/vendor/packages/schemas/examples/edit-layers-filter-invalid-src-present/edit.json +16 -0
  67. package/vendor/packages/schemas/examples/edit-layers-filter-invalid-unknown-type/edit.json +15 -0
  68. package/vendor/packages/schemas/examples/edit-layers-filter-invert-valid/edit.json +15 -0
  69. package/vendor/packages/schemas/examples/edit-layers-filter-lut-valid/edit.json +15 -0
  70. package/vendor/packages/schemas/examples/edit-layers-filter-saturation-valid/edit.json +19 -0
  71. package/vendor/packages/schemas/test/analysis-body-pose-3d-pointer.test.mjs +14 -0
  72. package/vendor/packages/schemas/test/analysis-face-contour-pointer.test.mjs +19 -0
  73. package/vendor/packages/schemas/test/analysis-face-expression-pointer.test.mjs +14 -0
  74. package/vendor/packages/schemas/test/edit-layer-filter-schema.test.mjs +65 -0
  75. package/vendor/packages/schemas/test/fixtures/asset/invalid-scene3d-model-missing-glb/scene3d/hero-missing-glb/fragment.html +1 -0
  76. package/vendor/packages/schemas/test/fixtures/asset/invalid-scene3d-model-missing-glb/scene3d/hero-missing-glb/meta.json +23 -0
  77. package/vendor/packages/schemas/test/fixtures/asset/invalid-scene3d-model-missing-glb/scene3d/hero-missing-glb/preview.png +0 -0
  78. package/vendor/packages/schemas/test/fixtures/asset/invalid-scene3d-neither/scene3d/hero-neither/fragment.html +1 -0
  79. package/vendor/packages/schemas/test/fixtures/asset/invalid-scene3d-neither/scene3d/hero-neither/meta.json +23 -0
  80. package/vendor/packages/schemas/test/fixtures/asset/invalid-scene3d-neither/scene3d/hero-neither/preview.png +0 -0
  81. package/vendor/packages/schemas/test/fixtures/asset/valid-scene3d-texts-only/scene3d/hero-texts-only/fragment.html +1 -0
  82. package/vendor/packages/schemas/test/fixtures/asset/valid-scene3d-texts-only/scene3d/hero-texts-only/meta.json +23 -0
  83. package/vendor/packages/schemas/test/fixtures/asset/valid-scene3d-texts-only/scene3d/hero-texts-only/preview.png +0 -0
  84. package/vendor/packages/schemas/test/validate-asset.test.mjs +19 -0
  85. package/vendor/packages/schemas/test/validate-connections.test.mjs +15 -0
  86. package/vendor/packages/schemas/test/validate-edit.test.mjs +8 -0
  87. package/vendor/packages/template-render/README.ja.md +6 -0
  88. package/vendor/packages/template-render/README.md +6 -0
  89. package/vendor/packages/template-render/package.json +10 -3
  90. package/vendor/presets/textanim/INDEX.md +32 -0
  91. package/vendor/presets/textanim/index.jsonl +47 -0
  92. package/vendor/skills/analyze-footage/bin/face-expression/artifacts.mjs +83 -0
  93. package/vendor/skills/analyze-footage/bin/face-expression/browser.mjs +63 -0
  94. package/vendor/skills/analyze-footage/bin/face-expression/browser.test.mjs +10 -0
  95. package/vendor/skills/analyze-footage/bin/face-expression/euler.mjs +52 -0
  96. package/vendor/skills/analyze-footage/bin/face-expression/euler.test.mjs +46 -0
  97. package/vendor/skills/analyze-footage/bin/face-expression/face-expression-schema.test.mjs +32 -0
  98. package/vendor/skills/analyze-footage/bin/face-expression/face-expression.mjs +348 -0
  99. package/vendor/skills/analyze-footage/bin/face-expression/model-resolver.mjs +78 -0
  100. package/vendor/skills/analyze-footage/bin/face-expression/model-resolver.test.mjs +80 -0
  101. package/vendor/skills/analyze-footage/bin/face-expression/render-page.mjs +74 -0
  102. package/vendor/skills/analyze-footage/bin/face-expression/render.html +7 -0
  103. package/vendor/skills/analyze-footage/bin/face-expression/runtime-artifacts.test.mjs +17 -0
  104. package/vendor/skills/analyze-footage/bin/face-expression/static-server.mjs +89 -0
  105. package/vendor/skills/analyze-footage/bin/face-expression/static-server.test.mjs +19 -0
  106. package/vendor/skills/analyze-footage/bin/face-expression/vendor/tasks-vision-0.10.17/LICENSE.txt +202 -0
  107. package/vendor/skills/analyze-footage/bin/face-expression/vendor/tasks-vision-0.10.17/README-AKARI.md +12 -0
  108. package/vendor/skills/analyze-footage/bin/face-expression/vendor/tasks-vision-0.10.17/vision_bundle.mjs +2 -0
  109. package/vendor/skills/analyze-footage/bin/face-expression/vendor/tasks-vision-0.10.17/wasm/vision_wasm_internal.js +20 -0
  110. package/vendor/skills/analyze-footage/bin/face-expression/vendor/tasks-vision-0.10.17/wasm/vision_wasm_internal.wasm +0 -0
  111. package/vendor/skills/analyze-footage/bin/face-expression/vendor/tasks-vision-0.10.17/wasm/vision_wasm_nosimd_internal.js +20 -0
  112. package/vendor/skills/analyze-footage/bin/face-expression/vendor/tasks-vision-0.10.17/wasm/vision_wasm_nosimd_internal.wasm +0 -0
  113. package/vendor/skills/analyze-footage/bin/vision-tracks/body-pose-3d-schema.test.mjs +47 -0
  114. package/vendor/skills/analyze-footage/bin/vision-tracks/vision-tracks-helper.swift +98 -14
  115. package/vendor/skills/analyze-footage/bin/vision-tracks/vision-tracks.mjs +31 -6
  116. package/vendor/skills/analyze-footage/references/vision-tracks.schema.json +150 -6
  117. package/vendor/skills/analyze-footage/test/fixtures/vision-tracks/dummy-helper.mjs +3 -0
  118. package/vendor/skills/analyze-footage/test/vision-tracks-assembly.test.mjs +5 -1
  119. package/vendor/skills/analyze-footage/test/vision-tracks-check.test.mjs +45 -3
  120. package/vendor/skills/analyze-footage/test/vision-tracks-schema.test.mjs +11 -0
  121. package/vendor/skills/analyze-footage/vision-tracks.md +131 -20
  122. package/vendor/skills/compile-review-session/bin/compile-review-session.mjs +25 -0
  123. package/vendor/skills/compile-review-session/bin/core/compiler.mjs +6 -2
  124. package/vendor/skills/compile-review-session/test/compiler.test.mjs +117 -0
  125. package/vendor/skills/edit-plan/SKILL.md +5 -3
  126. package/vendor/skills/edit-plan/execution.md +17 -10
  127. package/vendor/skills/edit-plan/workflow.md +9 -5
  128. package/vendor/skills/overlay-authoring/3d.md +281 -1
  129. package/vendor/skills/overlay-authoring/motion.md +49 -0
  130. package/vendor/skills/setup-chat-approval/SKILL.md +41 -0
  131. package/vendor/skills/setup-chat-approval/bin/doctor.mjs +132 -0
  132. package/vendor/skills/setup-chat-approval/bin/find-chat-id.mjs +68 -0
  133. package/vendor/skills/setup-chat-approval/setup-token.md +70 -0
  134. package/vendor/skills/setup-chat-approval/smoke-test.md +52 -0
  135. package/vendor/packages/akari-launcher/bin/akari.mjs +0 -77
  136. package/vendor/packages/analysis-report/render-analysis-report.mjs +0 -529
  137. package/vendor/packages/bake-layer/bin/bake-layer.mjs +0 -159
  138. package/vendor/packages/decision-cards/report-helper.mjs +0 -611
  139. package/vendor/packages/export-nle/bin/export-nle.mjs +0 -162
  140. package/vendor/packages/intake-form/intake-form-helper.mjs +0 -37
  141. package/vendor/packages/preview-server/src/server.mjs +0 -682
  142. package/vendor/packages/render-cut/bin/akari-apply-textstyle.mjs +0 -202
  143. package/vendor/packages/render-cut/bin/render-cut.mjs +0 -5
  144. package/vendor/packages/template-render/bin/akari-template-render.mjs +0 -257
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "akari-video",
3
- "version": "0.1.8",
3
+ "version": "0.1.10",
4
4
  "description": "AKARI Video launcher CLI — start an AI-edited video project from any directory: scaffold, connection check, then hand over to Claude Code (or opencode). AKARI Video を opencode や Claude Code で、どのディレクトリからでも始めるための `akari` ランチャー CLI。接続確認(doctor)→ 未セットアップならプロジェクト雛形を作成 → AI エージェントを起動する。外部 npm 依存ゼロ(Node.js 組み込みモジュールのみ)。",
5
5
  "type": "module",
6
6
  "bin": {
@@ -2,7 +2,10 @@ import { execFileSync } from "node:child_process";
2
2
  import { existsSync, lstatSync, readFileSync, realpathSync } from "node:fs";
3
3
  import { dirname, isAbsolute, join, relative, resolve } from "node:path";
4
4
 
5
- export function discoverCheckoutCapabilitySources(repoRoot, { trackedFiles = null } = {}) {
5
+ export function discoverCheckoutCapabilitySources(repoRoot, {
6
+ trackedFiles = null,
7
+ includeBinTargets = true,
8
+ } = {}) {
6
9
  const root = realpathSync(resolve(repoRoot));
7
10
  const tracked = trackedFiles ?? execFileSync("git", ["-C", root, "ls-files", "-z"], {
8
11
  maxBuffer: 64 * 1024 * 1024,
@@ -25,7 +28,7 @@ export function discoverCheckoutCapabilitySources(repoRoot, { trackedFiles = nul
25
28
  throw new Error(`${manifestPath} package.json#bin target is not tracked: ${canonical}`);
26
29
  }
27
30
  assertRegularContainedFile(root, canonical, `${manifestPath} package.json#bin`);
28
- selected.add(canonical);
31
+ if (includeBinTargets) selected.add(canonical);
29
32
  }
30
33
  }
31
34
  const paths = [...selected].sort(compare);
@@ -30,10 +30,17 @@ export async function runStatusCommand(argv, options = {}) {
30
30
  const status = parsed.full
31
31
  ? await resolveFullProjectStatus(parsed.projectRoot)
32
32
  : resolveProjectStatus(parsed.projectRoot, { mode: "fast" });
33
- log(parsed.json ? serializeStatus(status) : `${formatStatusSummary(status)}\n`);
33
+ log(parsed.json ? serializeStatus(status) : `${formatStatusOutput(status)}\n`);
34
34
  return { exitCode: status.state_health === "inconclusive" ? 1 : 0, status };
35
35
  }
36
36
 
37
+ export function formatStatusOutput(status) {
38
+ const summary = formatStatusSummary(status);
39
+ if (status.state_health !== "inconclusive" || status.problems.length === 0) return summary;
40
+ const problems = status.problems.map((problem) => ` - ${problem}`).join("\n");
41
+ return `${summary}\nCould not determine:\n${problems}\nNext: run \`akari status --json\` for the full machine-readable report, or resolve the items above and re-run \`akari status\`.`;
42
+ }
43
+
37
44
  export function parseStatusArguments(argv, cwd = process.cwd()) {
38
45
  let projectRoot = cwd;
39
46
  let pathSeen = false;
@@ -33,34 +33,30 @@
33
33
  "docs/contract-2026-08-05-fx-v0.md",
34
34
  "docs/contract-2026-08-11-analysis-vision-tracks-v0.md",
35
35
  "docs/contract-2026-08-11-review-session-ui-events.md",
36
- "packages/akari-launcher/bin/akari.mjs",
36
+ "docs/contract-2026-08-12-chat-approval-v0.md",
37
+ "docs/contract-2026-08-12-color-range-normalization-v0.md",
38
+ "docs/contract-2026-08-12-region-filter-layer-v0.md",
39
+ "docs/contract-2026-08-12-still-image-cut-source-v0.md",
40
+ "docs/contract-2026-08-13-avatar-drive-v0.md",
41
+ "docs/contract-2026-08-14-avatar-vrm-v0.md",
37
42
  "packages/akari-launcher/package.json",
38
43
  "packages/akari-launcher/README.md",
39
44
  "packages/akari-tools/package.json",
40
45
  "packages/analysis-report/package.json",
41
46
  "packages/analysis-report/README.md",
42
- "packages/analysis-report/render-analysis-report.mjs",
43
- "packages/asset-resolver/bin/akari-assets.mjs",
44
47
  "packages/asset-resolver/package.json",
45
48
  "packages/asset-resolver/README.md",
46
- "packages/audio-library-setup/bin/gallery-helper.mjs",
47
- "packages/audio-library-setup/bin/generate-candidates-html.mjs",
48
- "packages/audio-library-setup/bin/register-drop-folder.mjs",
49
49
  "packages/audio-library-setup/package.json",
50
50
  "packages/audio-library-setup/README.md",
51
- "packages/bake-layer/bin/bake-layer.mjs",
52
51
  "packages/bake-layer/package.json",
53
52
  "packages/bake-layer/README.md",
53
+ "packages/chat-bridge/package.json",
54
54
  "packages/creator-root/package.json",
55
55
  "packages/decision-cards/package.json",
56
56
  "packages/decision-cards/README.md",
57
- "packages/decision-cards/report-helper.mjs",
58
- "packages/edit-lint/bin/edit-lint.mjs",
59
57
  "packages/edit-lint/package.json",
60
58
  "packages/edit-store/package.json",
61
- "packages/export-nle/bin/export-nle.mjs",
62
59
  "packages/export-nle/package.json",
63
- "packages/intake-form/intake-form-helper.mjs",
64
60
  "packages/intake-form/package.json",
65
61
  "packages/intake-form/README.md",
66
62
  "packages/media-bin/package.json",
@@ -70,22 +66,15 @@
70
66
  "packages/preview-engine/package.json",
71
67
  "packages/preview-engine/README.md",
72
68
  "packages/preview-server/package.json",
73
- "packages/preview-server/src/server.mjs",
74
69
  "packages/project-scaffold/package.json",
75
- "packages/render-cut/bin/akari-apply-textstyle.mjs",
76
- "packages/render-cut/bin/render-cut.mjs",
77
70
  "packages/render-cut/package.json",
78
- "packages/schemas/bin/validate-asset.mjs",
79
- "packages/schemas/bin/validate-intake.mjs",
80
- "packages/schemas/bin/validate-plan.mjs",
81
- "packages/schemas/bin/validate-review.mjs",
82
71
  "packages/schemas/package.json",
83
- "packages/template-render/bin/akari-template-render.mjs",
84
72
  "packages/template-render/package.json",
85
73
  "packages/template-render/README.ja.md",
86
74
  "packages/template-render/README.md",
87
75
  "skills/address-review/SKILL.md",
88
76
  "skills/analyze-footage/analysis-json.md",
77
+ "skills/analyze-footage/bin/face-expression/vendor/tasks-vision-0.10.17/README-AKARI.md",
89
78
  "skills/analyze-footage/events-and-hooks.md",
90
79
  "skills/analyze-footage/keyframes-and-review.md",
91
80
  "skills/analyze-footage/media-and-transcript.md",
@@ -158,6 +147,9 @@
158
147
  "skills/setup-audio-library/gallery.md",
159
148
  "skills/setup-audio-library/SKILL.md",
160
149
  "skills/setup-audio-library/store-unlock.md",
150
+ "skills/setup-chat-approval/setup-token.md",
151
+ "skills/setup-chat-approval/SKILL.md",
152
+ "skills/setup-chat-approval/smoke-test.md",
161
153
  "skills/setup-library/fetch-and-validate.md",
162
154
  "skills/setup-library/SKILL.md",
163
155
  "skills/setup-library/starter-pack.md",
@@ -539,6 +539,7 @@ ffmpeg の `perspective` フィルタの制約(式に時刻変数を持たな
539
539
  | `layers[].perspective`(2026-08-06 実装) | ✅(§2.4.4。実ブラウザ実測済み) | ✅(§2.4.4。tsc -b + ユニット + Web 同一計算式で担保) |
540
540
  | `cuts[].fx`(2026-08-07 実装・近似あり) | 🟡(§2.4.5。5 種対応、3 種は近似バッジ付き) | ❌(未実装) |
541
541
  | `layers[].keyframes`(2026-08-09 実装) | ✅(§2.4.7。transform/crop は連続補間。perspective は blend:"normal" のみ・書き出しの段階保持とサンプル点で一致) | ✅(§2.4.7。同左) |
542
+ | `cuts[].static-image-source`(2026-08-12 実装。正本: `contract-2026-08-12-still-image-cut-source-v0.md`) | 🟡(`<img>`/`<video>` 出し分け + preview-engine ClipSession/Timeline の image 対応を実装。framing/freeze/transform は流用。実ブラウザでの対話的スクラブ・複数区間切替の実機検証は未実施) | ❌(スコープ外・未対応) |
542
543
 
543
544
  - `cuts[].framing`(静的クロップ / ズームキーフレーム)・`cuts[].freeze`(フリーズ)は
544
545
  `contract-2026-07-22-render-basics.md` #6/#7 としてレンダ(render-cut)に加え、
@@ -118,7 +118,19 @@ OK / 差し戻しを言う」だけ — その閲覧と素材の受け渡しを
118
118
 
119
119
  | 未達 | 状況 |
120
120
  |---|---|
121
- | HTTPS | **`tailscale serve` の TLS が未成立**。tailnet 側で HTTPS は有効(`CertDomains` に当該ドメインあり)だが Let's Encrypt の ACME 注文が `invalid` で失敗。上記の実測は tailnet 内の平文 HTTP で代替した(WireGuard 内側のため機密性は保たれるが、secure context を要する機能は使えない)。**平文 HTTP を既定にはしない** |
121
+ | HTTPS | **`tailscale serve` の TLS が未成立**。上記の実測は tailnet 内の平文 HTTP で代替した(WireGuard 内側のため機密性は保たれるが、secure context を要する機能は使えない)。**平文 HTTP を既定にはしない** |
122
+
123
+ **HTTPS 失敗の切り分け結果(2026-08-12)** — スキル側・serve 設定側の不備ではないと確定:
124
+
125
+ - tailnet で HTTPS は有効(`tailscale status --json` の `CertDomains` に当該ドメインあり)
126
+ - serve の設定自体は正常(同じ経路が平文 HTTP では 200 / 0.28 秒で応答する)
127
+ - CAA は発行を許可している(`ts.net` に `issue "letsencrypt.org"`)
128
+ - Let's Encrypt の注文が毎回 `status: invalid`(注文 ID は毎回新規 = レート制限ではなく検証失敗)
129
+ - **`_acme-challenge.<host>.<tailnet>.ts.net` に使い捨て TXT が 5 件累積**していた。
130
+ DNS-01 の検証失敗と符合する(現行トークンが正しく公開されていない疑い)
131
+ - → **tailnet 側(Tailscale の DNS / 制御プレーン)の問題**。再試行を重ねても TXT が増えるだけで
132
+ 好転しないため、打ち切って次の一手に回す。候補: (a) マシン名を変更して
133
+ `_acme-challenge` の名前ごと作り直す(serve URL が変わる)(b) Tailscale サポートに問い合わせる
122
134
 
123
135
  ## 8. 改訂履歴
124
136
 
@@ -1,10 +1,10 @@
1
1
  ---
2
2
  lifecycle: draft
3
3
  created: 2026-08-11
4
- updated: 2026-08-11
4
+ updated: 2026-08-14
5
5
  ---
6
6
 
7
- # 分析トラック契約 v0 — Vision ランドマーク・トラック(face-landmarks / hand-pose)と keyframes 消費
7
+ # 分析トラック契約 v0 — Vision ランドマーク・トラック(face-landmarks / hand-pose / body-pose-3d / face-expression)と keyframes 消費
8
8
 
9
9
  - 日付: 2026-08-11
10
10
  - 状態: **ドラフト**(v0 実装と同時に確定させる。実装で判明した齟齬は追記で解消)
@@ -13,8 +13,9 @@ updated: 2026-08-11
13
13
  - `contract-2026-07-23-analysis-person-matte.md`(Swift サイドカーの流儀・analysis.json の tracks 契約・検証責務の分担)
14
14
  - `contract-2026-07-25-project-structure-v0.md`(分析サイドカーの置き場 = `.akari/sidecars/`)
15
15
  - `contract-2026-08-02-preview-parity.md`(render/Web/shell 3 面パリティの原則)
16
- - スコープ: 動画から抽出する**ランドマーク・トラック**(顔・手)のデータ契約、生成サイドカーの
17
- 入出力、消費(`layers[].keyframes` への変換)の責務分担。**新しいレンダー機構は作らない**
16
+ - スコープ: 動画から抽出する**ランドマーク・トラック**(顔・手・3D ボディポーズ)のデータ契約、生成サイドカーの
17
+ 入出力、消費(`layers[].keyframes` への変換)の責務分担、および MediaPipe 由来の頭部姿勢・
18
+ 表情トラック。**新しいレンダー機構は作らない**
18
19
 
19
20
  ## 0. 設計原則
20
21
 
@@ -34,7 +35,7 @@ updated: 2026-08-11
34
35
 
35
36
  ## 1. analysis.json への追加(additive)
36
37
 
37
- `tracks` に **optional キー** 2 つを追加する。`tracks.required`(speakers / faces /
38
+ `tracks` に **optional キー** 3 つを追加する。`tracks.required`(speakers / faces /
38
39
  person_matte)には**入れない**(person_matte が必須なのは既存消費者の事情であり、新トラックは
39
40
  真に任意として追加する)。
40
41
 
@@ -48,18 +49,40 @@ person_matte)には**入れない**(person_matte が必須なのは既存消
48
49
  "tool": "vision-tracks.mjs v0",
49
50
  "generated_at": "2026-08-11T12:00:00Z"
50
51
  },
51
- "hand_pose": { /* 同形 */ }
52
+ "hand_pose": { /* 同形 */ },
53
+ "body_pose_3d": { // 任意。macOS 14+ の Vision 3D ボディポーズ
54
+ "path": "vision/body-pose-3d.json",
55
+ "sample_fps": 24,
56
+ "provider": "apple-vision",
57
+ "tool": "vision-tracks.mjs v0",
58
+ "generated_at": "2026-08-13T12:00:00Z"
59
+ }
60
+ }
61
+ ```
62
+
63
+ 2026-08-14 additive: 同じ optional pointer 形で `face_expression` を追加する。既存 3 キーと同様に
64
+ `tracks.required` には入れず、未生成はキー無しで表す。
65
+
66
+ ```jsonc
67
+ "face_expression": {
68
+ "path": "vision/face-expression.json",
69
+ "sample_fps": 24,
70
+ "provider": "mediapipe-face-landmarker",
71
+ "tool": "face-expression.mjs v0",
72
+ "generated_at": "2026-08-14T12:00:00Z",
73
+ "features": ["head-pose-ypr-radians", "mediapipe-blendshapes-52"]
52
74
  }
53
75
  ```
54
76
 
55
77
  ## 2. トラックファイル形式(vision-tracks v0)
56
78
 
57
- `path` が指す JSON ファイル。1 ファイル 1 種類(face-landmarks と hand-pose は別ファイル)。
79
+ `path` が指す JSON ファイル。1 ファイル 1 種類(face-landmarks / hand-pose /
80
+ body-pose-3d / face-expression は別ファイル)。
58
81
 
59
82
  ```jsonc
60
83
  {
61
84
  "version": 0,
62
- "kind": "face-landmarks", // "face-landmarks" | "hand-pose"
85
+ "kind": "face-landmarks", // "face-landmarks" | "hand-pose" | "body-pose-3d"
63
86
  "source": { "path": "../..(元動画への相対)", "duration": 12.5 },
64
87
  "sample_fps": 24,
65
88
  "provider": { "name": "apple-vision", "os": "macOS 15.5" },
@@ -70,8 +93,11 @@ person_matte)には**入れない**(person_matte が必須なのは既存消
70
93
  }
71
94
  ```
72
95
 
73
- - **座標系(最重要規約)**: すべて **0〜1 正規化・左上原点**(動画ピクセル系と同じ向き)。
96
+ - **画像座標系(最重要規約)**: face-landmarks / hand-pose と body-pose-3d の
97
+ `projection` はすべて **0〜1 正規化・左上原点**(動画ピクセル系と同じ向き)。
74
98
  Vision framework は左下原点で返すため、**y 反転はサイドカーの責務**。消費側は変換しない
99
+ - body-pose-3d の `position` だけは画像座標ではない。Vision が返すモデル座標
100
+ (root/hip 相対メートル)を変換せず保存する(§2.4)
75
101
  - `samples[].t` は元動画の秒(`in`/`out` と同じ時間軸)。サンプリングは `sample_fps` の等間隔
76
102
  - 生値主義: 平滑化・補間済みの値を入れない。信頼度(`conf`)を必ず併記する
77
103
 
@@ -126,6 +152,86 @@ person_matte)には**入れない**(person_matte が必須なのは既存消
126
152
  (§2.1 の必須 6 キーが揃わない detection を出力に混ぜない)。実測(1 名がほぼ映り続ける
127
153
  26.3 秒素材)ではこの除外は 0 件だった。
128
154
 
155
+ ### 2.4 body-pose-3d の detection(additive・2026-08-13)
156
+
157
+ `VNDetectHumanBodyPose3DRequest` revision 1 が返す 17 関節を保存する。API は macOS 14+
158
+ 限定である。トラックは分析の生値であり、平滑化・間引き・低信頼度除外を行わない。
159
+
160
+ ```jsonc
161
+ {
162
+ "conf": 0.86,
163
+ "joints": {
164
+ "root": {
165
+ "position": [0.0, 0.0, 0.0], // root/hip 相対メートル
166
+ "projection": [0.51, 0.63], // 0〜1 正規化・左上原点
167
+ "conf": 0.86
168
+ },
169
+ "right_hip": { /* 同形 */ },
170
+ "right_knee": { /* 同形 */ },
171
+ "right_ankle": { /* 同形 */ },
172
+ "left_hip": { /* 同形 */ },
173
+ "left_knee": { /* 同形 */ },
174
+ "left_ankle": { /* 同形 */ },
175
+ "spine": { /* 同形 */ },
176
+ "center_shoulder": { /* 同形 */ },
177
+ "center_head": { /* 同形 */ },
178
+ "top_head": { /* 同形 */ },
179
+ "left_shoulder": { /* 同形 */ },
180
+ "left_elbow": { /* 同形 */ },
181
+ "left_wrist": { /* 同形 */ },
182
+ "right_shoulder": { /* 同形 */ },
183
+ "right_elbow": { /* 同形 */ },
184
+ "right_wrist": { /* 同形 */ }
185
+ }
186
+ }
187
+ ```
188
+
189
+ - `position`: `VNHumanBodyRecognizedPoint3D.position` の平行移動成分 `[x,y,z]`。
190
+ Vision のモデル座標で root/hip 相対、単位はメートル。カメラ相対座標へ変換しない
191
+ - `projection`: `VNHumanBodyPose3DObservation.pointInImage` が返す画像投影を y 反転し、
192
+ `[0,1]` へクランプした `[x,y]`
193
+ - `conf`: Vision 3D API は関節別 confidence を公開しないため、apple-vision provider の v0 は
194
+ `VNHumanBodyPose3DObservation.confidence` を各関節へ複製する。これは関節別推定値ではなく、
195
+ 観測全体 confidence の由来明示である。消費者の `min-confidence` はこの値を使う
196
+ - 17 関節のいずれかを Vision から取得できない観測は detection ごと省略する。存在しない
197
+ 関節を補間・捏造せず、`detections: []` のフレームは時刻 `t` とともに残す
198
+ - pose-skeleton の v0 は各フレーム先頭の 1 人(`bodyIndex=0` 固定)のみを消費し、複数人には非対応
199
+
200
+ ### 2.5 face-expression の detection(additive・2026-08-14)
201
+
202
+ MediaPipe Face Landmarker が返す `facialTransformationMatrixes` と 52 blendshape category を、
203
+ 平滑化・補間せず保存する。1 ファイル 1 kind の `kind` は `face-expression`、analysis pointer は
204
+ `tracks.face_expression`、既定ファイル名は `vision/face-expression.json` とする。顔が検出されない
205
+ frame も `{ "t": ..., "detections": [] }` として残す。
206
+
207
+ ```jsonc
208
+ {
209
+ "head": {
210
+ "yaw": 0.12,
211
+ "pitch": -0.04,
212
+ "roll": 0.02
213
+ },
214
+ "blendshapes": {
215
+ "_neutral": 0.07,
216
+ "eyeBlinkLeft": 0.01,
217
+ "mouthSmileLeft": 0.64
218
+ // MediaPipe 固定 category 全 52 キー。各値は 0..1 の生 score
219
+ },
220
+ "conf": 0.64
221
+ }
222
+ ```
223
+
224
+ - `head` は 4x4 row-major 同次変換の上左 3x3 を行正規化し、
225
+ `R = Rz(roll) * Ry(yaw) * Rx(pitch)` で分解した**ラジアン**。右手系の解釈は +X=画像右、
226
+ +Y=画像下、+Z=canonical face 前方で、yaw 正=画面右向き、pitch 正=上向き、
227
+ roll 正=時計回り。gimbal lock は `roll=0` に固定する
228
+ - `blendshapes` は `_neutral` を含む MediaPipe の固定 52 category。キーは表現の生 score で、
229
+ 並びだけ byte 安定のため名前順へ正規化する。値の平滑化・クランプ・感情ラベル化はしない
230
+ - MediaPipe Web API は内部の face-presence score を結果へ公開しない。`conf` は捏造した定数ではなく、
231
+ 同じ detection に返った 52 生 score の最大値を signal confidence として決定論的に記録する。
232
+ face detection confidence そのものではないため、消費者は検出有無と blendshape 個別値を主に使う
233
+ - v0 は `numFaces=1`。複数人追跡・人物同一性の連結はしない
234
+
129
235
  ## 3. サイドカー(生成側)
130
236
 
131
237
  person-matte と同じ分離: **Swift ヘルパーはフレーム変換だけ、コンテナ・時刻・組み立ては
@@ -135,25 +241,73 @@ person-matte と同じ分離: **Swift ヘルパーはフレーム変換だけ、
135
241
  - `vision-tracks-helper.swift` — stdin から raw BGRA フレーム列、stdout へ **JSON Lines
136
242
  (1 フレーム 1 行の検出結果)**。`swiftc -O` オンデマンドビルド・バイナリは `.gitignore`
137
243
  - `vision-tracks.mjs` — `ffmpeg`(デコード・fps/幅統一)→ helper → トラックファイル組み立て →
138
- `analysis.json` の tracks へ追記(原子的置換)。`--kinds face,hand` / `--fps` / `--check`
139
- (macOS / swiftc / ffmpeg の可用性確認)
244
+ `analysis.json` の tracks へ追記(原子的置換)。`--kinds face,hand,body-pose-3d` /
245
+ `--fps` / `--check`(macOS 14+ / swiftc / ffmpeg の可用性確認。macOS 14 未満は
246
+ capability 不足として理由付きで拒否)
140
247
  - 手順書: `skills/analyze-footage/vision-tracks.md`(person-matte.md と同格の任意工程)。
141
248
  `SKILL.md` の実行順・ハードルールに配線する
142
249
  - 起動主体はエージェント(スキル手順に従い bash で直接叩く)。CLI サブコマンドにはしない
143
250
  (判断を伴う工程はスキル、の境界裁定に従う)
144
251
 
252
+ ### 3.1 face-expression の headless Chromium 生成器(additive・2026-08-14)
253
+
254
+ `skills/analyze-footage/bin/face-expression/face-expression.mjs` は face-expression 専用の独立生成器。
255
+ 既存 Swift helper は変更せず、ffmpeg で 24 fps(`--fps` 変更可)・幅 1280 以下の PNG 列へ
256
+ デコードし、Chrome for Testing + `puppeteer-core` のページ内で CPU/WASM 版 Face Landmarker を
257
+ 順に実行する。Chrome の探索順・起動引数・ページ処理後の結果吸い上げは avatar-vrm の既存
258
+ headless 経路を踏襲する。
259
+
260
+ JS 版を選んだ理由は、`@mediapipe/tasks-vision` がブラウザ/WASM 専用である一方、製品には既に
261
+ headless Chromium 経路があり、Python wheel や Swift helper という新しい実行系を増やさずに済むため。
262
+ 固定 Chrome・固定 WASM・CPU delegate・固定時刻入力により、同一環境では同じ行列分解と score 列を
263
+ 得られる。Python 版は arm64 wheel という別インストール面とバージョン解決を増やすため v0 では採らない。
264
+
265
+ モデルはリポジトリへ置かず、初回だけ次を取得する。`AKARI_HOME` があればそれを優先し、既定は
266
+ `~/.akari/models/mediapipe/face-landmarker/float16-1/face_landmarker.task`。既存ファイルも毎回
267
+ SHA-256 検査し、不一致時は再取得で隠さず即エラーにする。新規取得は `.tmp-<pid>` へ書き、検証後に
268
+ rename する。
269
+
270
+ - URL: `https://storage.googleapis.com/mediapipe-models/face_landmarker/face_landmarker/float16/1/face_landmarker.task`
271
+ - SHA-256: `64184e229b263107bc2b804c6625db1341ff2bb731874b0bcc2fe6544e0bc9ff`
272
+
273
+ ブラウザ runtime はネットワーク変動を避けるため `@mediapipe/tasks-vision@0.10.17` の必要ファイルを
274
+ 無変換で vendor した。ライセンスは Apache-2.0。
275
+
276
+ | artifact | SHA-256 |
277
+ |---|---|
278
+ | npm tarball | `d3dd0759295f1adcf5455f22aa652c58b8c1d537c0d14c8db7df78646011d523` |
279
+ | `vision_bundle.mjs` | `1ada13431ea2a8ed7ea449e6c3595122d43fea2a8a4788056ed7da271469b402` |
280
+ | `vision_wasm_internal.js` | `33a4125f825b343d2d9773951a73692f40bee368c9b591af8ff652fd501af90b` |
281
+ | `vision_wasm_internal.wasm` | `c88cf472dd5cab0a3954b071e5f442102ded3701dcccc987a7a02ee8f54aae85` |
282
+ | `vision_wasm_nosimd_internal.js` | `4e8d07dcf8cbb55b343cd76b7fc30d4303220f049d5529d6412f6f93296726a8` |
283
+ | `vision_wasm_nosimd_internal.wasm` | `f840f69d7229f89dedaed39c7ac7a52f0964a7cec02d6cb1ac9eff891db86dc2` |
284
+ | `LICENSE.txt` | `b070d77bfb2c52a1dd6996de0ce5f64c49a0ca55c889b163a963ddf5cb001ee2` |
285
+
286
+ > 注: `LICENSE.txt` のみ npm tarball には含まれず(tarball 展開で実測確認済み)、Apache-2.0 の定型全文を手動で添えたもの(出所は `vendor/.../README-AKARI.md` 参照)。他の行は全て tarball 由来の実ファイル。
287
+
288
+ tarball は `tar -xzf` 後、上記 bundle/WASM だけをコピーし、esbuild 等の再ビルドは行っていない。
289
+ tarball 自体の npm integrity は
290
+ `sha512-CZWV/q6TTe8ta61cZXjfnnHsfWIdFhms03M9T7Cnd5y2mdpylJM0rF1qRq+wsQVRMLz1OYPVEBU9ph2Bx8cxrg==`。
291
+
145
292
  ## 4. 消費(変換器)
146
293
 
147
- トラック → `edit.json` への反映は**決定論の変換器**が行う。v0 の消費者は 2 つ(別契約で
294
+ トラック → `edit.json` への反映は**決定論の変換器**が行う。v0 の消費者は 3 つ(別契約で
148
295
  実装しても本契約の §2 形式だけを入力にする):
149
296
 
150
297
  | 消費者 | 入力 | 出力 |
151
298
  |---|---|---|
152
299
  | eye-bar(目線黒帯) | face_landmarks(両瞳) | 黒帯レイヤー + `layers[].keyframes` の transform(x/y/rotate/scale) |
153
300
  | finger-frame(指フレーム) | hand_pose(両手の thumb_tip / index_tip = 4 点) | 対象レイヤーの `layers[].keyframes` の perspective(4 隅 corner-pin)+ 発動区間 |
301
+ | pose-skeleton | body_pose_3d(17 関節の 2D `projection`) | アルファ付きスティックフィギュアを事前ベイクした `kind: "baked"` layer |
302
+
303
+ `face_expression` のアバター駆動への結線は次の消費側契約に委ねる。本契約では生成 SSOT までとし、
304
+ 既存の「あいうえお」口パクや avatar-vrm / avatar-drive を変更しない。
154
305
 
155
306
  - 平滑化(移動平均・One Euro 等)・キーフレーム間引き・欠測補間は**変換器の責務**。
156
307
  パラメータは変換器の引数で決定論に
308
+ - pose-skeleton は欠測と cut 境界で平滑化状態をリセットし、低 confidence 関節を含む骨を
309
+ 非表示にする。欠測区間を hold せず、別 baked clip / layer へ分割する
310
+ - pose-skeleton の v0 は `bodyIndex=0` 固定で各フレーム先頭の 1 人だけを対象とし、複数人には非対応
157
311
  - perspective keyframes の ffmpeg 側は既存の時間窓分割フォールバック
158
312
  (`expandLayerForPerspectiveKeyframes`)に乗る。新しいレンダー経路は作らない
159
313
  - `cuts[].fx` は**使わない**(全画面ポスト効果の器。空間追跡系はレイヤー機構が正)
@@ -163,10 +317,13 @@ person-matte と同じ分離: **Swift ヘルパーはフレーム変換だけ、
163
317
  - `packages/schemas/bin/` に新しいバリデータ CLI は**作らない**(person-matte 契約 §7 の分担を
164
318
  継続)。トラックファイルの JSON Schema は `skills/analyze-footage/references/
165
319
  vision-tracks.schema.json` に置き、スキル手順の jsonschema 検証が担う
166
- - `analysis.schema.json` への tracks 2 キー追加は additive のみ
320
+ - `analysis.schema.json` への tracks 4 キー追加は additive のみ
167
321
  - **`packages/analysis-report/render-analysis-report.mjs` の軽量チェックを同時に更新する**
168
322
  (person_matte が残した「消費者の追随債務」を新トラックで繰り返さない)
169
323
  - verify は L0(該当 package / skill の `node --test`)。GUI を触らないため L1/L2 は対象外
324
+ - face-expression は schema wiring、52 キー固定、同一行列の Euler 分解一致、モデル初回配置と
325
+ cached/downloaded 両方の SHA-256 不一致拒否を unit test する。実素材の検出率・CPU 時間・
326
+ 実時間比は fieldtest 12 秒窓で別途記録し、値を捏造して本契約へ先書きしない
170
327
 
171
328
  ## 6. やらないこと(v0)
172
329
 
@@ -48,6 +48,7 @@
48
48
  | `timeline:cut:<n>` | タイムラインのカット(cuts[] index) | `timeline:cut:3` |
49
49
  | `timeline:overlay:<id>` | タイムラインのオーバーレイ | `timeline:overlay:o-0002` |
50
50
  | `asset:<path>` | 素材(プロジェクト相対 or カタログ id) | `asset:assets/broll/city.mp4` |
51
+ | `asset:<category>/<id>` | 素材(カタログ由来カード。key = `<category>/<id>`) | `asset:still/br-typing-laptop` |
51
52
 
52
53
  - **登録機構**: 記録対象の要素は `data-akari-ui="<target-id>"` 属性で opt-in する。
53
54
  クリック解決は capture-phase のリスナー 1 本で行い、**最近傍の登録済み祖先**に丸める。
@@ -80,10 +81,21 @@
80
81
  - 回帰: 新イベント入りの events.jsonl を旧読み手(compile-review-session の手順)が
81
82
  処理してもエラーにならないこと
82
83
 
83
- ## 6. 予約(次段の実装契約で確定)
84
-
85
- - review.json `annotations[].target` への **`ui:<element-id>`** 追加(選択ツール経由の
86
- UI 要素注釈の着地形)。§2 と同一の id 空間を使う
87
- - 書き出し済み MP4 への注釈は新 target 種を作らず既存 `src` 機構で扱う方針(要検証:
88
- プロジェクト外ファイルの src 正規化)
89
- - 音への注釈(`sourceRange` + トラック指定)の形
84
+ ## 6. 注釈の着地契約
85
+
86
+ - review.json `annotations[].target` の **`ui:<element-id>`** は、選択ツール経由の UI 要素注釈に
87
+ 使う。§2 と同一の id 空間を使い、たとえばオーバーレイは
88
+ `ui:timeline:overlay:<id>` に着地する
89
+ - render-cut は検証に成功した書き出し成果物を、edit.json v1 の `sources[]` へ自動追記する。
90
+ `path` はプロジェクトルート相対、`proxy` は `null` とし、同じ `path` が既にあれば追記しない。
91
+ 既存 id と衝突しない出力 stem 由来の id を使い、既存フィールドと整形は書き換えない。
92
+ edit.json を再読込・パースできない場合は警告だけを残し、書き出し成功を取り消さない。
93
+ v0 は schema 上 `sources` を持てないため自動追記せず、同様に警告する
94
+ - `sources[].path` に一致する動画ファイルを raw preview で開き、そのタブがフォーカスされて
95
+ いる間の注釈は、`src = sources[].id` と raw preview の現在再生位置を source 秒とする
96
+ `sourceT` に着地する。コンポーザーは対象を `🎞 <source id>` チップで表示する。
97
+ 一致しないファイルは従来どおり `src: null` の通常注釈とし、新しい target 種は作らない
98
+ - 音の素材区間は新しい `audio:` target を作らず、`src = sources[].id` と半開区間
99
+ `sourceRange: [start, end)` を使う。BGM 等のオーバーレイ音は既存の `overlay:<id>` または
100
+ `ui:timeline:overlay:<id>` で扱う。音声ファイルは専用 audio preview で開かれるため、raw video
101
+ preview の現在位置接続とは別であり、区間選択 UI は本契約の対象外とする
@@ -0,0 +1,99 @@
1
+ # chat-approval 契約 v0(チャット通知 + ボタン承認)
2
+
3
+ - 日付: 2026-08-12
4
+ - 状態: **v0 ドラフト・要オーナーレビュー**
5
+ - 前提: `contract-2026-08-02-setup-remote-v0.md`(tailnet 限定の閲覧経路。レポート URL の供給元)、
6
+ `contract-2026-07-25-project-structure-v0.md`(承認レポートの実体)、
7
+ `packages/decision-cards/report-helper.mjs`(`decisions.json` の read / write / commit の唯一の実装)、
8
+ `skills/manage-connections`(接続レジストリと credentials の唯一の入口)
9
+ - スコープ: 承認ゲート到達を**チャットへ通知**し、**ボタンのタップだけで**承認を返せるようにする
10
+ - 非スコープ: チャットからの自由文指示、エージェントの起動・操作、素材のチャット搬送、
11
+ 複数チャネル対応(v0 は Telegram 1 本)
12
+
13
+ ## 0. 位置づけ — 一言で言い切る
14
+
15
+ **チャットは配管であって頭脳ではない。** 運ぶのは「できました」という通知・レポートへのリンク・
16
+ 画像・そして**列挙可能な承認の返事**だけ。編集の判断は従来どおり AKARI のパイプラインが持ち、
17
+ 承認の記録は `decisions.json` が持つ。チャット層は SSOT を一切持たない。
18
+
19
+ ## 1. 決定事項
20
+
21
+ | 論点 | 決定 |
22
+ |---|---|
23
+ | 何を足すか | **通知と承認だけ**。エージェントをチャットから起動しない(別契約) |
24
+ | 書き込み経路 | ブリッジは `decisions.json` を**自分で書かない**。report-helper の HTTP API(`POST /api/state` / `POST /api/commit`)を 127.0.0.1 経由で叩く。書き込みの原子性・検証・`0600`・二重確定の 409 は既存実装のまま効く |
25
+ | 受信方式 | **long polling**(`getUpdates`)。公開エンドポイント・webhook・トンネルを作らない(受信口をインターネットに開けない) |
26
+ | 入力語彙 | **閉じた語彙のみ**。`callback_data` が既定の集合に一致するものだけ処理し、自由文は処理しない |
27
+ | チャネル | Telegram のみ(v0)。実装は `packages/chat-bridge/` に置き、後で LINE / Discord を足せる形にする |
28
+
29
+ ## 2. なぜ自由文を入れないか(この契約の中心)
30
+
31
+ 自由文をエージェントへ通すと、`planning`(非公開)の信頼境界契約が扱う prompt injection の
32
+ 窓口がそのまま開く。ボタンの `callback_data` は**発行側が定義した有限集合**であり、
33
+ 受信側は集合に無い値を捨てるだけでよい。v0 はこの性質を設計の土台にする。
34
+
35
+ - 差し戻しは「確定しない + レポートを開くリンクを返す」で表現する。理由の自由記述はレポート側で行う
36
+ - 自由文メッセージを受けたら、処理せず「レポートで操作してください」と定型文を返す
37
+
38
+ ## 3. 安全規律(ハードルール)
39
+
40
+ 1. **トークンを git 管理下・レポート・ログ・会話に出さない。** 置き場は
41
+ `~/.config/akari-video/credentials.env`(`600`)のみ。エージェントは値を読まず、KEY 名だけ案内する
42
+ 2. **チャット ID の許可リストを必須にする。** 登録済み chat ID 以外からの update は
43
+ 一切処理せず破棄する(bot のユーザー名は誰でも到達できるため、これが無いと第三者が承認できる)
44
+ 3. **ブリッジはポートを listen しない。** 送信も受信も outbound の long polling のみ
45
+ 4. **`decisions.json` を直接書かない**(§1 の決定)。report-helper 経由のみ
46
+ 5. **update の重複処理をしない。** `update_id` で冪等化する(Telegram は再配信しうる)
47
+ 6. **送ってよいのはレポートの画像とテキストのみ。** 撮影素材の原本・secrets・パスの内部構造を送らない
48
+ 7. **疎通確認(実機への通知到達 + ボタンで `decisions.json` 更新)が取れるまで完了と言わない**
49
+
50
+ ## 4. 構成要素
51
+
52
+ | 要素 | 置き場 | 役割 |
53
+ |---|---|---|
54
+ | ブリッジ本体 | `packages/chat-bridge/telegram.mjs` | 通知送信 + long polling + report-helper API 呼び出し |
55
+ | セットアップスキル | `skills/setup-chat-approval/` | doctor → BotFather 案内(人間手番)→ chat ID 取得 → 疎通確認。`setup-remote` と同じ型 |
56
+ | 接続登録 | `.akari/connections.json` | `manage-connections` の管轄(§5 の判断待ち) |
57
+
58
+ ### 送るメッセージの形
59
+
60
+ ```
61
+ 🎬 <プロジェクト名> — 承認をお願いします
62
+ <要約 1〜2 行>
63
+ [キーフレーム画像 数枚]
64
+
65
+ [ レポートを開く (URL) ] [ おまかせで確定 ] [ あとで ]
66
+ ```
67
+
68
+ - 「レポートを開く」は URL ボタン(tailnet 限定 URL。Telegram のサーバーからは到達できないため
69
+ リンクプレビューは出ない → `disable_web_page_preview` を付ける)
70
+ - 「おまかせで確定」は全カード既定値のまま `POST /api/commit`(レポート側の `accept-all` と同義)
71
+ - v1 の拡張余地: カードごとに選択肢ボタンを展開する(`data-option` は有限集合なので §2 と両立する)
72
+
73
+ ## 5. 判断待ち — `connections.json` の `kind` 拡張
74
+
75
+ 現行スキーマの `kind` は `genai / image / video / tts / music / sns / analytics` の閉じた enum で、
76
+ **通知系の枠が無い**。`manage-connections` のハードルール 7(レジストリに無い接続を使わない)を
77
+ 守るには、いずれかを選ぶ必要がある:
78
+
79
+ | 案 | 内容 | 評価 |
80
+ |---|---|---|
81
+ | **A. `notify` を enum に追加** | スキーマ + 検証 + 例 + `apps/shell` 側ミラーを更新 | **推奨**。通知は既存のどの kind とも性質が違う(発信ではなく往復)。将来の LINE / Discord も同じ枠に入る |
82
+ | B. 既存の `sns` を流用 | 変更ゼロ | `sns` は「SNS へ投稿する」枠であり、承認の往復とは別物。意味が濁る |
83
+ | C. レジストリに載せない | credentials.env だけで完結 | ハードルール 7 違反。採らない |
84
+
85
+ ## 6. 成果物と受け入れ基準
86
+
87
+ - **L0**: スキル lint / スキーマ検証 / 既存テストが green
88
+ - **L1**: 決定論の単体テスト — 許可外 chat ID の破棄・未知の `callback_data` の破棄・
89
+ `update_id` の冪等化・自由文の非処理・トークンが出力に混じらないこと
90
+ - **L2**: 実機 — スマホへ通知が届き、ボタンのタップで `decisions.json` が更新される
91
+
92
+ ## 7. 将来(非スコープの明示)
93
+
94
+ - カードごとの選択肢ボタン展開(v1)
95
+ - 複数チャネル(LINE / Discord / Slack)
96
+ - チャットからのエージェント起動・自由文指示(信頼境界の別契約が前提。常駐エージェント
97
+ = Hermes 等の検討もここに属する)
98
+ - 承認待ちのエージェント側の再開機構(現状は人間が「続けて」と言う前提。ファイル待ちの
99
+ ポーリングを挟む設計は別途)
@@ -0,0 +1,59 @@
1
+ # Color range normalization v0
2
+
3
+ ## 1. 背景と目的
4
+
5
+ full-range(`color_range=pc` / `yuvj420p`)の入力をそのまま H.264 出力へ伝播させると、配信向けの limited range を期待する再生・検証環境で階調とメタデータが一致しない。本契約は Render の全エンコード工程を limited range(FFmpeg の `tv`)へ正規化し、最終成果物と中間生成物の一貫性を保証する。
6
+
7
+ この不具合の起点と再現条件は [公開 issue #21](https://github.com/AkariLabs/akari-video/issues/21) を参照する。
8
+
9
+ ## 2. 最終出力の保証
10
+
11
+ MP4 / H.264 の最終映像ストリームは次を満たさなければならない。
12
+
13
+ - pixel format は `yuv420p`
14
+ - `color_range` は `tv`(limited range)
15
+
16
+ この組み合わせを AKARI Video の配信標準出力とする。
17
+
18
+ ## 3. 値変換とメタデータの不変条件
19
+
20
+ 色域レンジの正規化は、画素値の変換と映像ストリームへのメタデータのタグ付けを常に対で行う。
21
+
22
+ - 各 video filter chain は出力直前に `scale=out_range=tv` 相当の値変換を行う。
23
+ - 各 H.264 encode は `-color_range tv` 相当のメタデータを付ける。
24
+ - full-range の画素値を残したまま `tv` タグだけを付けることを禁止する。
25
+ - 値だけを limited range へ変換し、タグ付けを省略することも禁止する。
26
+
27
+ 入力がすでに tv range の場合、`scale=out_range=tv` はレンジ変換について no-op として扱う。
28
+
29
+ ## 4. 工程不変
30
+
31
+ cut、tail padding、track stack、layers、overlay composite を含む各エンコード工程の出力フレームは常に tv range とする。これは最終成果物だけでなく、後続工程へ渡す中間生成物にも適用する。
32
+
33
+ 複数ソースの cut では、ソースごとの前処理チェーンで tv range へ正規化してから concat / transition へ入力する。これにより pc / tv range が混在したフレームを同じ concat へ直接渡さない。さらに、LUT や合成後の工程出力も終端で tv range に正規化する。
34
+
35
+ 映像を再エンコードしない audio-only mux と、alpha を運ぶ非 H.264 overlay 中間生成物は本契約の対象外とする。
36
+
37
+ ## 5. Probe と provenance
38
+
39
+ 入力ソースの `pix_fmt` と `color_range` を ffprobe の映像ストリームから取得し、既存の duration、audio 有無、width、height、fps と同じ provenance 情報として記録する。ffprobe が入力の `color_range` を報告しない場合は `null` として記録し、推測値で置き換えない。
40
+
41
+ ## 6. Verify 契約
42
+
43
+ レンダープランの期待値へ `color_range: "tv"` を追加し、official verify は `verify.color-range` を報告する。
44
+
45
+ - 実測 `color_range` が `pc` の場合は error とする。
46
+ - 実測が `tv` の場合は pass とする。
47
+ - ffprobe が `color_range` を報告しない場合は、H.264 の仕様既定(未指定は limited range)に従って tv とみなし pass とする。
48
+
49
+ `verify.pixel-format` の期待値 `yuv420p` は維持し、`verify.color-range` と独立に判定する。
50
+
51
+ ## 7. 予約(今回のスコープ外)
52
+
53
+ 次の事項は別契約で扱い、本契約 v0 では実装しない。
54
+
55
+ - BT.601 から BT.709 などの colorspace 変換
56
+ - `-colorspace` / `-color_primaries` / `-color_trc` による colorspace メタデータの正規化
57
+ - 10bit および HDR 入力の変換、tone mapping、出力形式
58
+
59
+ 本契約の `tv` 正規化は color range のみに限定され、上記の色域・伝達特性変換を暗黙に保証しない。