thth 5.2.0__tar.gz → 5.4.0__tar.gz

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 (831) hide show
  1. {thth-5.2.0 → thth-5.4.0}/PKG-INFO +2 -2
  2. {thth-5.2.0 → thth-5.4.0}/README.md +1 -1
  3. thth-5.4.0/accounts.example/instagram.json +30 -0
  4. {thth-5.2.0 → thth-5.4.0}/mcp/server.py +4 -1
  5. {thth-5.2.0 → thth-5.4.0}/skills/thth/SKILL.md +19 -0
  6. {thth-5.2.0 → thth-5.4.0}/skills/thth/dashboard/all.html +3 -3
  7. {thth-5.2.0 → thth-5.4.0}/skills/thth/dashboard/campaign.html +3 -3
  8. {thth-5.2.0 → thth-5.4.0}/skills/thth/dashboard/project.html +3 -3
  9. {thth-5.2.0 → thth-5.4.0}/skills/thth/dashboard/series.html +3 -3
  10. {thth-5.2.0 → thth-5.4.0}/tests/conftest.py +17 -0
  11. {thth-5.2.0 → thth-5.4.0}/tests/test_adapter_boundary.py +4 -2
  12. {thth-5.2.0 → thth-5.4.0}/tests/test_facebook_adapter.py +3 -1
  13. {thth-5.2.0 → thth-5.4.0}/tests/test_limits_stage2.py +3 -3
  14. {thth-5.2.0 → thth-5.4.0}/tests/test_limits_stage3.py +1 -1
  15. {thth-5.2.0 → thth-5.4.0}/tests/test_media_registry.py +16 -4
  16. thth-5.4.0/tests/test_meta_stage2.py +453 -0
  17. thth-5.4.0/tests/test_meta_stage3.py +611 -0
  18. thth-5.4.0/tests/test_meta_stage4.py +592 -0
  19. thth-5.4.0/tests/test_meta_stage6.py +438 -0
  20. {thth-5.2.0 → thth-5.4.0}/tests/test_v210_json_contract.py +8 -0
  21. thth-5.4.0/thth/VERSION +1 -0
  22. {thth-5.2.0 → thth-5.4.0}/thth/account_cli.py +29 -3
  23. {thth-5.2.0 → thth-5.4.0}/thth/accounts.py +1 -1
  24. {thth-5.2.0 → thth-5.4.0}/thth/adapters/__init__.py +4 -1
  25. {thth-5.2.0 → thth-5.4.0}/thth/adapters/auth_facebook.py +41 -15
  26. thth-5.4.0/thth/adapters/auth_instagram.py +83 -0
  27. {thth-5.2.0 → thth-5.4.0}/thth/adapters/base.py +3 -0
  28. {thth-5.2.0 → thth-5.4.0}/thth/adapters/facebook.py +268 -120
  29. thth-5.4.0/thth/adapters/instagram.py +783 -0
  30. thth-5.4.0/thth/adapters/instagram_media.py +307 -0
  31. {thth-5.2.0 → thth-5.4.0}/thth/admin_log.py +5 -1
  32. thth-5.4.0/thth/ads/__init__.py +18 -0
  33. thth-5.4.0/thth/ads/auth.py +366 -0
  34. thth-5.4.0/thth/ads/cli.py +421 -0
  35. thth-5.4.0/thth/ads/graphread.py +269 -0
  36. thth-5.4.0/thth/ads/ledger.py +366 -0
  37. thth-5.4.0/thth/ads/watch.py +355 -0
  38. {thth-5.2.0 → thth-5.4.0}/thth/authflow.py +3 -1
  39. {thth-5.2.0 → thth-5.4.0}/thth/cli.py +11 -0
  40. {thth-5.2.0 → thth-5.4.0}/thth/collect.py +69 -1
  41. {thth-5.2.0 → thth-5.4.0}/thth/core.py +29 -0
  42. {thth-5.2.0 → thth-5.4.0}/thth/dashboard_cli.py +24 -2
  43. {thth-5.2.0 → thth-5.4.0}/thth/edit_cli.py +74 -0
  44. {thth-5.2.0 → thth-5.4.0}/thth/events_cli.py +52 -1
  45. {thth-5.2.0 → thth-5.4.0}/thth/leave.py +1 -1
  46. {thth-5.2.0 → thth-5.4.0}/thth/leave_gate.py +7 -3
  47. {thth-5.2.0 → thth-5.4.0}/thth/limits_config.py +1 -1
  48. {thth-5.2.0 → thth-5.4.0}/thth/limits_policy.py +2 -2
  49. {thth-5.2.0 → thth-5.4.0}/thth/lint.py +2 -1
  50. {thth-5.2.0 → thth-5.4.0}/thth/maintain.py +76 -3
  51. {thth-5.2.0 → thth-5.4.0}/thth/measured.py +4 -0
  52. {thth-5.2.0 → thth-5.4.0}/thth/media.py +5 -2
  53. {thth-5.2.0 → thth-5.4.0}/thth/media_capabilities.py +32 -1
  54. {thth-5.2.0 → thth-5.4.0}/thth/media_delivery.py +17 -5
  55. {thth-5.2.0 → thth-5.4.0}/thth/media_registry.py +15 -0
  56. thth-5.4.0/thth/media_traits/instagram.json +145 -0
  57. {thth-5.2.0 → thth-5.4.0}/thth/media_traits.py +1 -1
  58. thth-5.4.0/thth/meta/__init__.py +16 -0
  59. thth-5.4.0/thth/meta/errors.py +118 -0
  60. thth-5.4.0/thth/meta/graph.py +190 -0
  61. thth-5.4.0/thth/meta/media.py +31 -0
  62. thth-5.4.0/thth/meta/tokens.py +74 -0
  63. thth-5.4.0/thth/meta/usage.py +237 -0
  64. {thth-5.2.0 → thth-5.4.0}/thth/morning.py +35 -2
  65. {thth-5.2.0 → thth-5.4.0}/thth/oauth.py +4 -1
  66. {thth-5.2.0 → thth-5.4.0}/thth/queuefile.py +3 -1
  67. thth-5.4.0/thth/quota_cli.py +102 -0
  68. {thth-5.2.0 → thth-5.4.0}/thth/relay.py +5 -1
  69. {thth-5.2.0 → thth-5.4.0}/thth/scenario.py +2 -2
  70. {thth-5.2.0 → thth-5.4.0}/thth/thread_read.py +4 -0
  71. thth-5.4.0/thth/webhook.py +508 -0
  72. {thth-5.2.0 → thth-5.4.0}/thth/worker.py +5 -0
  73. thth-5.2.0/thth/VERSION +0 -1
  74. {thth-5.2.0 → thth-5.4.0}/.gitignore +0 -0
  75. {thth-5.2.0 → thth-5.4.0}/LICENSE +0 -0
  76. {thth-5.2.0 → thth-5.4.0}/accounts.example/bluesky.json +0 -0
  77. {thth-5.2.0 → thth-5.4.0}/accounts.example/facebook.json +0 -0
  78. {thth-5.2.0 → thth-5.4.0}/accounts.example/hatenablog.json +0 -0
  79. {thth-5.2.0 → thth-5.4.0}/accounts.example/mastodon.json +0 -0
  80. {thth-5.2.0 → thth-5.4.0}/accounts.example/principal-hatena.json +0 -0
  81. {thth-5.2.0 → thth-5.4.0}/accounts.example/threads.json +0 -0
  82. {thth-5.2.0 → thth-5.4.0}/accounts.example/x.json +0 -0
  83. {thth-5.2.0 → thth-5.4.0}/bin/thth +0 -0
  84. {thth-5.2.0 → thth-5.4.0}/bin/thth-remote +0 -0
  85. {thth-5.2.0 → thth-5.4.0}/bin/thth-run +0 -0
  86. {thth-5.2.0 → thth-5.4.0}/pyproject.toml +0 -0
  87. {thth-5.2.0 → thth-5.4.0}/skills/thth/dashboard/README.md +0 -0
  88. {thth-5.2.0 → thth-5.4.0}/skills/thth-admin/SKILL.md +0 -0
  89. {thth-5.2.0 → thth-5.4.0}/systemd/thth@.service +0 -0
  90. {thth-5.2.0 → thth-5.4.0}/tests/__init__.py +0 -0
  91. {thth-5.2.0 → thth-5.4.0}/tests/coordination_snapshot.py +0 -0
  92. {thth-5.2.0 → thth-5.4.0}/tests/fixtures/GraphemeBreakTest-17.0.0.txt +0 -0
  93. {thth-5.2.0 → thth-5.4.0}/tests/fixtures/build_injection_eval.py +0 -0
  94. {thth-5.2.0 → thth-5.4.0}/tests/fixtures/callback/worker_responses.json +0 -0
  95. {thth-5.2.0 → thth-5.4.0}/tests/fixtures/injection_eval.json +0 -0
  96. {thth-5.2.0 → thth-5.4.0}/tests/fixtures/limits_stage3/README.txt +0 -0
  97. {thth-5.2.0 → thth-5.4.0}/tests/fixtures/limits_stage3/ledgers/vm-kopicha-hatenablog.json +0 -0
  98. {thth-5.2.0 → thth-5.4.0}/tests/fixtures/limits_stage3/ledgers/vm-kopicha-x.json +0 -0
  99. {thth-5.2.0 → thth-5.4.0}/tests/fixtures/limits_stage3/ledgers/vm-music-threads.json +0 -0
  100. {thth-5.2.0 → thth-5.4.0}/tests/fixtures/limits_stage3/principals/vm-kopicha.json +0 -0
  101. {thth-5.2.0 → thth-5.4.0}/tests/fixtures/scenario_v1_golden.json +0 -0
  102. {thth-5.2.0 → thth-5.4.0}/tests/fixtures/twitter_text_v3_weighted.json +0 -0
  103. {thth-5.2.0 → thth-5.4.0}/tests/fixtures/umami-bile.md +0 -0
  104. {thth-5.2.0 → thth-5.4.0}/tests/hatena_s1_flow_helpers.py +0 -0
  105. {thth-5.2.0 → thth-5.4.0}/tests/hatena_s1_helpers.py +0 -0
  106. {thth-5.2.0 → thth-5.4.0}/tests/hatena_s2_helpers.py +0 -0
  107. {thth-5.2.0 → thth-5.4.0}/tests/hatena_s2_http.py +0 -0
  108. {thth-5.2.0 → thth-5.4.0}/tests/hatena_s3_helpers.py +0 -0
  109. {thth-5.2.0 → thth-5.4.0}/tests/hatena_s4_helpers.py +0 -0
  110. {thth-5.2.0 → thth-5.4.0}/tests/hatena_s5_helpers.py +0 -0
  111. {thth-5.2.0 → thth-5.4.0}/tests/hatena_s6_helpers.py +0 -0
  112. {thth-5.2.0 → thth-5.4.0}/tests/helpers/__init__.py +0 -0
  113. {thth-5.2.0 → thth-5.4.0}/tests/helpers/fake_oauth_server.py +0 -0
  114. {thth-5.2.0 → thth-5.4.0}/tests/helpers/fake_push_adapter.py +0 -0
  115. {thth-5.2.0 → thth-5.4.0}/tests/helpers/fake_redirect_server.py +0 -0
  116. {thth-5.2.0 → thth-5.4.0}/tests/helpers/fake_search_adapter.py +0 -0
  117. {thth-5.2.0 → thth-5.4.0}/tests/helpers/fake_threads_reader.py +0 -0
  118. {thth-5.2.0 → thth-5.4.0}/tests/helpers/fake_threads_status.py +0 -0
  119. {thth-5.2.0 → thth-5.4.0}/tests/helpers/fake_threads_writes.py +0 -0
  120. {thth-5.2.0 → thth-5.4.0}/tests/helpers/fake_x.py +0 -0
  121. {thth-5.2.0 → thth-5.4.0}/tests/helpers/hold_lock.py +0 -0
  122. {thth-5.2.0 → thth-5.4.0}/tests/helpers/hold_repo_lock.py +0 -0
  123. {thth-5.2.0 → thth-5.4.0}/tests/helpers/worker_probe.mjs +0 -0
  124. {thth-5.2.0 → thth-5.4.0}/tests/test_27_acceptance_fixes.py +0 -0
  125. {thth-5.2.0 → thth-5.4.0}/tests/test_account_name_check.py +0 -0
  126. {thth-5.2.0 → thth-5.4.0}/tests/test_account_report.py +0 -0
  127. {thth-5.2.0 → thth-5.4.0}/tests/test_accounts_dir.py +0 -0
  128. {thth-5.2.0 → thth-5.4.0}/tests/test_admin_http_mcp.py +0 -0
  129. {thth-5.2.0 → thth-5.4.0}/tests/test_admin_log.py +0 -0
  130. {thth-5.2.0 → thth-5.4.0}/tests/test_admin_notifications.py +0 -0
  131. {thth-5.2.0 → thth-5.4.0}/tests/test_admin_repairs.py +0 -0
  132. {thth-5.2.0 → thth-5.4.0}/tests/test_admin_report.py +0 -0
  133. {thth-5.2.0 → thth-5.4.0}/tests/test_after_cli.py +0 -0
  134. {thth-5.2.0 → thth-5.4.0}/tests/test_analytics_comparison.py +0 -0
  135. {thth-5.2.0 → thth-5.4.0}/tests/test_analytics_report.py +0 -0
  136. {thth-5.2.0 → thth-5.4.0}/tests/test_api_diagnostic.py +0 -0
  137. {thth-5.2.0 → thth-5.4.0}/tests/test_api_error_body.py +0 -0
  138. {thth-5.2.0 → thth-5.4.0}/tests/test_app_env_cli.py +0 -0
  139. {thth-5.2.0 → thth-5.4.0}/tests/test_approval.py +0 -0
  140. {thth-5.2.0 → thth-5.4.0}/tests/test_approve_cmd.py +0 -0
  141. {thth-5.2.0 → thth-5.4.0}/tests/test_ask.py +0 -0
  142. {thth-5.2.0 → thth-5.4.0}/tests/test_assess.py +0 -0
  143. {thth-5.2.0 → thth-5.4.0}/tests/test_atlas_fifth_review.py +0 -0
  144. {thth-5.2.0 → thth-5.4.0}/tests/test_atlas_fourth_review.py +0 -0
  145. {thth-5.2.0 → thth-5.4.0}/tests/test_atlas_review.py +0 -0
  146. {thth-5.2.0 → thth-5.4.0}/tests/test_atlas_second_review.py +0 -0
  147. {thth-5.2.0 → thth-5.4.0}/tests/test_atlas_third_review.py +0 -0
  148. {thth-5.2.0 → thth-5.4.0}/tests/test_audit1_fixes.py +0 -0
  149. {thth-5.2.0 → thth-5.4.0}/tests/test_audit2_fixes.py +0 -0
  150. {thth-5.2.0 → thth-5.4.0}/tests/test_audit_b_counterexamples.py +0 -0
  151. {thth-5.2.0 → thth-5.4.0}/tests/test_audit_b_m1_semantics.py +0 -0
  152. {thth-5.2.0 → thth-5.4.0}/tests/test_audit_b_n1_depth.py +0 -0
  153. {thth-5.2.0 → thth-5.4.0}/tests/test_audit_b_r1_closure.py +0 -0
  154. {thth-5.2.0 → thth-5.4.0}/tests/test_audit_v201.py +0 -0
  155. {thth-5.2.0 → thth-5.4.0}/tests/test_audit_v23_details.py +0 -0
  156. {thth-5.2.0 → thth-5.4.0}/tests/test_auth.py +0 -0
  157. {thth-5.2.0 → thth-5.4.0}/tests/test_auth_identity_required.py +0 -0
  158. {thth-5.2.0 → thth-5.4.0}/tests/test_b17_remote_reads.py +0 -0
  159. {thth-5.2.0 → thth-5.4.0}/tests/test_behind_remote.py +0 -0
  160. {thth-5.2.0 → thth-5.4.0}/tests/test_bluesky_adapter.py +0 -0
  161. {thth-5.2.0 → thth-5.4.0}/tests/test_bluesky_secret_scrub.py +0 -0
  162. {thth-5.2.0 → thth-5.4.0}/tests/test_board_needs_review.py +0 -0
  163. {thth-5.2.0 → thth-5.4.0}/tests/test_board_running.py +0 -0
  164. {thth-5.2.0 → thth-5.4.0}/tests/test_branch_replies.py +0 -0
  165. {thth-5.2.0 → thth-5.4.0}/tests/test_bundle.py +0 -0
  166. {thth-5.2.0 → thth-5.4.0}/tests/test_bundle_not_type_mismatch.py +0 -0
  167. {thth-5.2.0 → thth-5.4.0}/tests/test_charcount.py +0 -0
  168. {thth-5.2.0 → thth-5.4.0}/tests/test_cli_entrypoint.py +0 -0
  169. {thth-5.2.0 → thth-5.4.0}/tests/test_cli_help.py +0 -0
  170. {thth-5.2.0 → thth-5.4.0}/tests/test_cli_vm_path.py +0 -0
  171. {thth-5.2.0 → thth-5.4.0}/tests/test_collect.py +0 -0
  172. {thth-5.2.0 → thth-5.4.0}/tests/test_collect_engagements.py +0 -0
  173. {thth-5.2.0 → thth-5.4.0}/tests/test_collect_runs.py +0 -0
  174. {thth-5.2.0 → thth-5.4.0}/tests/test_collect_sent.py +0 -0
  175. {thth-5.2.0 → thth-5.4.0}/tests/test_comparability.py +0 -0
  176. {thth-5.2.0 → thth-5.4.0}/tests/test_control_chars_refused.py +0 -0
  177. {thth-5.2.0 → thth-5.4.0}/tests/test_conversation_paging.py +0 -0
  178. {thth-5.2.0 → thth-5.4.0}/tests/test_count_for.py +0 -0
  179. {thth-5.2.0 → thth-5.4.0}/tests/test_docs_links.py +0 -0
  180. {thth-5.2.0 → thth-5.4.0}/tests/test_doctor.py +0 -0
  181. {thth-5.2.0 → thth-5.4.0}/tests/test_doctor_all_permissions.py +0 -0
  182. {thth-5.2.0 → thth-5.4.0}/tests/test_doctor_next_step.py +0 -0
  183. {thth-5.2.0 → thth-5.4.0}/tests/test_duplicate_front_matter_keys.py +0 -0
  184. {thth-5.2.0 → thth-5.4.0}/tests/test_editorial_audit.py +0 -0
  185. {thth-5.2.0 → thth-5.4.0}/tests/test_editorial_n3_n4.py +0 -0
  186. {thth-5.2.0 → thth-5.4.0}/tests/test_engagements.py +0 -0
  187. {thth-5.2.0 → thth-5.4.0}/tests/test_external_acceptance.py +0 -0
  188. {thth-5.2.0 → thth-5.4.0}/tests/test_facebook_auth.py +0 -0
  189. {thth-5.2.0 → thth-5.4.0}/tests/test_facebook_stage1.py +0 -0
  190. {thth-5.2.0 → thth-5.4.0}/tests/test_fake_api.py +0 -0
  191. {thth-5.2.0 → thth-5.4.0}/tests/test_form_spec.py +0 -0
  192. {thth-5.2.0 → thth-5.4.0}/tests/test_form_spec_cli.py +0 -0
  193. {thth-5.2.0 → thth-5.4.0}/tests/test_forms.py +0 -0
  194. {thth-5.2.0 → thth-5.4.0}/tests/test_fresh_install.py +0 -0
  195. {thth-5.2.0 → thth-5.4.0}/tests/test_front_matter_injection.py +0 -0
  196. {thth-5.2.0 → thth-5.4.0}/tests/test_healthcheck_notifications.py +0 -0
  197. {thth-5.2.0 → thth-5.4.0}/tests/test_hypotheses.py +0 -0
  198. {thth-5.2.0 → thth-5.4.0}/tests/test_hypothesis_boundaries.py +0 -0
  199. {thth-5.2.0 → thth-5.4.0}/tests/test_hypothesis_units.py +0 -0
  200. {thth-5.2.0 → thth-5.4.0}/tests/test_impact_cli.py +0 -0
  201. {thth-5.2.0 → thth-5.4.0}/tests/test_inbox_granted_scopes.py +0 -0
  202. {thth-5.2.0 → thth-5.4.0}/tests/test_incident_notifications.py +0 -0
  203. {thth-5.2.0 → thth-5.4.0}/tests/test_independent_thread_review.py +0 -0
  204. {thth-5.2.0 → thth-5.4.0}/tests/test_independent_topic_review.py +0 -0
  205. {thth-5.2.0 → thth-5.4.0}/tests/test_independent_topic_review_r2.py +0 -0
  206. {thth-5.2.0 → thth-5.4.0}/tests/test_inflight.py +0 -0
  207. {thth-5.2.0 → thth-5.4.0}/tests/test_inflight_tag_body_hash.py +0 -0
  208. {thth-5.2.0 → thth-5.4.0}/tests/test_jst.py +0 -0
  209. {thth-5.2.0 → thth-5.4.0}/tests/test_limits_policy.py +0 -0
  210. {thth-5.2.0 → thth-5.4.0}/tests/test_limits_stage4.py +0 -0
  211. {thth-5.2.0 → thth-5.4.0}/tests/test_lint.py +0 -0
  212. {thth-5.2.0 → thth-5.4.0}/tests/test_list_projections.py +0 -0
  213. {thth-5.2.0 → thth-5.4.0}/tests/test_llm_trial.py +0 -0
  214. {thth-5.2.0 → thth-5.4.0}/tests/test_llm_trial_engage.py +0 -0
  215. {thth-5.2.0 → thth-5.4.0}/tests/test_lock.py +0 -0
  216. {thth-5.2.0 → thth-5.4.0}/tests/test_m2_independent.py +0 -0
  217. {thth-5.2.0 → thth-5.4.0}/tests/test_maintain.py +0 -0
  218. {thth-5.2.0 → thth-5.4.0}/tests/test_mastodon_adapter.py +0 -0
  219. {thth-5.2.0 → thth-5.4.0}/tests/test_mastodon_visibility.py +0 -0
  220. {thth-5.2.0 → thth-5.4.0}/tests/test_max_per_run.py +0 -0
  221. {thth-5.2.0 → thth-5.4.0}/tests/test_mcp.py +0 -0
  222. {thth-5.2.0 → thth-5.4.0}/tests/test_mcp_robustness.py +0 -0
  223. {thth-5.2.0 → thth-5.4.0}/tests/test_measured.py +0 -0
  224. {thth-5.2.0 → thth-5.4.0}/tests/test_media_accounts.py +0 -0
  225. {thth-5.2.0 → thth-5.4.0}/tests/test_media_next_steps.py +0 -0
  226. {thth-5.2.0 → thth-5.4.0}/tests/test_media_roundtrip.py +0 -0
  227. {thth-5.2.0 → thth-5.4.0}/tests/test_media_traits.py +0 -0
  228. {thth-5.2.0 → thth-5.4.0}/tests/test_meta_stage0.py +0 -0
  229. {thth-5.2.0 → thth-5.4.0}/tests/test_mismatch_diagnostics.py +0 -0
  230. {thth-5.2.0 → thth-5.4.0}/tests/test_n5_reply_null_element.py +0 -0
  231. {thth-5.2.0 → thth-5.4.0}/tests/test_n6_replies_incomplete_ledger.py +0 -0
  232. {thth-5.2.0 → thth-5.4.0}/tests/test_n7_doctor_error_body.py +0 -0
  233. {thth-5.2.0 → thth-5.4.0}/tests/test_no_limits.py +0 -0
  234. {thth-5.2.0 → thth-5.4.0}/tests/test_observation_provenance.py +0 -0
  235. {thth-5.2.0 → thth-5.4.0}/tests/test_operations_handoff.py +0 -0
  236. {thth-5.2.0 → thth-5.4.0}/tests/test_packaging.py +0 -0
  237. {thth-5.2.0 → thth-5.4.0}/tests/test_preview.py +0 -0
  238. {thth-5.2.0 → thth-5.4.0}/tests/test_production_mode.py +0 -0
  239. {thth-5.2.0 → thth-5.4.0}/tests/test_pull_cli.py +0 -0
  240. {thth-5.2.0 → thth-5.4.0}/tests/test_pulse.py +0 -0
  241. {thth-5.2.0 → thth-5.4.0}/tests/test_queue.py +0 -0
  242. {thth-5.2.0 → thth-5.4.0}/tests/test_quiet_hours.py +0 -0
  243. {thth-5.2.0 → thth-5.4.0}/tests/test_r2_r4_r5.py +0 -0
  244. {thth-5.2.0 → thth-5.4.0}/tests/test_read_cli_bare_runtimeerror.py +0 -0
  245. {thth-5.2.0 → thth-5.4.0}/tests/test_reason_adoption.py +0 -0
  246. {thth-5.2.0 → thth-5.4.0}/tests/test_redact.py +0 -0
  247. {thth-5.2.0 → thth-5.4.0}/tests/test_redirect_token_leak.py +0 -0
  248. {thth-5.2.0 → thth-5.4.0}/tests/test_refresh.py +0 -0
  249. {thth-5.2.0 → thth-5.4.0}/tests/test_refresh_destination.py +0 -0
  250. {thth-5.2.0 → thth-5.4.0}/tests/test_rejudge_new.py +0 -0
  251. {thth-5.2.0 → thth-5.4.0}/tests/test_rejudge_r2_r3.py +0 -0
  252. {thth-5.2.0 → thth-5.4.0}/tests/test_replies.py +0 -0
  253. {thth-5.2.0 → thth-5.4.0}/tests/test_replies_refresh.py +0 -0
  254. {thth-5.2.0 → thth-5.4.0}/tests/test_repo_lock.py +0 -0
  255. {thth-5.2.0 → thth-5.4.0}/tests/test_repo_sync.py +0 -0
  256. {thth-5.2.0 → thth-5.4.0}/tests/test_report_checkout_import.py +0 -0
  257. {thth-5.2.0 → thth-5.4.0}/tests/test_report_http.py +0 -0
  258. {thth-5.2.0 → thth-5.4.0}/tests/test_report_isolation.py +0 -0
  259. {thth-5.2.0 → thth-5.4.0}/tests/test_report_service.py +0 -0
  260. {thth-5.2.0 → thth-5.4.0}/tests/test_retract_already_gone.py +0 -0
  261. {thth-5.2.0 → thth-5.4.0}/tests/test_reverse_audit.py +0 -0
  262. {thth-5.2.0 → thth-5.4.0}/tests/test_review_case_id_cluster_m2.py +0 -0
  263. {thth-5.2.0 → thth-5.4.0}/tests/test_review_case_id_cluster_r2.py +0 -0
  264. {thth-5.2.0 → thth-5.4.0}/tests/test_review_cli.py +0 -0
  265. {thth-5.2.0 → thth-5.4.0}/tests/test_review_evidence_account.py +0 -0
  266. {thth-5.2.0 → thth-5.4.0}/tests/test_review_form_spec_and_case_id.py +0 -0
  267. {thth-5.2.0 → thth-5.4.0}/tests/test_review_provenance.py +0 -0
  268. {thth-5.2.0 → thth-5.4.0}/tests/test_review_records.py +0 -0
  269. {thth-5.2.0 → thth-5.4.0}/tests/test_revoke.py +0 -0
  270. {thth-5.2.0 → thth-5.4.0}/tests/test_roundtrip.py +0 -0
  271. {thth-5.2.0 → thth-5.4.0}/tests/test_run_missing_env.py +0 -0
  272. {thth-5.2.0 → thth-5.4.0}/tests/test_run_record_failclosed.py +0 -0
  273. {thth-5.2.0 → thth-5.4.0}/tests/test_runs_collected.py +0 -0
  274. {thth-5.2.0 → thth-5.4.0}/tests/test_runs_read_rows.py +0 -0
  275. {thth-5.2.0 → thth-5.4.0}/tests/test_schedule_multiple.py +0 -0
  276. {thth-5.2.0 → thth-5.4.0}/tests/test_schedule_view.py +0 -0
  277. {thth-5.2.0 → thth-5.4.0}/tests/test_scopes_source_requested.py +0 -0
  278. {thth-5.2.0 → thth-5.4.0}/tests/test_search_engage_targets.py +0 -0
  279. {thth-5.2.0 → thth-5.4.0}/tests/test_search_media_routes.py +0 -0
  280. {thth-5.2.0 → thth-5.4.0}/tests/test_secret_value_redact.py +0 -0
  281. {thth-5.2.0 → thth-5.4.0}/tests/test_secrets_grep.py +0 -0
  282. {thth-5.2.0 → thth-5.4.0}/tests/test_security_small_fixes.py +0 -0
  283. {thth-5.2.0 → thth-5.4.0}/tests/test_select.py +0 -0
  284. {thth-5.2.0 → thth-5.4.0}/tests/test_selfupdate.py +0 -0
  285. {thth-5.2.0 → thth-5.4.0}/tests/test_send.py +0 -0
  286. {thth-5.2.0 → thth-5.4.0}/tests/test_send_cli.py +0 -0
  287. {thth-5.2.0 → thth-5.4.0}/tests/test_send_control_chars.py +0 -0
  288. {thth-5.2.0 → thth-5.4.0}/tests/test_send_guard_cli.py +0 -0
  289. {thth-5.2.0 → thth-5.4.0}/tests/test_send_media.py +0 -0
  290. {thth-5.2.0 → thth-5.4.0}/tests/test_send_visibility.py +0 -0
  291. {thth-5.2.0 → thth-5.4.0}/tests/test_sent_integrity.py +0 -0
  292. {thth-5.2.0 → thth-5.4.0}/tests/test_server_json.py +0 -0
  293. {thth-5.2.0 → thth-5.4.0}/tests/test_site.py +0 -0
  294. {thth-5.2.0 → thth-5.4.0}/tests/test_skill_doc.py +0 -0
  295. {thth-5.2.0 → thth-5.4.0}/tests/test_study_report.py +0 -0
  296. {thth-5.2.0 → thth-5.4.0}/tests/test_systemd.py +0 -0
  297. {thth-5.2.0 → thth-5.4.0}/tests/test_thread_publish.py +0 -0
  298. {thth-5.2.0 → thth-5.4.0}/tests/test_thread_read.py +0 -0
  299. {thth-5.2.0 → thth-5.4.0}/tests/test_threadrun.py +0 -0
  300. {thth-5.2.0 → thth-5.4.0}/tests/test_threads_read_permissions.py +0 -0
  301. {thth-5.2.0 → thth-5.4.0}/tests/test_threadshape.py +0 -0
  302. {thth-5.2.0 → thth-5.4.0}/tests/test_thth_root.py +0 -0
  303. {thth-5.2.0 → thth-5.4.0}/tests/test_token_exists.py +0 -0
  304. {thth-5.2.0 → thth-5.4.0}/tests/test_token_scopes_record.py +0 -0
  305. {thth-5.2.0 → thth-5.4.0}/tests/test_token_set.py +0 -0
  306. {thth-5.2.0 → thth-5.4.0}/tests/test_topic.py +0 -0
  307. {thth-5.2.0 → thth-5.4.0}/tests/test_topic_advice.py +0 -0
  308. {thth-5.2.0 → thth-5.4.0}/tests/test_topic_cli.py +0 -0
  309. {thth-5.2.0 → thth-5.4.0}/tests/test_topic_models.py +0 -0
  310. {thth-5.2.0 → thth-5.4.0}/tests/test_topic_review_fixes.py +0 -0
  311. {thth-5.2.0 → thth-5.4.0}/tests/test_topic_shelf_observers.py +0 -0
  312. {thth-5.2.0 → thth-5.4.0}/tests/test_topic_store.py +0 -0
  313. {thth-5.2.0 → thth-5.4.0}/tests/test_topics_record_concurrent.py +0 -0
  314. {thth-5.2.0 → thth-5.4.0}/tests/test_topics_tool.py +0 -0
  315. {thth-5.2.0 → thth-5.4.0}/tests/test_trial_frictions.py +0 -0
  316. {thth-5.2.0 → thth-5.4.0}/tests/test_v1015_path_semantics.py +0 -0
  317. {thth-5.2.0 → thth-5.4.0}/tests/test_v1015_take_what_we_can.py +0 -0
  318. {thth-5.2.0 → thth-5.4.0}/tests/test_v210_admin_transaction.py +0 -0
  319. {thth-5.2.0 → thth-5.4.0}/tests/test_v210_approve_account.py +0 -0
  320. {thth-5.2.0 → thth-5.4.0}/tests/test_v210_length.py +0 -0
  321. {thth-5.2.0 → thth-5.4.0}/tests/test_v210_lock_wait.py +0 -0
  322. {thth-5.2.0 → thth-5.4.0}/tests/test_v210_mastodon_scopes.py +0 -0
  323. {thth-5.2.0 → thth-5.4.0}/tests/test_v210_mentions.py +0 -0
  324. {thth-5.2.0 → thth-5.4.0}/tests/test_v210_post_urls.py +0 -0
  325. {thth-5.2.0 → thth-5.4.0}/tests/test_v210_repo_paths.py +0 -0
  326. {thth-5.2.0 → thth-5.4.0}/tests/test_v210_reposts.py +0 -0
  327. {thth-5.2.0 → thth-5.4.0}/tests/test_v210_study_add.py +0 -0
  328. {thth-5.2.0 → thth-5.4.0}/tests/test_v210_tool_version.py +0 -0
  329. {thth-5.2.0 → thth-5.4.0}/tests/test_v210_unanswered.py +0 -0
  330. {thth-5.2.0 → thth-5.4.0}/tests/test_v210_unanswered_scope.py +0 -0
  331. {thth-5.2.0 → thth-5.4.0}/tests/test_v210_where_arguments.py +0 -0
  332. {thth-5.2.0 → thth-5.4.0}/tests/test_v210_where_filters.py +0 -0
  333. {thth-5.2.0 → thth-5.4.0}/tests/test_v210_who_arguments.py +0 -0
  334. {thth-5.2.0 → thth-5.4.0}/tests/test_v211_auth_integration.py +0 -0
  335. {thth-5.2.0 → thth-5.4.0}/tests/test_v211_authflow.py +0 -0
  336. {thth-5.2.0 → thth-5.4.0}/tests/test_v211_bluesky_stdin.py +0 -0
  337. {thth-5.2.0 → thth-5.4.0}/tests/test_v211_doctor_binding.py +0 -0
  338. {thth-5.2.0 → thth-5.4.0}/tests/test_v211_mastodon_auth.py +0 -0
  339. {thth-5.2.0 → thth-5.4.0}/tests/test_v211_x_auth.py +0 -0
  340. {thth-5.2.0 → thth-5.4.0}/tests/test_v212_ask_coordination.py +0 -0
  341. {thth-5.2.0 → thth-5.4.0}/tests/test_v212_budget.py +0 -0
  342. {thth-5.2.0 → thth-5.4.0}/tests/test_v212_budget_auth.py +0 -0
  343. {thth-5.2.0 → thth-5.4.0}/tests/test_v212_budget_mcp.py +0 -0
  344. {thth-5.2.0 → thth-5.4.0}/tests/test_v212_deletion.py +0 -0
  345. {thth-5.2.0 → thth-5.4.0}/tests/test_v212_leave.py +0 -0
  346. {thth-5.2.0 → thth-5.4.0}/tests/test_v212_leave_gate.py +0 -0
  347. {thth-5.2.0 → thth-5.4.0}/tests/test_v212_leave_providers.py +0 -0
  348. {thth-5.2.0 → thth-5.4.0}/tests/test_v212_leave_token_safety.py +0 -0
  349. {thth-5.2.0 → thth-5.4.0}/tests/test_v212_mastodon_guide.py +0 -0
  350. {thth-5.2.0 → thth-5.4.0}/tests/test_v212_mcp_compat.py +0 -0
  351. {thth-5.2.0 → thth-5.4.0}/tests/test_v212_missing_token.py +0 -0
  352. {thth-5.2.0 → thth-5.4.0}/tests/test_v212_read_coordination.py +0 -0
  353. {thth-5.2.0 → thth-5.4.0}/tests/test_v212_relay.py +0 -0
  354. {thth-5.2.0 → thth-5.4.0}/tests/test_v212_server_writes.py +0 -0
  355. {thth-5.2.0 → thth-5.4.0}/tests/test_v212_token_parent.py +0 -0
  356. {thth-5.2.0 → thth-5.4.0}/tests/test_v212_write_storage.py +0 -0
  357. {thth-5.2.0 → thth-5.4.0}/tests/test_v2131_send_dry_run.py +0 -0
  358. {thth-5.2.0 → thth-5.4.0}/tests/test_v2131_threads_publish_wait.py +0 -0
  359. {thth-5.2.0 → thth-5.4.0}/tests/test_v2132_send_inflight_hint.py +0 -0
  360. {thth-5.2.0 → thth-5.4.0}/tests/test_v2132_threads_create_timeout.py +0 -0
  361. {thth-5.2.0 → thth-5.4.0}/tests/test_v2132_threads_create_unknown.py +0 -0
  362. {thth-5.2.0 → thth-5.4.0}/tests/test_v213_acceptance_fixes.py +0 -0
  363. {thth-5.2.0 → thth-5.4.0}/tests/test_v213_adts_asf.py +0 -0
  364. {thth-5.2.0 → thth-5.4.0}/tests/test_v213_attachment_kinds.py +0 -0
  365. {thth-5.2.0 → thth-5.4.0}/tests/test_v213_bluesky_external.py +0 -0
  366. {thth-5.2.0 → thth-5.4.0}/tests/test_v213_bluesky_media.py +0 -0
  367. {thth-5.2.0 → thth-5.4.0}/tests/test_v213_bluesky_metadata.py +0 -0
  368. {thth-5.2.0 → thth-5.4.0}/tests/test_v213_bluesky_metadata_uri.py +0 -0
  369. {thth-5.2.0 → thth-5.4.0}/tests/test_v213_bluesky_metadata_video.py +0 -0
  370. {thth-5.2.0 → thth-5.4.0}/tests/test_v213_bluesky_record.py +0 -0
  371. {thth-5.2.0 → thth-5.4.0}/tests/test_v213_bluesky_record_video.py +0 -0
  372. {thth-5.2.0 → thth-5.4.0}/tests/test_v213_bluesky_video.py +0 -0
  373. {thth-5.2.0 → thth-5.4.0}/tests/test_v213_bluesky_webm_named.py +0 -0
  374. {thth-5.2.0 → thth-5.4.0}/tests/test_v213_bmff_typed_metadata.py +0 -0
  375. {thth-5.2.0 → thth-5.4.0}/tests/test_v213_budget_lock.py +0 -0
  376. {thth-5.2.0 → thth-5.4.0}/tests/test_v213_capabilities.py +0 -0
  377. {thth-5.2.0 → thth-5.4.0}/tests/test_v213_carryover_timeout.py +0 -0
  378. {thth-5.2.0 → thth-5.4.0}/tests/test_v213_graphemes.py +0 -0
  379. {thth-5.2.0 → thth-5.4.0}/tests/test_v213_mastodon_auth_media.py +0 -0
  380. {thth-5.2.0 → thth-5.4.0}/tests/test_v213_mastodon_bmff_audio.py +0 -0
  381. {thth-5.2.0 → thth-5.4.0}/tests/test_v213_mastodon_flac.py +0 -0
  382. {thth-5.2.0 → thth-5.4.0}/tests/test_v213_mastodon_gif.py +0 -0
  383. {thth-5.2.0 → thth-5.4.0}/tests/test_v213_mastodon_link.py +0 -0
  384. {thth-5.2.0 → thth-5.4.0}/tests/test_v213_mastodon_media.py +0 -0
  385. {thth-5.2.0 → thth-5.4.0}/tests/test_v213_mastodon_mp3.py +0 -0
  386. {thth-5.2.0 → thth-5.4.0}/tests/test_v213_mastodon_ogg_opus.py +0 -0
  387. {thth-5.2.0 → thth-5.4.0}/tests/test_v213_mastodon_ogg_vorbis.py +0 -0
  388. {thth-5.2.0 → thth-5.4.0}/tests/test_v213_mastodon_poll.py +0 -0
  389. {thth-5.2.0 → thth-5.4.0}/tests/test_v213_mastodon_primary_size_boundary.py +0 -0
  390. {thth-5.2.0 → thth-5.4.0}/tests/test_v213_mastodon_quote.py +0 -0
  391. {thth-5.2.0 → thth-5.4.0}/tests/test_v213_mastodon_thumbnail.py +0 -0
  392. {thth-5.2.0 → thth-5.4.0}/tests/test_v213_mastodon_wav.py +0 -0
  393. {thth-5.2.0 → thth-5.4.0}/tests/test_v213_mastodon_webm_audio.py +0 -0
  394. {thth-5.2.0 → thth-5.4.0}/tests/test_v213_mastodon_webm_video.py +0 -0
  395. {thth-5.2.0 → thth-5.4.0}/tests/test_v213_media_cleanup.py +0 -0
  396. {thth-5.2.0 → thth-5.4.0}/tests/test_v213_media_formats.py +0 -0
  397. {thth-5.2.0 → thth-5.4.0}/tests/test_v213_media_foundation.py +0 -0
  398. {thth-5.2.0 → thth-5.4.0}/tests/test_v213_media_prepared.py +0 -0
  399. {thth-5.2.0 → thth-5.4.0}/tests/test_v213_media_relay.py +0 -0
  400. {thth-5.2.0 → thth-5.4.0}/tests/test_v213_media_relay_refusals.py +0 -0
  401. {thth-5.2.0 → thth-5.4.0}/tests/test_v213_media_upload.py +0 -0
  402. {thth-5.2.0 → thth-5.4.0}/tests/test_v213_review_followups.py +0 -0
  403. {thth-5.2.0 → thth-5.4.0}/tests/test_v213_stage810_audit.py +0 -0
  404. {thth-5.2.0 → thth-5.4.0}/tests/test_v213_static_diagnostics.py +0 -0
  405. {thth-5.2.0 → thth-5.4.0}/tests/test_v213_stop_observation.py +0 -0
  406. {thth-5.2.0 → thth-5.4.0}/tests/test_v213_systemd_worker.py +0 -0
  407. {thth-5.2.0 → thth-5.4.0}/tests/test_v213_threads_ghost.py +0 -0
  408. {thth-5.2.0 → thth-5.4.0}/tests/test_v213_threads_gif.py +0 -0
  409. {thth-5.2.0 → thth-5.4.0}/tests/test_v213_threads_link.py +0 -0
  410. {thth-5.2.0 → thth-5.4.0}/tests/test_v213_threads_media.py +0 -0
  411. {thth-5.2.0 → thth-5.4.0}/tests/test_v213_threads_media_spoiler.py +0 -0
  412. {thth-5.2.0 → thth-5.4.0}/tests/test_v213_threads_poll.py +0 -0
  413. {thth-5.2.0 → thth-5.4.0}/tests/test_v213_threads_quote.py +0 -0
  414. {thth-5.2.0 → thth-5.4.0}/tests/test_v213_threads_quote_media.py +0 -0
  415. {thth-5.2.0 → thth-5.4.0}/tests/test_v213_threads_replies.py +0 -0
  416. {thth-5.2.0 → thth-5.4.0}/tests/test_v213_threads_text.py +0 -0
  417. {thth-5.2.0 → thth-5.4.0}/tests/test_v213_threads_text_spoiler.py +0 -0
  418. {thth-5.2.0 → thth-5.4.0}/tests/test_v213_threads_video.py +0 -0
  419. {thth-5.2.0 → thth-5.4.0}/tests/test_v213_transport_review.py +0 -0
  420. {thth-5.2.0 → thth-5.4.0}/tests/test_v213_video_typed_metadata.py +0 -0
  421. {thth-5.2.0 → thth-5.4.0}/tests/test_v213_vorbis_comment_boundary.py +0 -0
  422. {thth-5.2.0 → thth-5.4.0}/tests/test_v213_webm_schema_boundary.py +0 -0
  423. {thth-5.2.0 → thth-5.4.0}/tests/test_v2141_draft_lint_reason.py +0 -0
  424. {thth-5.2.0 → thth-5.4.0}/tests/test_v2141_managed_repo_umask.py +0 -0
  425. {thth-5.2.0 → thth-5.4.0}/tests/test_v214_release_signing.py +0 -0
  426. {thth-5.2.0 → thth-5.4.0}/tests/test_v214_x_adapter.py +0 -0
  427. {thth-5.2.0 → thth-5.4.0}/tests/test_v214_x_auth.py +0 -0
  428. {thth-5.2.0 → thth-5.4.0}/tests/test_v214_x_media.py +0 -0
  429. {thth-5.2.0 → thth-5.4.0}/tests/test_v214_x_posts_budget.py +0 -0
  430. {thth-5.2.0 → thth-5.4.0}/tests/test_v21b_approved_writes.py +0 -0
  431. {thth-5.2.0 → thth-5.4.0}/tests/test_v28_tags.py +0 -0
  432. {thth-5.2.0 → thth-5.4.0}/tests/test_v3100_docs.py +0 -0
  433. {thth-5.2.0 → thth-5.4.0}/tests/test_v3100_invite_admin_approval.py +0 -0
  434. {thth-5.2.0 → thth-5.4.0}/tests/test_v3100_invite_create.py +0 -0
  435. {thth-5.2.0 → thth-5.4.0}/tests/test_v3100_invite_credential.py +0 -0
  436. {thth-5.2.0 → thth-5.4.0}/tests/test_v3100_invite_limits.py +0 -0
  437. {thth-5.2.0 → thth-5.4.0}/tests/test_v3100_invite_worker.py +0 -0
  438. {thth-5.2.0 → thth-5.4.0}/tests/test_v310_morning.py +0 -0
  439. {thth-5.2.0 → thth-5.4.0}/tests/test_v310_search_timeout.py +0 -0
  440. {thth-5.2.0 → thth-5.4.0}/tests/test_v310_watch_words.py +0 -0
  441. {thth-5.2.0 → thth-5.4.0}/tests/test_v3111_observe_counts_only.py +0 -0
  442. {thth-5.2.0 → thth-5.4.0}/tests/test_v3111_observe_no_world.py +0 -0
  443. {thth-5.2.0 → thth-5.4.0}/tests/test_v311_leave_same_user.py +0 -0
  444. {thth-5.2.0 → thth-5.4.0}/tests/test_v311_mastodon_retract.py +0 -0
  445. {thth-5.2.0 → thth-5.4.0}/tests/test_v311_morning_overdue.py +0 -0
  446. {thth-5.2.0 → thth-5.4.0}/tests/test_v311_morning_own_exclusion.py +0 -0
  447. {thth-5.2.0 → thth-5.4.0}/tests/test_v311_morning_unanswered_window.py +0 -0
  448. {thth-5.2.0 → thth-5.4.0}/tests/test_v311_notifications_guidance.py +0 -0
  449. {thth-5.2.0 → thth-5.4.0}/tests/test_v311_replies_owned.py +0 -0
  450. {thth-5.2.0 → thth-5.4.0}/tests/test_v3120_activity.py +0 -0
  451. {thth-5.2.0 → thth-5.4.0}/tests/test_v3120_admin_write_reasons.py +0 -0
  452. {thth-5.2.0 → thth-5.4.0}/tests/test_v3120_guard.py +0 -0
  453. {thth-5.2.0 → thth-5.4.0}/tests/test_v3120_worker_version.py +0 -0
  454. {thth-5.2.0 → thth-5.4.0}/tests/test_v312_leave_shared_token.py +0 -0
  455. {thth-5.2.0 → thth-5.4.0}/tests/test_v312_own_handles_stopped.py +0 -0
  456. {thth-5.2.0 → thth-5.4.0}/tests/test_v312_report_admin.py +0 -0
  457. {thth-5.2.0 → thth-5.4.0}/tests/test_v312_report_channel.py +0 -0
  458. {thth-5.2.0 → thth-5.4.0}/tests/test_v312_report_inbox.py +0 -0
  459. {thth-5.2.0 → thth-5.4.0}/tests/test_v312_report_mcp_user.py +0 -0
  460. {thth-5.2.0 → thth-5.4.0}/tests/test_v3140_api_relay.py +0 -0
  461. {thth-5.2.0 → thth-5.4.0}/tests/test_v3140_invite_key.py +0 -0
  462. {thth-5.2.0 → thth-5.4.0}/tests/test_v3140_remote_cli.py +0 -0
  463. {thth-5.2.0 → thth-5.4.0}/tests/test_v3140_server_reads.py +0 -0
  464. {thth-5.2.0 → thth-5.4.0}/tests/test_v3142_browser_login.py +0 -0
  465. {thth-5.2.0 → thth-5.4.0}/tests/test_v3143_activity_push.py +0 -0
  466. {thth-5.2.0 → thth-5.4.0}/tests/test_v3143_cli_umask_leave.py +0 -0
  467. {thth-5.2.0 → thth-5.4.0}/tests/test_v3143_inflight_remote.py +0 -0
  468. {thth-5.2.0 → thth-5.4.0}/tests/test_v3143_managed_repo_recover.py +0 -0
  469. {thth-5.2.0 → thth-5.4.0}/tests/test_v315_reactions.py +0 -0
  470. {thth-5.2.0 → thth-5.4.0}/tests/test_v317_run_lock_wait.py +0 -0
  471. {thth-5.2.0 → thth-5.4.0}/tests/test_v3180_json_names.py +0 -0
  472. {thth-5.2.0 → thth-5.4.0}/tests/test_v3180_map_timer.py +0 -0
  473. {thth-5.2.0 → thth-5.4.0}/tests/test_v3180_where_filters.py +0 -0
  474. {thth-5.2.0 → thth-5.4.0}/tests/test_v3180_where_keep.py +0 -0
  475. {thth-5.2.0 → thth-5.4.0}/tests/test_v3192_publish_hold.py +0 -0
  476. {thth-5.2.0 → thth-5.4.0}/tests/test_v31_analysis.py +0 -0
  477. {thth-5.2.0 → thth-5.4.0}/tests/test_v31_collection.py +0 -0
  478. {thth-5.2.0 → thth-5.4.0}/tests/test_v3200_x_weighted_length.py +0 -0
  479. {thth-5.2.0 → thth-5.4.0}/tests/test_v320_reply_to_file_display.py +0 -0
  480. {thth-5.2.0 → thth-5.4.0}/tests/test_v320_reply_to_file_lint.py +0 -0
  481. {thth-5.2.0 → thth-5.4.0}/tests/test_v320_reply_to_file_resolve.py +0 -0
  482. {thth-5.2.0 → thth-5.4.0}/tests/test_v320_report_fixes.py +0 -0
  483. {thth-5.2.0 → thth-5.4.0}/tests/test_v3220_account_scheduled.py +0 -0
  484. {thth-5.2.0 → thth-5.4.0}/tests/test_v3220_bluesky_reply_control.py +0 -0
  485. {thth-5.2.0 → thth-5.4.0}/tests/test_v3220_bluesky_retract.py +0 -0
  486. {thth-5.2.0 → thth-5.4.0}/tests/test_v3220_invite_mastodon.py +0 -0
  487. {thth-5.2.0 → thth-5.4.0}/tests/test_v3220_mentions_inbox.py +0 -0
  488. {thth-5.2.0 → thth-5.4.0}/tests/test_v3250_burst_reply_queue.py +0 -0
  489. {thth-5.2.0 → thth-5.4.0}/tests/test_v330_fingerprint_version.py +0 -0
  490. {thth-5.2.0 → thth-5.4.0}/tests/test_v330_held.py +0 -0
  491. {thth-5.2.0 → thth-5.4.0}/tests/test_v330_held_names.py +0 -0
  492. {thth-5.2.0 → thth-5.4.0}/tests/test_v330_last_refusals.py +0 -0
  493. {thth-5.2.0 → thth-5.4.0}/tests/test_v330_notification_route.py +0 -0
  494. {thth-5.2.0 → thth-5.4.0}/tests/test_v330_observe.py +0 -0
  495. {thth-5.2.0 → thth-5.4.0}/tests/test_v330_report_friction.py +0 -0
  496. {thth-5.2.0 → thth-5.4.0}/tests/test_v330_report_words.py +0 -0
  497. {thth-5.2.0 → thth-5.4.0}/tests/test_v330_reports_effect.py +0 -0
  498. {thth-5.2.0 → thth-5.4.0}/tests/test_v331_inflight_cli.py +0 -0
  499. {thth-5.2.0 → thth-5.4.0}/tests/test_v331_listing_resolve.py +0 -0
  500. {thth-5.2.0 → thth-5.4.0}/tests/test_v331_outbox_retention.py +0 -0
  501. {thth-5.2.0 → thth-5.4.0}/tests/test_v331_publish_status.py +0 -0
  502. {thth-5.2.0 → thth-5.4.0}/tests/test_v331_self_resolve.py +0 -0
  503. {thth-5.2.0 → thth-5.4.0}/tests/test_v340_plaza_cli.py +0 -0
  504. {thth-5.2.0 → thth-5.4.0}/tests/test_v340_plaza_docs.py +0 -0
  505. {thth-5.2.0 → thth-5.4.0}/tests/test_v340_plaza_fields.py +0 -0
  506. {thth-5.2.0 → thth-5.4.0}/tests/test_v340_plaza_mcp.py +0 -0
  507. {thth-5.2.0 → thth-5.4.0}/tests/test_v340_plaza_morning.py +0 -0
  508. {thth-5.2.0 → thth-5.4.0}/tests/test_v340_plaza_observe.py +0 -0
  509. {thth-5.2.0 → thth-5.4.0}/tests/test_v340_plaza_open.py +0 -0
  510. {thth-5.2.0 → thth-5.4.0}/tests/test_v340_plaza_open_confirm.py +0 -0
  511. {thth-5.2.0 → thth-5.4.0}/tests/test_v340_plaza_reply.py +0 -0
  512. {thth-5.2.0 → thth-5.4.0}/tests/test_v340_plaza_store.py +0 -0
  513. {thth-5.2.0 → thth-5.4.0}/tests/test_v350_map_docs.py +0 -0
  514. {thth-5.2.0 → thth-5.4.0}/tests/test_v350_map_nodes.py +0 -0
  515. {thth-5.2.0 → thth-5.4.0}/tests/test_v350_map_observe.py +0 -0
  516. {thth-5.2.0 → thth-5.4.0}/tests/test_v350_map_plaza.py +0 -0
  517. {thth-5.2.0 → thth-5.4.0}/tests/test_v350_map_retention.py +0 -0
  518. {thth-5.2.0 → thth-5.4.0}/tests/test_v350_map_self.py +0 -0
  519. {thth-5.2.0 → thth-5.4.0}/tests/test_v350_map_show.py +0 -0
  520. {thth-5.2.0 → thth-5.4.0}/tests/test_v350_map_world.py +0 -0
  521. {thth-5.2.0 → thth-5.4.0}/tests/test_v351_leave_timers.py +0 -0
  522. {thth-5.2.0 → thth-5.4.0}/tests/test_v351_plaza_open_holes.py +0 -0
  523. {thth-5.2.0 → thth-5.4.0}/tests/test_v351_who_owned.py +0 -0
  524. {thth-5.2.0 → thth-5.4.0}/tests/test_v360_goal_analytics.py +0 -0
  525. {thth-5.2.0 → thth-5.4.0}/tests/test_v360_goal_docs.py +0 -0
  526. {thth-5.2.0 → thth-5.4.0}/tests/test_v360_goal_observe_map.py +0 -0
  527. {thth-5.2.0 → thth-5.4.0}/tests/test_v360_goal_plaza.py +0 -0
  528. {thth-5.2.0 → thth-5.4.0}/tests/test_v360_goal_record.py +0 -0
  529. {thth-5.2.0 → thth-5.4.0}/tests/test_v360_goal_send.py +0 -0
  530. {thth-5.2.0 → thth-5.4.0}/tests/test_v360_report_timeline.py +0 -0
  531. {thth-5.2.0 → thth-5.4.0}/tests/test_v370_account_self_healing.py +0 -0
  532. {thth-5.2.0 → thth-5.4.0}/tests/test_v370_approve_pending.py +0 -0
  533. {thth-5.2.0 → thth-5.4.0}/tests/test_v370_click_attribution.py +0 -0
  534. {thth-5.2.0 → thth-5.4.0}/tests/test_v370_collection_stale_basis.py +0 -0
  535. {thth-5.2.0 → thth-5.4.0}/tests/test_v370_docs.py +0 -0
  536. {thth-5.2.0 → thth-5.4.0}/tests/test_v370_follow_comparison.py +0 -0
  537. {thth-5.2.0 → thth-5.4.0}/tests/test_v370_local_path.py +0 -0
  538. {thth-5.2.0 → thth-5.4.0}/tests/test_v370_weekly_goals.py +0 -0
  539. {thth-5.2.0 → thth-5.4.0}/tests/test_v370_where_aggregate.py +0 -0
  540. {thth-5.2.0 → thth-5.4.0}/tests/test_v370_where_also.py +0 -0
  541. {thth-5.2.0 → thth-5.4.0}/tests/test_v370_where_zero.py +0 -0
  542. {thth-5.2.0 → thth-5.4.0}/tests/test_v380_docs.py +0 -0
  543. {thth-5.2.0 → thth-5.4.0}/tests/test_v380_plaza_digest.py +0 -0
  544. {thth-5.2.0 → thth-5.4.0}/tests/test_v380_plaza_from.py +0 -0
  545. {thth-5.2.0 → thth-5.4.0}/tests/test_v380_plaza_mcp.py +0 -0
  546. {thth-5.2.0 → thth-5.4.0}/tests/test_v380_plaza_moments.py +0 -0
  547. {thth-5.2.0 → thth-5.4.0}/tests/test_v380_plaza_owner.py +0 -0
  548. {thth-5.2.0 → thth-5.4.0}/tests/test_v380_plaza_received.py +0 -0
  549. {thth-5.2.0 → thth-5.4.0}/tests/test_v380_plaza_triggers.py +0 -0
  550. {thth-5.2.0 → thth-5.4.0}/tests/test_v381_plaza_visibility_default.py +0 -0
  551. {thth-5.2.0 → thth-5.4.0}/tests/test_v382_confirm_file.py +0 -0
  552. {thth-5.2.0 → thth-5.4.0}/tests/test_v382_first_stage_not_refusal.py +0 -0
  553. {thth-5.2.0 → thth-5.4.0}/tests/test_v382_reader_lock_and_push_pending.py +0 -0
  554. {thth-5.2.0 → thth-5.4.0}/tests/test_v382_sync_without_fetch_head.py +0 -0
  555. {thth-5.2.0 → thth-5.4.0}/tests/test_v390_docs.py +0 -0
  556. {thth-5.2.0 → thth-5.4.0}/tests/test_v390_per_post_clicks.py +0 -0
  557. {thth-5.2.0 → thth-5.4.0}/tests/test_v390_where_rate.py +0 -0
  558. {thth-5.2.0 → thth-5.4.0}/tests/test_v390_where_suggest.py +0 -0
  559. {thth-5.2.0 → thth-5.4.0}/tests/test_v391_plaza_text_numbers.py +0 -0
  560. {thth-5.2.0 → thth-5.4.0}/tests/test_v400_jikki_1017.py +0 -0
  561. {thth-5.2.0 → thth-5.4.0}/tests/test_v400_jikki_1017_bookmark.py +0 -0
  562. {thth-5.2.0 → thth-5.4.0}/tests/test_v400_s1_approve.py +0 -0
  563. {thth-5.2.0 → thth-5.4.0}/tests/test_v400_s1_deliveries.py +0 -0
  564. {thth-5.2.0 → thth-5.4.0}/tests/test_v400_s1_events.py +0 -0
  565. {thth-5.2.0 → thth-5.4.0}/tests/test_v400_s1_fingerprints.py +0 -0
  566. {thth-5.2.0 → thth-5.4.0}/tests/test_v400_s1_guard.py +0 -0
  567. {thth-5.2.0 → thth-5.4.0}/tests/test_v400_s1_locks.py +0 -0
  568. {thth-5.2.0 → thth-5.4.0}/tests/test_v400_s1_principals.py +0 -0
  569. {thth-5.2.0 → thth-5.4.0}/tests/test_v400_s1_publish.py +0 -0
  570. {thth-5.2.0 → thth-5.4.0}/tests/test_v400_s1_remote.py +0 -0
  571. {thth-5.2.0 → thth-5.4.0}/tests/test_v400_s2_adapter.py +0 -0
  572. {thth-5.2.0 → thth-5.4.0}/tests/test_v400_s2_audit.py +0 -0
  573. {thth-5.2.0 → thth-5.4.0}/tests/test_v400_s2_audit_fixes.py +0 -0
  574. {thth-5.2.0 → thth-5.4.0}/tests/test_v400_s2_commit.py +0 -0
  575. {thth-5.2.0 → thth-5.4.0}/tests/test_v400_s2_convert.py +0 -0
  576. {thth-5.2.0 → thth-5.4.0}/tests/test_v400_s2_held.py +0 -0
  577. {thth-5.2.0 → thth-5.4.0}/tests/test_v400_s2_oauth1.py +0 -0
  578. {thth-5.2.0 → thth-5.4.0}/tests/test_v400_s2_principal_auth.py +0 -0
  579. {thth-5.2.0 → thth-5.4.0}/tests/test_v400_s2_syntax_readback.py +0 -0
  580. {thth-5.2.0 → thth-5.4.0}/tests/test_v400_s3_adapter.py +0 -0
  581. {thth-5.2.0 → thth-5.4.0}/tests/test_v400_s3_convert.py +0 -0
  582. {thth-5.2.0 → thth-5.4.0}/tests/test_v400_s3_flow.py +0 -0
  583. {thth-5.2.0 → thth-5.4.0}/tests/test_v400_s4_announce.py +0 -0
  584. {thth-5.2.0 → thth-5.4.0}/tests/test_v400_s4_audit_fixes.py +0 -0
  585. {thth-5.2.0 → thth-5.4.0}/tests/test_v400_s4_bookmark.py +0 -0
  586. {thth-5.2.0 → thth-5.4.0}/tests/test_v400_s4_ledger.py +0 -0
  587. {thth-5.2.0 → thth-5.4.0}/tests/test_v400_s5_audit_fixes.py +0 -0
  588. {thth-5.2.0 → thth-5.4.0}/tests/test_v400_s5_conflict.py +0 -0
  589. {thth-5.2.0 → thth-5.4.0}/tests/test_v400_s5_detect.py +0 -0
  590. {thth-5.2.0 → thth-5.4.0}/tests/test_v400_s5_guard_remote.py +0 -0
  591. {thth-5.2.0 → thth-5.4.0}/tests/test_v400_s5_jikki.py +0 -0
  592. {thth-5.2.0 → thth-5.4.0}/tests/test_v400_s6_audit_fixes.py +0 -0
  593. {thth-5.2.0 → thth-5.4.0}/tests/test_v400_s6_bundle.py +0 -0
  594. {thth-5.2.0 → thth-5.4.0}/tests/test_v400_s6_collect.py +0 -0
  595. {thth-5.2.0 → thth-5.4.0}/tests/test_v401_board_hatena_token.py +0 -0
  596. {thth-5.2.0 → thth-5.4.0}/tests/test_v401_bookmark_burst.py +0 -0
  597. {thth-5.2.0 → thth-5.4.0}/tests/test_v40_contract_json.py +0 -0
  598. {thth-5.2.0 → thth-5.4.0}/tests/test_v410_audit_night.py +0 -0
  599. {thth-5.2.0 → thth-5.4.0}/tests/test_v410_bookmark_batch.py +0 -0
  600. {thth-5.2.0 → thth-5.4.0}/tests/test_v411_batch_cleanup.py +0 -0
  601. {thth-5.2.0 → thth-5.4.0}/tests/test_v420_bookmark_edit.py +0 -0
  602. {thth-5.2.0 → thth-5.4.0}/tests/test_v420_bookmark_tag_bytes.py +0 -0
  603. {thth-5.2.0 → thth-5.4.0}/tests/test_v421_audit_fixes.py +0 -0
  604. {thth-5.2.0 → thth-5.4.0}/tests/test_v422_bookmark_rejections.py +0 -0
  605. {thth-5.2.0 → thth-5.4.0}/tests/test_v422_sparse_checkout.py +0 -0
  606. {thth-5.2.0 → thth-5.4.0}/tests/test_v430_reply_batch.py +0 -0
  607. {thth-5.2.0 → thth-5.4.0}/tests/test_v431_stopped_timer.py +0 -0
  608. {thth-5.2.0 → thth-5.4.0}/tests/test_v440_reply_follow.py +0 -0
  609. {thth-5.2.0 → thth-5.4.0}/tests/test_v441_approval_ledger.py +0 -0
  610. {thth-5.2.0 → thth-5.4.0}/tests/test_v442_sync_lookup.py +0 -0
  611. {thth-5.2.0 → thth-5.4.0}/tests/test_v450_x_full_archive.py +0 -0
  612. {thth-5.2.0 → thth-5.4.0}/tests/test_v452_schedule_stock.py +0 -0
  613. {thth-5.2.0 → thth-5.4.0}/tests/test_v453_approve_redo.py +0 -0
  614. {thth-5.2.0 → thth-5.4.0}/tests/test_v453_bookmark_gate.py +0 -0
  615. {thth-5.2.0 → thth-5.4.0}/tests/test_v454_schedule_thin.py +0 -0
  616. {thth-5.2.0 → thth-5.4.0}/tests/test_v455_media_inflight_published.py +0 -0
  617. {thth-5.2.0 → thth-5.4.0}/tests/test_v455_threads_publish_timeout.py +0 -0
  618. {thth-5.2.0 → thth-5.4.0}/tests/test_v456_not_after.py +0 -0
  619. {thth-5.2.0 → thth-5.4.0}/tests/test_v500_scenario.py +0 -0
  620. {thth-5.2.0 → thth-5.4.0}/tests/test_v50_audit_fixes.py +0 -0
  621. {thth-5.2.0 → thth-5.4.0}/tests/test_v50_dashboard.py +0 -0
  622. {thth-5.2.0 → thth-5.4.0}/tests/test_v50_joined.py +0 -0
  623. {thth-5.2.0 → thth-5.4.0}/tests/test_value_domains_shown.py +0 -0
  624. {thth-5.2.0 → thth-5.4.0}/tests/test_version.py +0 -0
  625. {thth-5.2.0 → thth-5.4.0}/tests/test_where_cli.py +0 -0
  626. {thth-5.2.0 → thth-5.4.0}/tests/test_where_threads_tag_author.py +0 -0
  627. {thth-5.2.0 → thth-5.4.0}/tests/test_who_cli.py +0 -0
  628. {thth-5.2.0 → thth-5.4.0}/tests/test_writeback_rebase.py +0 -0
  629. {thth-5.2.0 → thth-5.4.0}/tests/test_writeback_validate.py +0 -0
  630. {thth-5.2.0 → thth-5.4.0}/tests/test_x_community.py +0 -0
  631. {thth-5.2.0 → thth-5.4.0}/tests/test_x_list.py +0 -0
  632. {thth-5.2.0 → thth-5.4.0}/tests/test_x_metrics.py +0 -0
  633. {thth-5.2.0 → thth-5.4.0}/tests/test_x_owned_metrics.py +0 -0
  634. {thth-5.2.0 → thth-5.4.0}/tests/test_x_search_thread.py +0 -0
  635. {thth-5.2.0 → thth-5.4.0}/tests/test_x_token_autorefresh.py +0 -0
  636. {thth-5.2.0 → thth-5.4.0}/tests/test_x_trends_counts.py +0 -0
  637. {thth-5.2.0 → thth-5.4.0}/thth/__init__.py +0 -0
  638. {thth-5.2.0 → thth-5.4.0}/thth/__main__.py +0 -0
  639. {thth-5.2.0 → thth-5.4.0}/thth/_grapheme_data.py +0 -0
  640. {thth-5.2.0 → thth-5.4.0}/thth/account_report.py +0 -0
  641. {thth-5.2.0 → thth-5.4.0}/thth/account_settings.py +0 -0
  642. {thth-5.2.0 → thth-5.4.0}/thth/activity.py +0 -0
  643. {thth-5.2.0 → thth-5.4.0}/thth/adapters/_x_text_data.py +0 -0
  644. {thth-5.2.0 → thth-5.4.0}/thth/adapters/auth_mastodon.py +0 -0
  645. {thth-5.2.0 → thth-5.4.0}/thth/adapters/auth_threads.py +0 -0
  646. {thth-5.2.0 → thth-5.4.0}/thth/adapters/auth_x.py +0 -0
  647. {thth-5.2.0 → thth-5.4.0}/thth/adapters/bluesky.py +0 -0
  648. {thth-5.2.0 → thth-5.4.0}/thth/adapters/bluesky_media.py +0 -0
  649. {thth-5.2.0 → thth-5.4.0}/thth/adapters/bluesky_video.py +0 -0
  650. {thth-5.2.0 → thth-5.4.0}/thth/adapters/fotolife.py +0 -0
  651. {thth-5.2.0 → thth-5.4.0}/thth/adapters/hatena_common.py +0 -0
  652. {thth-5.2.0 → thth-5.4.0}/thth/adapters/hatenablog.py +0 -0
  653. {thth-5.2.0 → thth-5.4.0}/thth/adapters/hatenabookmark.py +0 -0
  654. {thth-5.2.0 → thth-5.4.0}/thth/adapters/hatenastar.py +0 -0
  655. {thth-5.2.0 → thth-5.4.0}/thth/adapters/mastodon.py +0 -0
  656. {thth-5.2.0 → thth-5.4.0}/thth/adapters/mastodon_media.py +0 -0
  657. {thth-5.2.0 → thth-5.4.0}/thth/adapters/threads.py +0 -0
  658. {thth-5.2.0 → thth-5.4.0}/thth/adapters/threads_media.py +0 -0
  659. {thth-5.2.0 → thth-5.4.0}/thth/adapters/x.py +0 -0
  660. {thth-5.2.0 → thth-5.4.0}/thth/adapters/x_media.py +0 -0
  661. {thth-5.2.0 → thth-5.4.0}/thth/adapters/x_text.py +0 -0
  662. {thth-5.2.0 → thth-5.4.0}/thth/admin_diagnose.py +0 -0
  663. {thth-5.2.0 → thth-5.4.0}/thth/admin_notifications.py +0 -0
  664. {thth-5.2.0 → thth-5.4.0}/thth/admin_report.py +0 -0
  665. {thth-5.2.0 → thth-5.4.0}/thth/advise.py +0 -0
  666. {thth-5.2.0 → thth-5.4.0}/thth/after_cli.py +0 -0
  667. {thth-5.2.0 → thth-5.4.0}/thth/analytics_clicks.py +0 -0
  668. {thth-5.2.0 → thth-5.4.0}/thth/analytics_comparison.py +0 -0
  669. {thth-5.2.0 → thth-5.4.0}/thth/analytics_goals.py +0 -0
  670. {thth-5.2.0 → thth-5.4.0}/thth/analytics_report.py +0 -0
  671. {thth-5.2.0 → thth-5.4.0}/thth/analytics_shapes.py +0 -0
  672. {thth-5.2.0 → thth-5.4.0}/thth/analytics_threads.py +0 -0
  673. {thth-5.2.0 → thth-5.4.0}/thth/analytics_weekly.py +0 -0
  674. {thth-5.2.0 → thth-5.4.0}/thth/announce.py +0 -0
  675. {thth-5.2.0 → thth-5.4.0}/thth/api_diagnostic.py +0 -0
  676. {thth-5.2.0 → thth-5.4.0}/thth/api_requests.py +0 -0
  677. {thth-5.2.0 → thth-5.4.0}/thth/appconfig.py +0 -0
  678. {thth-5.2.0 → thth-5.4.0}/thth/appenv.py +0 -0
  679. {thth-5.2.0 → thth-5.4.0}/thth/approval.py +0 -0
  680. {thth-5.2.0 → thth-5.4.0}/thth/approval_ledger.py +0 -0
  681. {thth-5.2.0 → thth-5.4.0}/thth/approve_pending.py +0 -0
  682. {thth-5.2.0 → thth-5.4.0}/thth/archive_cli.py +0 -0
  683. {thth-5.2.0 → thth-5.4.0}/thth/article_cli.py +0 -0
  684. {thth-5.2.0 → thth-5.4.0}/thth/article_reactions.py +0 -0
  685. {thth-5.2.0 → thth-5.4.0}/thth/ask.py +0 -0
  686. {thth-5.2.0 → thth-5.4.0}/thth/ask_cli.py +0 -0
  687. {thth-5.2.0 → thth-5.4.0}/thth/assess.py +0 -0
  688. {thth-5.2.0 → thth-5.4.0}/thth/audioformats.py +0 -0
  689. {thth-5.2.0 → thth-5.4.0}/thth/auth_log.py +0 -0
  690. {thth-5.2.0 → thth-5.4.0}/thth/authclients.py +0 -0
  691. {thth-5.2.0 → thth-5.4.0}/thth/authpassword.py +0 -0
  692. {thth-5.2.0 → thth-5.4.0}/thth/bluesky_metadata.py +0 -0
  693. {thth-5.2.0 → thth-5.4.0}/thth/bookmark_batch.py +0 -0
  694. {thth-5.2.0 → thth-5.4.0}/thth/bookmark_cli.py +0 -0
  695. {thth-5.2.0 → thth-5.4.0}/thth/budget_x.py +0 -0
  696. {thth-5.2.0 → thth-5.4.0}/thth/budget_x_posts.py +0 -0
  697. {thth-5.2.0 → thth-5.4.0}/thth/bundle.py +0 -0
  698. {thth-5.2.0 → thth-5.4.0}/thth/burst_queue.py +0 -0
  699. {thth-5.2.0 → thth-5.4.0}/thth/click_attribution.py +0 -0
  700. {thth-5.2.0 → thth-5.4.0}/thth/collection_context.py +0 -0
  701. {thth-5.2.0 → thth-5.4.0}/thth/collection_status.py +0 -0
  702. {thth-5.2.0 → thth-5.4.0}/thth/community_cli.py +0 -0
  703. {thth-5.2.0 → thth-5.4.0}/thth/conflict_cli.py +0 -0
  704. {thth-5.2.0 → thth-5.4.0}/thth/deletion.py +0 -0
  705. {thth-5.2.0 → thth-5.4.0}/thth/deliveries.py +0 -0
  706. {thth-5.2.0 → thth-5.4.0}/thth/deliveries_cli.py +0 -0
  707. {thth-5.2.0 → thth-5.4.0}/thth/doctor.py +0 -0
  708. {thth-5.2.0 → thth-5.4.0}/thth/ebmlaudio.py +0 -0
  709. {thth-5.2.0 → thth-5.4.0}/thth/ebmlvideo.py +0 -0
  710. {thth-5.2.0 → thth-5.4.0}/thth/engagements.py +0 -0
  711. {thth-5.2.0 → thth-5.4.0}/thth/fingerprint_golden.py +0 -0
  712. {thth-5.2.0 → thth-5.4.0}/thth/flacformats.py +0 -0
  713. {thth-5.2.0 → thth-5.4.0}/thth/forms.py +0 -0
  714. {thth-5.2.0 → thth-5.4.0}/thth/fotolife_cli.py +0 -0
  715. {thth-5.2.0 → thth-5.4.0}/thth/goals.py +0 -0
  716. {thth-5.2.0 → thth-5.4.0}/thth/graphemes.py +0 -0
  717. {thth-5.2.0 → thth-5.4.0}/thth/guard.py +0 -0
  718. {thth-5.2.0 → thth-5.4.0}/thth/handoff_cursor.py +0 -0
  719. {thth-5.2.0 → thth-5.4.0}/thth/hatena_bookmark.py +0 -0
  720. {thth-5.2.0 → thth-5.4.0}/thth/hatena_cli.py +0 -0
  721. {thth-5.2.0 → thth-5.4.0}/thth/hatena_conflict.py +0 -0
  722. {thth-5.2.0 → thth-5.4.0}/thth/hatena_convert.py +0 -0
  723. {thth-5.2.0 → thth-5.4.0}/thth/hatena_entry.py +0 -0
  724. {thth-5.2.0 → thth-5.4.0}/thth/hatena_fotolife.py +0 -0
  725. {thth-5.2.0 → thth-5.4.0}/thth/hatena_publish.py +0 -0
  726. {thth-5.2.0 → thth-5.4.0}/thth/hatena_reactions.py +0 -0
  727. {thth-5.2.0 → thth-5.4.0}/thth/healthcheck.py +0 -0
  728. {thth-5.2.0 → thth-5.4.0}/thth/httpsafe.py +0 -0
  729. {thth-5.2.0 → thth-5.4.0}/thth/incident.py +0 -0
  730. {thth-5.2.0 → thth-5.4.0}/thth/inflight.py +0 -0
  731. {thth-5.2.0 → thth-5.4.0}/thth/inflight_cli.py +0 -0
  732. {thth-5.2.0 → thth-5.4.0}/thth/inflight_resolve.py +0 -0
  733. {thth-5.2.0 → thth-5.4.0}/thth/invites.py +0 -0
  734. {thth-5.2.0 → thth-5.4.0}/thth/jst.py +0 -0
  735. {thth-5.2.0 → thth-5.4.0}/thth/limits_view.py +0 -0
  736. {thth-5.2.0 → thth-5.4.0}/thth/list_cli.py +0 -0
  737. {thth-5.2.0 → thth-5.4.0}/thth/lock.py +0 -0
  738. {thth-5.2.0 → thth-5.4.0}/thth/managed_repo.py +0 -0
  739. {thth-5.2.0 → thth-5.4.0}/thth/map_cli.py +0 -0
  740. {thth-5.2.0 → thth-5.4.0}/thth/map_store.py +0 -0
  741. {thth-5.2.0 → thth-5.4.0}/thth/map_view.py +0 -0
  742. {thth-5.2.0 → thth-5.4.0}/thth/map_world.py +0 -0
  743. {thth-5.2.0 → thth-5.4.0}/thth/media_cleanup.py +0 -0
  744. {thth-5.2.0 → thth-5.4.0}/thth/media_cli.py +0 -0
  745. {thth-5.2.0 → thth-5.4.0}/thth/media_observations.py +0 -0
  746. {thth-5.2.0 → thth-5.4.0}/thth/media_relay.py +0 -0
  747. {thth-5.2.0 → thth-5.4.0}/thth/media_traits/bluesky.json +0 -0
  748. {thth-5.2.0 → thth-5.4.0}/thth/media_traits/facebook.json +0 -0
  749. {thth-5.2.0 → thth-5.4.0}/thth/media_traits/fotolife.json +0 -0
  750. {thth-5.2.0 → thth-5.4.0}/thth/media_traits/hatenablog.json +0 -0
  751. {thth-5.2.0 → thth-5.4.0}/thth/media_traits/hatenabookmark.json +0 -0
  752. {thth-5.2.0 → thth-5.4.0}/thth/media_traits/mastodon.json +0 -0
  753. {thth-5.2.0 → thth-5.4.0}/thth/media_traits/misskey.json +0 -0
  754. {thth-5.2.0 → thth-5.4.0}/thth/media_traits/threads.json +0 -0
  755. {thth-5.2.0 → thth-5.4.0}/thth/media_traits/x.json +0 -0
  756. {thth-5.2.0 → thth-5.4.0}/thth/media_uploads.py +0 -0
  757. {thth-5.2.0 → thth-5.4.0}/thth/mediaformats.py +0 -0
  758. {thth-5.2.0 → thth-5.4.0}/thth/mpeg_audio.py +0 -0
  759. {thth-5.2.0 → thth-5.4.0}/thth/notification_route.py +0 -0
  760. {thth-5.2.0 → thth-5.4.0}/thth/oauth1.py +0 -0
  761. {thth-5.2.0 → thth-5.4.0}/thth/oggformats.py +0 -0
  762. {thth-5.2.0 → thth-5.4.0}/thth/operations_handoff.py +0 -0
  763. {thth-5.2.0 → thth-5.4.0}/thth/plaza.py +0 -0
  764. {thth-5.2.0 → thth-5.4.0}/thth/plaza_cli.py +0 -0
  765. {thth-5.2.0 → thth-5.4.0}/thth/plaza_from.py +0 -0
  766. {thth-5.2.0 → thth-5.4.0}/thth/plaza_moments.py +0 -0
  767. {thth-5.2.0 → thth-5.4.0}/thth/plaza_observe.py +0 -0
  768. {thth-5.2.0 → thth-5.4.0}/thth/plaza_reads.py +0 -0
  769. {thth-5.2.0 → thth-5.4.0}/thth/plaza_redact.py +0 -0
  770. {thth-5.2.0 → thth-5.4.0}/thth/postid.py +0 -0
  771. {thth-5.2.0 → thth-5.4.0}/thth/principal_cli.py +0 -0
  772. {thth-5.2.0 → thth-5.4.0}/thth/principals.py +0 -0
  773. {thth-5.2.0 → thth-5.4.0}/thth/private_store.py +0 -0
  774. {thth-5.2.0 → thth-5.4.0}/thth/publish_hold.py +0 -0
  775. {thth-5.2.0 → thth-5.4.0}/thth/pulse.py +0 -0
  776. {thth-5.2.0 → thth-5.4.0}/thth/reaction_cli.py +0 -0
  777. {thth-5.2.0 → thth-5.4.0}/thth/reactions.py +0 -0
  778. {thth-5.2.0 → thth-5.4.0}/thth/read_coordination.py +0 -0
  779. {thth-5.2.0 → thth-5.4.0}/thth/read_window.py +0 -0
  780. {thth-5.2.0 → thth-5.4.0}/thth/redact.py +0 -0
  781. {thth-5.2.0 → thth-5.4.0}/thth/refusals.py +0 -0
  782. {thth-5.2.0 → thth-5.4.0}/thth/remote.py +0 -0
  783. {thth-5.2.0 → thth-5.4.0}/thth/replies.py +0 -0
  784. {thth-5.2.0 → thth-5.4.0}/thth/reply_follow.py +0 -0
  785. {thth-5.2.0 → thth-5.4.0}/thth/report.py +0 -0
  786. {thth-5.2.0 → thth-5.4.0}/thth/report_details.py +0 -0
  787. {thth-5.2.0 → thth-5.4.0}/thth/report_http.py +0 -0
  788. {thth-5.2.0 → thth-5.4.0}/thth/report_inbox.py +0 -0
  789. {thth-5.2.0 → thth-5.4.0}/thth/report_isolation.py +0 -0
  790. {thth-5.2.0 → thth-5.4.0}/thth/report_service.py +0 -0
  791. {thth-5.2.0 → thth-5.4.0}/thth/report_timeline.py +0 -0
  792. {thth-5.2.0 → thth-5.4.0}/thth/retract_cli.py +0 -0
  793. {thth-5.2.0 → thth-5.4.0}/thth/runs.py +0 -0
  794. {thth-5.2.0 → thth-5.4.0}/thth/scenario_cli.py +0 -0
  795. {thth-5.2.0 → thth-5.4.0}/thth/scopes.py +0 -0
  796. {thth-5.2.0 → thth-5.4.0}/thth/secrets_fs.py +0 -0
  797. {thth-5.2.0 → thth-5.4.0}/thth/select.py +0 -0
  798. {thth-5.2.0 → thth-5.4.0}/thth/selfupdate.py +0 -0
  799. {thth-5.2.0 → thth-5.4.0}/thth/sent.py +0 -0
  800. {thth-5.2.0 → thth-5.4.0}/thth/server_files.py +0 -0
  801. {thth-5.2.0 → thth-5.4.0}/thth/server_reads.py +0 -0
  802. {thth-5.2.0 → thth-5.4.0}/thth/server_writes.py +0 -0
  803. {thth-5.2.0 → thth-5.4.0}/thth/stop_observation.py +0 -0
  804. {thth-5.2.0 → thth-5.4.0}/thth/study_cli.py +0 -0
  805. {thth-5.2.0 → thth-5.4.0}/thth/study_report.py +0 -0
  806. {thth-5.2.0 → thth-5.4.0}/thth/summary_cli.py +0 -0
  807. {thth-5.2.0 → thth-5.4.0}/thth/systemd_gen.py +0 -0
  808. {thth-5.2.0 → thth-5.4.0}/thth/tags.py +0 -0
  809. {thth-5.2.0 → thth-5.4.0}/thth/threadrun.py +0 -0
  810. {thth-5.2.0 → thth-5.4.0}/thth/threads_read_cli.py +0 -0
  811. {thth-5.2.0 → thth-5.4.0}/thth/threadshape.py +0 -0
  812. {thth-5.2.0 → thth-5.4.0}/thth/threadthrow.py +0 -0
  813. {thth-5.2.0 → thth-5.4.0}/thth/timer_cleanup.py +0 -0
  814. {thth-5.2.0 → thth-5.4.0}/thth/tool_version.py +0 -0
  815. {thth-5.2.0 → thth-5.4.0}/thth/topic_advice.py +0 -0
  816. {thth-5.2.0 → thth-5.4.0}/thth/topic_cli.py +0 -0
  817. {thth-5.2.0 → thth-5.4.0}/thth/topic_models.py +0 -0
  818. {thth-5.2.0 → thth-5.4.0}/thth/topic_store.py +0 -0
  819. {thth-5.2.0 → thth-5.4.0}/thth/topics.py +0 -0
  820. {thth-5.2.0 → thth-5.4.0}/thth/trends_cli.py +0 -0
  821. {thth-5.2.0 → thth-5.4.0}/thth/unanswered.py +0 -0
  822. {thth-5.2.0 → thth-5.4.0}/thth/vorbisformats.py +0 -0
  823. {thth-5.2.0 → thth-5.4.0}/thth/watch_cli.py +0 -0
  824. {thth-5.2.0 → thth-5.4.0}/thth/where_cli.py +0 -0
  825. {thth-5.2.0 → thth-5.4.0}/thth/where_keep.py +0 -0
  826. {thth-5.2.0 → thth-5.4.0}/thth/where_suggest.py +0 -0
  827. {thth-5.2.0 → thth-5.4.0}/thth/who_cli.py +0 -0
  828. {thth-5.2.0 → thth-5.4.0}/thth/worker_version.py +0 -0
  829. {thth-5.2.0 → thth-5.4.0}/thth/writeback.py +0 -0
  830. {thth-5.2.0 → thth-5.4.0}/thth/x_archive.py +0 -0
  831. {thth-5.2.0 → thth-5.4.0}/thth/x_metrics.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: thth
3
- Version: 5.2.0
3
+ Version: 5.4.0
4
4
  Summary: 承認済みの投稿だけを投げる門(Threads・Bluesky・Mastodon)。人が承認し、記録は利用者の git に残る。
5
5
  Project-URL: Homepage, https://thth.me
6
6
  Project-URL: Source, https://github.com/aokings/thth
@@ -124,7 +124,7 @@ MCP の道具は CLI の写しで、同じ `thth` コマンドを呼ぶだけで
124
124
 
125
125
  `thth --help` で各コマンドの説明が出ます。
126
126
 
127
- 動くもの(`thth --help` の全サブコマンド): `lint`・`preview`・`approve`・`approvals`・`account`・`revoke`・`posts`・`replies`・`measured`・`threads`・`after`・`analytics-report`・`study-report`・`study`・`unanswered`・`handoff-report`・`observe`・`morning`・`events`・`summary`・`scenario`・`dashboard`・`deliveries`・`media`・`article`・`conflict`・`hatena`・`bookmark`・`serve-reports`・`worker`・`topics`・`forms`・`queue`・`schedule`・`throw`・`run`・`systemd`・`board`・`collect`・`pull`・`auth`・`refresh`・`maintain`・`send`・`doctor`・`app`・`token`・`ask`・`mentions`・`profile`・`thread`・`where`・`where-log`・`who`・`retract`・`edit`・`reply`・`repost`・`unrepost`・`like`・`unlike`・`list`・`community`・`trends`・`counts`・`archive`・`principal`・`fotolife`・`inflight`・`location`・`notifications`・`report`・`plaza`・`map`・`admin`・`login`・`logout`。
127
+ 動くもの(`thth --help` の全サブコマンド): `lint`・`preview`・`approve`・`approvals`・`account`・`revoke`・`posts`・`replies`・`measured`・`threads`・`after`・`analytics-report`・`study-report`・`study`・`unanswered`・`handoff-report`・`observe`・`morning`・`events`・`summary`・`scenario`・`dashboard`・`deliveries`・`media`・`article`・`conflict`・`hatena`・`bookmark`・`serve-reports`・`worker`・`topics`・`forms`・`queue`・`schedule`・`throw`・`run`・`systemd`・`board`・`collect`・`pull`・`auth`・`refresh`・`maintain`・`send`・`doctor`・`app`・`token`・`ask`・`mentions`・`profile`・`thread`・`where`・`where-log`・`who`・`retract`・`edit`・`reply`・`comments`・`quota`・`repost`・`unrepost`・`like`・`unlike`・`list`・`community`・`trends`・`counts`・`archive`・`principal`・`ads`・`webhook`・`fotolife`・`inflight`・`location`・`notifications`・`report`・`plaza`・`map`・`admin`・`login`・`logout`。
128
128
 
129
129
  ## 版と配布
130
130
 
@@ -100,7 +100,7 @@ MCP の道具は CLI の写しで、同じ `thth` コマンドを呼ぶだけで
100
100
 
101
101
  `thth --help` で各コマンドの説明が出ます。
102
102
 
103
- 動くもの(`thth --help` の全サブコマンド): `lint`・`preview`・`approve`・`approvals`・`account`・`revoke`・`posts`・`replies`・`measured`・`threads`・`after`・`analytics-report`・`study-report`・`study`・`unanswered`・`handoff-report`・`observe`・`morning`・`events`・`summary`・`scenario`・`dashboard`・`deliveries`・`media`・`article`・`conflict`・`hatena`・`bookmark`・`serve-reports`・`worker`・`topics`・`forms`・`queue`・`schedule`・`throw`・`run`・`systemd`・`board`・`collect`・`pull`・`auth`・`refresh`・`maintain`・`send`・`doctor`・`app`・`token`・`ask`・`mentions`・`profile`・`thread`・`where`・`where-log`・`who`・`retract`・`edit`・`reply`・`repost`・`unrepost`・`like`・`unlike`・`list`・`community`・`trends`・`counts`・`archive`・`principal`・`fotolife`・`inflight`・`location`・`notifications`・`report`・`plaza`・`map`・`admin`・`login`・`logout`。
103
+ 動くもの(`thth --help` の全サブコマンド): `lint`・`preview`・`approve`・`approvals`・`account`・`revoke`・`posts`・`replies`・`measured`・`threads`・`after`・`analytics-report`・`study-report`・`study`・`unanswered`・`handoff-report`・`observe`・`morning`・`events`・`summary`・`scenario`・`dashboard`・`deliveries`・`media`・`article`・`conflict`・`hatena`・`bookmark`・`serve-reports`・`worker`・`topics`・`forms`・`queue`・`schedule`・`throw`・`run`・`systemd`・`board`・`collect`・`pull`・`auth`・`refresh`・`maintain`・`send`・`doctor`・`app`・`token`・`ask`・`mentions`・`profile`・`thread`・`where`・`where-log`・`who`・`retract`・`edit`・`reply`・`comments`・`quota`・`repost`・`unrepost`・`like`・`unlike`・`list`・`community`・`trends`・`counts`・`archive`・`principal`・`ads`・`webhook`・`fotolife`・`inflight`・`location`・`notifications`・`report`・`plaza`・`map`・`admin`・`login`・`logout`。
104
104
 
105
105
  ## 版と配布
106
106
 
@@ -0,0 +1,30 @@
1
+ {
2
+ "account": "demo-instagram",
3
+ "project": "demo",
4
+ "media": "instagram",
5
+ "handle": "0",
6
+ "ig_user_id": "0",
7
+ "page_id": "0",
8
+ "user_id": "",
9
+ "repo_dir": "$THTH_ROOT/repos/demo",
10
+ "queue_dir": "docs/sns/queue",
11
+ "replies_dir": "data/sns/replies",
12
+ "quiet_hours": [
13
+ "22:00",
14
+ "07:00"
15
+ ],
16
+ "min_interval_hours": 6,
17
+ "max_per_run": 1,
18
+ "tick_minutes": 10,
19
+ "collect_days": 14,
20
+ "hashtags": false,
21
+ "max_hashtags": 3,
22
+ "stale_days": 7,
23
+ "env": "~/.config/thth/demo-instagram.env",
24
+ "token": "~/.config/thth/demo-instagram.token",
25
+ "ping": "wrapper",
26
+ "timeout": 300,
27
+ "dry_run_env": "THTH_DRY_RUN",
28
+ "production": false,
29
+ "scheduled": false
30
+ }
@@ -57,7 +57,10 @@ FACEBOOK_NOTE = (
57
57
  "の id ではない)。post_id は <ページID>_<数字>。画像は 1〜10 枚(2 枚以上は 1 本にまとまる)。"
58
58
  "できないこと: 動画・字幕・投票・返信に画像・いいね・シェア・inbox。投稿の編集とコメントを隠す・消すは "
59
59
  "CLI(thth edit・thth reply)だけ。文字数の上限 5,000 は THTH の線(台帳の char_limit で上げられる)。"
60
- "facebook_data_access の行は Meta のデータに触れる期限(14 日を切ったら thth auth のやり直し)")
60
+ "facebook_data_access の行は Meta のデータに触れる期限(14 日を切ったら thth auth のやり直し)。"
61
+ "instagram の口座(段 3)は 1 プロ口座 1 口座で、作るのは CLI(--media instagram --handle <ig_user_id> "
62
+ "--page-id <ページID>)と VM での thth auth。出せるのは JPEG の画像 1〜20 枚だけ(文だけ・PNG・動画は断る)。"
63
+ "post_id は数字。キャプションは 2,200 字。90 日ごとに thth auth のやり直し(instagram_data_access の行)")
61
64
 
62
65
  TOOLS = [
63
66
  {
@@ -106,6 +106,25 @@ THTH は CLI ツール。あなた(エージェント)が使う MCP の道
106
106
  - **できないこと**(この版の口に無い・`tool.capabilities` の facebook の行): 動画・字幕・投票・返信の制御・返信に画像、いいね・シェア(`unsupported_on_platform`)、inbox と言及、ほかのページの観測。
107
107
  - **文字数の上限 5,000 は THTH の線**(Facebook の公式の上限ではない)。超えると lint が断り、上げ方(口座の台帳の `char_limit`)を添える。
108
108
  - **データに触れる期限**: ページの鍵そのものに期限は無いが、Meta の `data_access_expires_at`(通常 90 日)が 14 日を切ると `thth maintain` が `data_access_expiring`(過ぎたら `data_access_expired`)と言い、`thth events` に `token_data_access_expiring` が出る。対処は `thth auth <口座> --by <名前>` のやり直し(人が VM で打つ)。
109
+ - **量の制限**(段 2): `thth quota <口座> [--json]` が Meta の使用量のヘッダ(`X-App-Usage`・`X-Business-Use-Case-Usage`)と待ちを返す(読むだけ・MCP には無い)。Meta が量の制限で断ると(code 4・17・32・80001 など)断り文は `meta_rate_limited` で、**出ていない**。Meta が言う戻るまでの分(無ければ 60 分・THTH の線)だけ、その口座の timer は出さず(`thth throw` は `rate_limited`)、明けた次の回に同じ原稿が出る。待ちは `state/<口座>/meta_usage.json`。
110
+
111
+ ## Instagram(Meta 計画 段 3・10/29)
112
+
113
+ - **口座の作り方**: 1 つのプロ口座を 1 口座で扱う。鍵はつながっている Facebook ページの鍵(Facebook ログインの道)で、口座ごとに別の `.token`。`thth account add <口座> --media instagram --project <p> --handle <ig_user_id> --page-id <ページID> --by <名前>` のあと `thth auth <口座> --by <名前>`(VM で人が打つ・MCP には無い)。`ig_user_id` はプロ口座の数字の id(`@` の名前ではない)で、ページの `instagram_business_account` から引ける。認可はページにつながっている Instagram が台帳の `ig_user_id` と同じときだけ保存する(違えば `instagram_account_mismatch`、つながっていなければ `instagram_account_not_linked`)。
114
+ - **post_id の形**: 数字(例 `17895695668004550`)。返事の宛先(`reply_to`)は**コメントの id**。
115
+ - **できること**: JPEG の画像 1〜20 枚(8MB・縦横比 4:5〜1.91:1・2 枚以上はカルーセル)・`alt`(1000 字まで)・`post_options: {is_ai_generated: true}`(AI で作った印・承認の指紋に入る)・コメントへの返事・取り下げ・最近の投稿・コメントと返事を読む・数(いいね・コメント・表示・リーチ・シェア・保存)・口座の日次(`thth collect`)・`thth quota`(1 日の公開の上限 `content_publishing_limit` と使用量)。公開は Meta が `https://thth.me/m/` の一時 URL から画像を取りに来る(Threads と同じ relay)。公開の前に毎回 1 日の上限を読み、届いていれば出さない。
116
+ - **コメントを隠す・戻す・消す**は `thth reply hide|unhide|delete`(Facebook と同じ)。**コメント欄の開け閉め** `thth comments close|open <口座> <post_id> --by <名前>`(1 段・戻せる・記録は moderation)。どれも台帳の `production: true` が要る。MCP には無い。
117
+ - **できないこと**: 文だけの投稿(Instagram に無い)・PNG・GIF・WebP(JPEG だけ)・動画とリール(資材が来てから)・キャプションの編集・いいね・ストーリー・DM・ハッシュタグ検索。コメントは webhook でなく採取で拾う(審査まで)。
118
+ - **文字数の上限 2,200**(キャプションの公式の上限)。ハッシュタグ 30・@ 20 は媒体が数える。
119
+ - **90 日ごとに `thth auth` をやり直す**: instagram の権限はデータに触れる期限(`data_access_expires_at`)に当たる見込み。14 日を切ると `thth maintain` が `data_access_expiring` と言う。
120
+
121
+ ## 広告(Meta 計画 段 4・読むだけ・10/29)
122
+
123
+ - **運営者の CLI だけ**(MCP の道具には無い)。広告は投稿先の媒体ではないので `thth account` でなく `thth ads`。台帳は VM の `adaccounts/<名前>.json`(上限 `ceilings` は 0・`ads_enabled`/`production` は false が既定)、状態は `adaccount_state/<名前>/`。
124
+ - **Meta に書く命令は無い**(作る・配信・予算・`spend_cap`・止めるは段 5)。`spend_cap` と THTH の上限を上げる命令は段 5 になっても無い——`spend_cap` は masaru が広告マネージャで入れ、上限は VM の台帳を直接書く。
125
+ - 命令: `thth ads add <名前> --act-id <数字> --project <p> [--page <口座>] [--ig <口座>] --by <名前>` → `thth ads auth <名前> --by <名前>`(ユーザーの長い鍵・約 60 日)→ `thth ads show|insights|doctor|accounts <名前>`・`thth ads collect`(`thth collect <口座>` のたびにも結んだ広告の口座を読む)。`thth ads auth <名前> --exchange` は鍵の再交換で期限が延びるかを 1 回試す。
126
+ - 金額は**最小単位の整数**(JPY なら円)。通貨が JPY 以外の口座は台帳に `writes_refused: ads_currency_unverified` が付く(段 5 の書く操作を断る)。
127
+ - 見張り(`thth events`・dashboard の要対応・`thth morning` の予定の段): `ads_active_outside_thth`(THTH の外で誰かが配信を始めた・最優先)・`ads_no_end_time`・`ads_account_status`・`ads_spend_cap_unset`(`spend_cap` が入っていない)・`ads_spend_cap_near`・`ads_budget_low`・`ads_token_expiring`(鍵の残り 14 日・`thth maintain` も `ads_token_expiring`)・`ads_read_failed`。
109
128
 
110
129
  ## 何を拒むか
111
130
 
@@ -40,11 +40,11 @@
40
40
  <script>
41
41
  /* ---- 共通(4 つの型で同じ。直すなら 4 つとも同じに) ---- */
42
42
  const THTH_SCHEMA="thth.dashboard/1";
43
- const THTH_MEDIUM={threads:"Threads",x:"X",bluesky:"Bluesky",mastodon:"Mastodon",facebook:"Facebook",hatenablog:"はてなブログ",hatena_bookmark:"はてブ",site:"サイト"};
43
+ const THTH_MEDIUM={threads:"Threads",x:"X",bluesky:"Bluesky",mastodon:"Mastodon",facebook:"Facebook",instagram:"Instagram",hatenablog:"はてなブログ",hatena_bookmark:"はてブ",site:"サイト"};
44
44
  const THTH_STAGE={collect:"リサーチ",diary:"仮説",article:"本記事",announce:"SNS 告知",warm:"エンゲージ"};
45
45
  const THTH_EVENT={researched:"リサーチ済み",hypothesis:"仮説を書いた",article_published:"本記事を公開",closed:"終えた"};
46
- const THTH_KIND={scenario_stalled:"停滞",series_low:"ストック不足",untied_drafts:"未分類の下書き",unanswered_replies:"未返信",schedule_low:"予約切れ間近",schedule_thin:"予約が薄い",not_after_passed:"期限切れ",publish_held:"公開を停止中",account_stopped:"口座が停止",inflight_open:"公開の結果待ち",delivery_held:"配信の保留",delivery_behind:"記事の更新待ち",principal_stopped:"はてな ID が停止",budget_exhausted:"予算切れ",token_data_access_expiring:"データの期限が近い(認可し直す)"};
47
- const THTH_REASON={scenario_not_installed:"シナリオ(段 1)が未導入",no_declarations:"宣言が読めない",scenario_schema_unknown:"シナリオの版が違う",blog_pv_not_collected:"ブログ PV は取っていない",article_clicks_not_connected:"記事側の計測とつないでいない",needs_scenario_show:"詳細が読めない",posts_unlisted:"投稿の一覧が無い",deliveries_unlisted:"配信の一覧が無い",scenario_show_unavailable:"詳細が読めない",accounts_not_summed:"口座をまたいで足さない(口座ごとに見る)",not_measured_yet:"実測がまだ無い",not_published_yet:"まだ公開していない",no_deliveries:"配信がまだ無い",measured_unreadable:"実測の記録が読めない",reactions_unreadable:"反応の記録が読めない",account_unreadable:"口座の台帳が読めない",repo_unreadable:"原稿の置き場が読めない",scenarios_unreadable:"キャンペーン・シリーズの宣言が読めない",queue_unreadable:"原稿の置き場が読めない",sent_unreadable:"送った記録が読めない",deliveries_unreadable:"配信の記録が読めない",replies_unreadable:"返信の記録が読めない",inbox_unreadable:"通知の記録が読めない",source_unreadable:"記事の正本が読めない",inflight_unreadable:"公開の途中の記録が読めない",budget_unreadable:"X の予算の記録が読めない",hatena_account_missing:"はてなの口座がつながっていない",series_account_unknown:"シリーズの口座に知らない名前がある",series_account_out_of_scope:"この鍵では見えない口座がある(その分は数えない)",scenario_unreadable:"キャンペーン・シリーズが読めない",events_unreadable:"出来事が読めない",summary_unreadable:"直近 7 日の集計が読めない",summary_previous_unreadable:"前週の集計が読めない",schedule_unreadable:"予定が読めない",approve_pending_unreadable:"承認待ちが読めない",published_unreadable:"公開の数が読めない",followers_unreadable:"フォロワーの数が読めない",stopped_unreadable:"止まりの印が読めない",unanswered_unreadable:"未返信が読めない",full_trimmed:"詳細は大きすぎて省いた",target_unknown:"対象が見つからない"};
46
+ const THTH_KIND={scenario_stalled:"停滞",series_low:"ストック不足",untied_drafts:"未分類の下書き",unanswered_replies:"未返信",schedule_low:"予約切れ間近",schedule_thin:"予約が薄い",not_after_passed:"期限切れ",publish_held:"公開を停止中",account_stopped:"口座が停止",inflight_open:"公開の結果待ち",delivery_held:"配信の保留",delivery_behind:"記事の更新待ち",principal_stopped:"はてな ID が停止",budget_exhausted:"予算切れ",token_data_access_expiring:"データの期限が近い(認可し直す)",ads_active_outside_thth:"THTH の外で広告が配信中",ads_no_end_time:"終了日の無い広告が配信中",ads_account_status:"広告アカウントの状態",ads_spend_over_estimate:"広告の支出が見積もり超え",ads_spend_cap_near:"広告の支出が上限に近い",ads_budget_low:"広告の月の上限の残りが少ない",ads_spend_cap_unset:"広告アカウントに支出の上限が無い",ads_token_expiring:"広告の鍵の期限が近い",ads_read_failed:"広告の口座を読めない",webhook_down:"Webhook の取り込みが止まっている"};
47
+ const THTH_REASON={scenario_not_installed:"シナリオ(段 1)が未導入",no_declarations:"宣言が読めない",scenario_schema_unknown:"シナリオの版が違う",blog_pv_not_collected:"ブログ PV は取っていない",article_clicks_not_connected:"記事側の計測とつないでいない",needs_scenario_show:"詳細が読めない",posts_unlisted:"投稿の一覧が無い",deliveries_unlisted:"配信の一覧が無い",scenario_show_unavailable:"詳細が読めない",accounts_not_summed:"口座をまたいで足さない(口座ごとに見る)",not_measured_yet:"実測がまだ無い",not_published_yet:"まだ公開していない",no_deliveries:"配信がまだ無い",measured_unreadable:"実測の記録が読めない",reactions_unreadable:"反応の記録が読めない",account_unreadable:"口座の台帳が読めない",repo_unreadable:"原稿の置き場が読めない",scenarios_unreadable:"キャンペーン・シリーズの宣言が読めない",queue_unreadable:"原稿の置き場が読めない",sent_unreadable:"送った記録が読めない",deliveries_unreadable:"配信の記録が読めない",replies_unreadable:"返信の記録が読めない",inbox_unreadable:"通知の記録が読めない",source_unreadable:"記事の正本が読めない",inflight_unreadable:"公開の途中の記録が読めない",budget_unreadable:"X の予算の記録が読めない",hatena_account_missing:"はてなの口座がつながっていない",series_account_unknown:"シリーズの口座に知らない名前がある",series_account_out_of_scope:"この鍵では見えない口座がある(その分は数えない)",scenario_unreadable:"キャンペーン・シリーズが読めない",events_unreadable:"出来事が読めない",summary_unreadable:"直近 7 日の集計が読めない",summary_previous_unreadable:"前週の集計が読めない",schedule_unreadable:"予定が読めない",approve_pending_unreadable:"承認待ちが読めない",published_unreadable:"公開の数が読めない",followers_unreadable:"フォロワーの数が読めない",stopped_unreadable:"止まりの印が読めない",unanswered_unreadable:"未返信が読めない",full_trimmed:"詳細は大きすぎて省いた",target_unknown:"対象が見つからない",ads_unreadable:"広告の口座を読めない",webhook_unreadable:"Webhook の記録を読めない"};
48
48
  function ththEsc(s){return String(s==null?"":s).replace(/[&<>"']/g,c=>({"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"}[c]));}
49
49
  function ththNum(v){return v==null?"—":(typeof v==="number"?v.toLocaleString("ja-JP"):ththEsc(v));}
50
50
  function ththPct(v){return v==null?"—":v+"%";}
@@ -44,11 +44,11 @@
44
44
  <script>
45
45
  /* ---- 共通(4 つの型で同じ。直すなら 4 つとも同じに) ---- */
46
46
  const THTH_SCHEMA="thth.dashboard/1";
47
- const THTH_MEDIUM={threads:"Threads",x:"X",bluesky:"Bluesky",mastodon:"Mastodon",facebook:"Facebook",hatenablog:"はてなブログ",hatena_bookmark:"はてブ",site:"サイト"};
47
+ const THTH_MEDIUM={threads:"Threads",x:"X",bluesky:"Bluesky",mastodon:"Mastodon",facebook:"Facebook",instagram:"Instagram",hatenablog:"はてなブログ",hatena_bookmark:"はてブ",site:"サイト"};
48
48
  const THTH_STAGE={collect:"リサーチ",diary:"仮説",article:"本記事",announce:"SNS 告知",warm:"エンゲージ"};
49
49
  const THTH_EVENT={researched:"リサーチ済み",hypothesis:"仮説を書いた",article_published:"本記事を公開",closed:"終えた"};
50
- const THTH_KIND={scenario_stalled:"停滞",series_low:"ストック不足",untied_drafts:"未分類の下書き",unanswered_replies:"未返信",schedule_low:"予約切れ間近",schedule_thin:"予約が薄い",not_after_passed:"期限切れ",publish_held:"公開を停止中",account_stopped:"口座が停止",inflight_open:"公開の結果待ち",delivery_held:"配信の保留",delivery_behind:"記事の更新待ち",principal_stopped:"はてな ID が停止",budget_exhausted:"予算切れ",token_data_access_expiring:"データの期限が近い(認可し直す)"};
51
- const THTH_REASON={scenario_not_installed:"シナリオ(段 1)が未導入",no_declarations:"宣言が読めない",scenario_schema_unknown:"シナリオの版が違う",blog_pv_not_collected:"ブログ PV は取っていない",article_clicks_not_connected:"記事側の計測とつないでいない",needs_scenario_show:"詳細が読めない",posts_unlisted:"投稿の一覧が無い",deliveries_unlisted:"配信の一覧が無い",scenario_show_unavailable:"詳細が読めない",accounts_not_summed:"口座をまたいで足さない(口座ごとに見る)",not_measured_yet:"実測がまだ無い",not_published_yet:"まだ公開していない",no_deliveries:"配信がまだ無い",measured_unreadable:"実測の記録が読めない",reactions_unreadable:"反応の記録が読めない",account_unreadable:"口座の台帳が読めない",repo_unreadable:"原稿の置き場が読めない",scenarios_unreadable:"キャンペーン・シリーズの宣言が読めない",queue_unreadable:"原稿の置き場が読めない",sent_unreadable:"送った記録が読めない",deliveries_unreadable:"配信の記録が読めない",replies_unreadable:"返信の記録が読めない",inbox_unreadable:"通知の記録が読めない",source_unreadable:"記事の正本が読めない",inflight_unreadable:"公開の途中の記録が読めない",budget_unreadable:"X の予算の記録が読めない",hatena_account_missing:"はてなの口座がつながっていない",series_account_unknown:"シリーズの口座に知らない名前がある",series_account_out_of_scope:"この鍵では見えない口座がある(その分は数えない)",scenario_unreadable:"キャンペーン・シリーズが読めない",events_unreadable:"出来事が読めない",summary_unreadable:"直近 7 日の集計が読めない",summary_previous_unreadable:"前週の集計が読めない",schedule_unreadable:"予定が読めない",approve_pending_unreadable:"承認待ちが読めない",published_unreadable:"公開の数が読めない",followers_unreadable:"フォロワーの数が読めない",stopped_unreadable:"止まりの印が読めない",unanswered_unreadable:"未返信が読めない",full_trimmed:"詳細は大きすぎて省いた",target_unknown:"対象が見つからない"};
50
+ const THTH_KIND={scenario_stalled:"停滞",series_low:"ストック不足",untied_drafts:"未分類の下書き",unanswered_replies:"未返信",schedule_low:"予約切れ間近",schedule_thin:"予約が薄い",not_after_passed:"期限切れ",publish_held:"公開を停止中",account_stopped:"口座が停止",inflight_open:"公開の結果待ち",delivery_held:"配信の保留",delivery_behind:"記事の更新待ち",principal_stopped:"はてな ID が停止",budget_exhausted:"予算切れ",token_data_access_expiring:"データの期限が近い(認可し直す)",ads_active_outside_thth:"THTH の外で広告が配信中",ads_no_end_time:"終了日の無い広告が配信中",ads_account_status:"広告アカウントの状態",ads_spend_over_estimate:"広告の支出が見積もり超え",ads_spend_cap_near:"広告の支出が上限に近い",ads_budget_low:"広告の月の上限の残りが少ない",ads_spend_cap_unset:"広告アカウントに支出の上限が無い",ads_token_expiring:"広告の鍵の期限が近い",ads_read_failed:"広告の口座を読めない",webhook_down:"Webhook の取り込みが止まっている"};
51
+ const THTH_REASON={scenario_not_installed:"シナリオ(段 1)が未導入",no_declarations:"宣言が読めない",scenario_schema_unknown:"シナリオの版が違う",blog_pv_not_collected:"ブログ PV は取っていない",article_clicks_not_connected:"記事側の計測とつないでいない",needs_scenario_show:"詳細が読めない",posts_unlisted:"投稿の一覧が無い",deliveries_unlisted:"配信の一覧が無い",scenario_show_unavailable:"詳細が読めない",accounts_not_summed:"口座をまたいで足さない(口座ごとに見る)",not_measured_yet:"実測がまだ無い",not_published_yet:"まだ公開していない",no_deliveries:"配信がまだ無い",measured_unreadable:"実測の記録が読めない",reactions_unreadable:"反応の記録が読めない",account_unreadable:"口座の台帳が読めない",repo_unreadable:"原稿の置き場が読めない",scenarios_unreadable:"キャンペーン・シリーズの宣言が読めない",queue_unreadable:"原稿の置き場が読めない",sent_unreadable:"送った記録が読めない",deliveries_unreadable:"配信の記録が読めない",replies_unreadable:"返信の記録が読めない",inbox_unreadable:"通知の記録が読めない",source_unreadable:"記事の正本が読めない",inflight_unreadable:"公開の途中の記録が読めない",budget_unreadable:"X の予算の記録が読めない",hatena_account_missing:"はてなの口座がつながっていない",series_account_unknown:"シリーズの口座に知らない名前がある",series_account_out_of_scope:"この鍵では見えない口座がある(その分は数えない)",scenario_unreadable:"キャンペーン・シリーズが読めない",events_unreadable:"出来事が読めない",summary_unreadable:"直近 7 日の集計が読めない",summary_previous_unreadable:"前週の集計が読めない",schedule_unreadable:"予定が読めない",approve_pending_unreadable:"承認待ちが読めない",published_unreadable:"公開の数が読めない",followers_unreadable:"フォロワーの数が読めない",stopped_unreadable:"止まりの印が読めない",unanswered_unreadable:"未返信が読めない",full_trimmed:"詳細は大きすぎて省いた",target_unknown:"対象が見つからない",ads_unreadable:"広告の口座を読めない",webhook_unreadable:"Webhook の記録を読めない"};
52
52
  function ththEsc(s){return String(s==null?"":s).replace(/[&<>"']/g,c=>({"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"}[c]));}
53
53
  function ththNum(v){return v==null?"—":(typeof v==="number"?v.toLocaleString("ja-JP"):ththEsc(v));}
54
54
  function ththPct(v){return v==null?"—":v+"%";}
@@ -44,11 +44,11 @@ table.pl{width:100%;border-collapse:collapse;font-size:11px;table-layout:fixed}.
44
44
  <script>
45
45
  /* ---- 共通(4 つの型で同じ。直すなら 4 つとも同じに) ---- */
46
46
  const THTH_SCHEMA="thth.dashboard/1";
47
- const THTH_MEDIUM={threads:"Threads",x:"X",bluesky:"Bluesky",mastodon:"Mastodon",facebook:"Facebook",hatenablog:"はてなブログ",hatena_bookmark:"はてブ",site:"サイト"};
47
+ const THTH_MEDIUM={threads:"Threads",x:"X",bluesky:"Bluesky",mastodon:"Mastodon",facebook:"Facebook",instagram:"Instagram",hatenablog:"はてなブログ",hatena_bookmark:"はてブ",site:"サイト"};
48
48
  const THTH_STAGE={collect:"リサーチ",diary:"仮説",article:"本記事",announce:"SNS 告知",warm:"エンゲージ"};
49
49
  const THTH_EVENT={researched:"リサーチ済み",hypothesis:"仮説を書いた",article_published:"本記事を公開",closed:"終えた"};
50
- const THTH_KIND={scenario_stalled:"停滞",series_low:"ストック不足",untied_drafts:"未分類の下書き",unanswered_replies:"未返信",schedule_low:"予約切れ間近",schedule_thin:"予約が薄い",not_after_passed:"期限切れ",publish_held:"公開を停止中",account_stopped:"口座が停止",inflight_open:"公開の結果待ち",delivery_held:"配信の保留",delivery_behind:"記事の更新待ち",principal_stopped:"はてな ID が停止",budget_exhausted:"予算切れ",token_data_access_expiring:"データの期限が近い(認可し直す)"};
51
- const THTH_REASON={scenario_not_installed:"シナリオ(段 1)が未導入",no_declarations:"宣言が読めない",scenario_schema_unknown:"シナリオの版が違う",blog_pv_not_collected:"ブログ PV は取っていない",article_clicks_not_connected:"記事側の計測とつないでいない",needs_scenario_show:"詳細が読めない",posts_unlisted:"投稿の一覧が無い",deliveries_unlisted:"配信の一覧が無い",scenario_show_unavailable:"詳細が読めない",accounts_not_summed:"口座をまたいで足さない(口座ごとに見る)",not_measured_yet:"実測がまだ無い",not_published_yet:"まだ公開していない",no_deliveries:"配信がまだ無い",measured_unreadable:"実測の記録が読めない",reactions_unreadable:"反応の記録が読めない",account_unreadable:"口座の台帳が読めない",repo_unreadable:"原稿の置き場が読めない",scenarios_unreadable:"キャンペーン・シリーズの宣言が読めない",queue_unreadable:"原稿の置き場が読めない",sent_unreadable:"送った記録が読めない",deliveries_unreadable:"配信の記録が読めない",replies_unreadable:"返信の記録が読めない",inbox_unreadable:"通知の記録が読めない",source_unreadable:"記事の正本が読めない",inflight_unreadable:"公開の途中の記録が読めない",budget_unreadable:"X の予算の記録が読めない",hatena_account_missing:"はてなの口座がつながっていない",series_account_unknown:"シリーズの口座に知らない名前がある",series_account_out_of_scope:"この鍵では見えない口座がある(その分は数えない)",scenario_unreadable:"キャンペーン・シリーズが読めない",events_unreadable:"出来事が読めない",summary_unreadable:"直近 7 日の集計が読めない",summary_previous_unreadable:"前週の集計が読めない",schedule_unreadable:"予定が読めない",approve_pending_unreadable:"承認待ちが読めない",published_unreadable:"公開の数が読めない",followers_unreadable:"フォロワーの数が読めない",stopped_unreadable:"止まりの印が読めない",unanswered_unreadable:"未返信が読めない",full_trimmed:"詳細は大きすぎて省いた",target_unknown:"対象が見つからない"};
50
+ const THTH_KIND={scenario_stalled:"停滞",series_low:"ストック不足",untied_drafts:"未分類の下書き",unanswered_replies:"未返信",schedule_low:"予約切れ間近",schedule_thin:"予約が薄い",not_after_passed:"期限切れ",publish_held:"公開を停止中",account_stopped:"口座が停止",inflight_open:"公開の結果待ち",delivery_held:"配信の保留",delivery_behind:"記事の更新待ち",principal_stopped:"はてな ID が停止",budget_exhausted:"予算切れ",token_data_access_expiring:"データの期限が近い(認可し直す)",ads_active_outside_thth:"THTH の外で広告が配信中",ads_no_end_time:"終了日の無い広告が配信中",ads_account_status:"広告アカウントの状態",ads_spend_over_estimate:"広告の支出が見積もり超え",ads_spend_cap_near:"広告の支出が上限に近い",ads_budget_low:"広告の月の上限の残りが少ない",ads_spend_cap_unset:"広告アカウントに支出の上限が無い",ads_token_expiring:"広告の鍵の期限が近い",ads_read_failed:"広告の口座を読めない",webhook_down:"Webhook の取り込みが止まっている"};
51
+ const THTH_REASON={scenario_not_installed:"シナリオ(段 1)が未導入",no_declarations:"宣言が読めない",scenario_schema_unknown:"シナリオの版が違う",blog_pv_not_collected:"ブログ PV は取っていない",article_clicks_not_connected:"記事側の計測とつないでいない",needs_scenario_show:"詳細が読めない",posts_unlisted:"投稿の一覧が無い",deliveries_unlisted:"配信の一覧が無い",scenario_show_unavailable:"詳細が読めない",accounts_not_summed:"口座をまたいで足さない(口座ごとに見る)",not_measured_yet:"実測がまだ無い",not_published_yet:"まだ公開していない",no_deliveries:"配信がまだ無い",measured_unreadable:"実測の記録が読めない",reactions_unreadable:"反応の記録が読めない",account_unreadable:"口座の台帳が読めない",repo_unreadable:"原稿の置き場が読めない",scenarios_unreadable:"キャンペーン・シリーズの宣言が読めない",queue_unreadable:"原稿の置き場が読めない",sent_unreadable:"送った記録が読めない",deliveries_unreadable:"配信の記録が読めない",replies_unreadable:"返信の記録が読めない",inbox_unreadable:"通知の記録が読めない",source_unreadable:"記事の正本が読めない",inflight_unreadable:"公開の途中の記録が読めない",budget_unreadable:"X の予算の記録が読めない",hatena_account_missing:"はてなの口座がつながっていない",series_account_unknown:"シリーズの口座に知らない名前がある",series_account_out_of_scope:"この鍵では見えない口座がある(その分は数えない)",scenario_unreadable:"キャンペーン・シリーズが読めない",events_unreadable:"出来事が読めない",summary_unreadable:"直近 7 日の集計が読めない",summary_previous_unreadable:"前週の集計が読めない",schedule_unreadable:"予定が読めない",approve_pending_unreadable:"承認待ちが読めない",published_unreadable:"公開の数が読めない",followers_unreadable:"フォロワーの数が読めない",stopped_unreadable:"止まりの印が読めない",unanswered_unreadable:"未返信が読めない",full_trimmed:"詳細は大きすぎて省いた",target_unknown:"対象が見つからない",ads_unreadable:"広告の口座を読めない",webhook_unreadable:"Webhook の記録を読めない"};
52
52
  function ththEsc(s){return String(s==null?"":s).replace(/[&<>"']/g,c=>({"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"}[c]));}
53
53
  function ththNum(v){return v==null?"—":(typeof v==="number"?v.toLocaleString("ja-JP"):ththEsc(v));}
54
54
  function ththPct(v){return v==null?"—":v+"%";}
@@ -39,11 +39,11 @@
39
39
  <script>
40
40
  /* ---- 共通(4 つの型で同じ。直すなら 4 つとも同じに) ---- */
41
41
  const THTH_SCHEMA="thth.dashboard/1";
42
- const THTH_MEDIUM={threads:"Threads",x:"X",bluesky:"Bluesky",mastodon:"Mastodon",facebook:"Facebook",hatenablog:"はてなブログ",hatena_bookmark:"はてブ",site:"サイト"};
42
+ const THTH_MEDIUM={threads:"Threads",x:"X",bluesky:"Bluesky",mastodon:"Mastodon",facebook:"Facebook",instagram:"Instagram",hatenablog:"はてなブログ",hatena_bookmark:"はてブ",site:"サイト"};
43
43
  const THTH_STAGE={collect:"リサーチ",diary:"仮説",article:"本記事",announce:"SNS 告知",warm:"エンゲージ"};
44
44
  const THTH_EVENT={researched:"リサーチ済み",hypothesis:"仮説を書いた",article_published:"本記事を公開",closed:"終えた"};
45
- const THTH_KIND={scenario_stalled:"停滞",series_low:"ストック不足",untied_drafts:"未分類の下書き",unanswered_replies:"未返信",schedule_low:"予約切れ間近",schedule_thin:"予約が薄い",not_after_passed:"期限切れ",publish_held:"公開を停止中",account_stopped:"口座が停止",inflight_open:"公開の結果待ち",delivery_held:"配信の保留",delivery_behind:"記事の更新待ち",principal_stopped:"はてな ID が停止",budget_exhausted:"予算切れ",token_data_access_expiring:"データの期限が近い(認可し直す)"};
46
- const THTH_REASON={scenario_not_installed:"シナリオ(段 1)が未導入",no_declarations:"宣言が読めない",scenario_schema_unknown:"シナリオの版が違う",blog_pv_not_collected:"ブログ PV は取っていない",article_clicks_not_connected:"記事側の計測とつないでいない",needs_scenario_show:"詳細が読めない",posts_unlisted:"投稿の一覧が無い",deliveries_unlisted:"配信の一覧が無い",scenario_show_unavailable:"詳細が読めない",accounts_not_summed:"口座をまたいで足さない(口座ごとに見る)",not_measured_yet:"実測がまだ無い",not_published_yet:"まだ公開していない",no_deliveries:"配信がまだ無い",measured_unreadable:"実測の記録が読めない",reactions_unreadable:"反応の記録が読めない",account_unreadable:"口座の台帳が読めない",repo_unreadable:"原稿の置き場が読めない",scenarios_unreadable:"キャンペーン・シリーズの宣言が読めない",queue_unreadable:"原稿の置き場が読めない",sent_unreadable:"送った記録が読めない",deliveries_unreadable:"配信の記録が読めない",replies_unreadable:"返信の記録が読めない",inbox_unreadable:"通知の記録が読めない",source_unreadable:"記事の正本が読めない",inflight_unreadable:"公開の途中の記録が読めない",budget_unreadable:"X の予算の記録が読めない",hatena_account_missing:"はてなの口座がつながっていない",series_account_unknown:"シリーズの口座に知らない名前がある",series_account_out_of_scope:"この鍵では見えない口座がある(その分は数えない)",scenario_unreadable:"キャンペーン・シリーズが読めない",events_unreadable:"出来事が読めない",summary_unreadable:"直近 7 日の集計が読めない",summary_previous_unreadable:"前週の集計が読めない",schedule_unreadable:"予定が読めない",approve_pending_unreadable:"承認待ちが読めない",published_unreadable:"公開の数が読めない",followers_unreadable:"フォロワーの数が読めない",stopped_unreadable:"止まりの印が読めない",unanswered_unreadable:"未返信が読めない",full_trimmed:"詳細は大きすぎて省いた",target_unknown:"対象が見つからない"};
45
+ const THTH_KIND={scenario_stalled:"停滞",series_low:"ストック不足",untied_drafts:"未分類の下書き",unanswered_replies:"未返信",schedule_low:"予約切れ間近",schedule_thin:"予約が薄い",not_after_passed:"期限切れ",publish_held:"公開を停止中",account_stopped:"口座が停止",inflight_open:"公開の結果待ち",delivery_held:"配信の保留",delivery_behind:"記事の更新待ち",principal_stopped:"はてな ID が停止",budget_exhausted:"予算切れ",token_data_access_expiring:"データの期限が近い(認可し直す)",ads_active_outside_thth:"THTH の外で広告が配信中",ads_no_end_time:"終了日の無い広告が配信中",ads_account_status:"広告アカウントの状態",ads_spend_over_estimate:"広告の支出が見積もり超え",ads_spend_cap_near:"広告の支出が上限に近い",ads_budget_low:"広告の月の上限の残りが少ない",ads_spend_cap_unset:"広告アカウントに支出の上限が無い",ads_token_expiring:"広告の鍵の期限が近い",ads_read_failed:"広告の口座を読めない",webhook_down:"Webhook の取り込みが止まっている"};
46
+ const THTH_REASON={scenario_not_installed:"シナリオ(段 1)が未導入",no_declarations:"宣言が読めない",scenario_schema_unknown:"シナリオの版が違う",blog_pv_not_collected:"ブログ PV は取っていない",article_clicks_not_connected:"記事側の計測とつないでいない",needs_scenario_show:"詳細が読めない",posts_unlisted:"投稿の一覧が無い",deliveries_unlisted:"配信の一覧が無い",scenario_show_unavailable:"詳細が読めない",accounts_not_summed:"口座をまたいで足さない(口座ごとに見る)",not_measured_yet:"実測がまだ無い",not_published_yet:"まだ公開していない",no_deliveries:"配信がまだ無い",measured_unreadable:"実測の記録が読めない",reactions_unreadable:"反応の記録が読めない",account_unreadable:"口座の台帳が読めない",repo_unreadable:"原稿の置き場が読めない",scenarios_unreadable:"キャンペーン・シリーズの宣言が読めない",queue_unreadable:"原稿の置き場が読めない",sent_unreadable:"送った記録が読めない",deliveries_unreadable:"配信の記録が読めない",replies_unreadable:"返信の記録が読めない",inbox_unreadable:"通知の記録が読めない",source_unreadable:"記事の正本が読めない",inflight_unreadable:"公開の途中の記録が読めない",budget_unreadable:"X の予算の記録が読めない",hatena_account_missing:"はてなの口座がつながっていない",series_account_unknown:"シリーズの口座に知らない名前がある",series_account_out_of_scope:"この鍵では見えない口座がある(その分は数えない)",scenario_unreadable:"キャンペーン・シリーズが読めない",events_unreadable:"出来事が読めない",summary_unreadable:"直近 7 日の集計が読めない",summary_previous_unreadable:"前週の集計が読めない",schedule_unreadable:"予定が読めない",approve_pending_unreadable:"承認待ちが読めない",published_unreadable:"公開の数が読めない",followers_unreadable:"フォロワーの数が読めない",stopped_unreadable:"止まりの印が読めない",unanswered_unreadable:"未返信が読めない",full_trimmed:"詳細は大きすぎて省いた",target_unknown:"対象が見つからない",ads_unreadable:"広告の口座を読めない",webhook_unreadable:"Webhook の記録を読めない"};
47
47
  function ththEsc(s){return String(s==null?"":s).replace(/[&<>"']/g,c=>({"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"}[c]));}
48
48
  function ththNum(v){return v==null?"—":(typeof v==="number"?v.toLocaleString("ja-JP"):ththEsc(v));}
49
49
  function ththPct(v){return v==null?"—":v+"%";}
@@ -125,6 +125,23 @@ def no_real_hatena_adapters(monkeypatch):
125
125
  monkeypatch.setattr(hatena_reactions, "SLEEP", lambda seconds: None)
126
126
 
127
127
 
128
+ @pytest.fixture(autouse=True)
129
+ def no_real_meta_app_secret(monkeypatch):
130
+ """Meta の共通層(`thth/meta/graph.py`)が `appsecret_proof` のために読む app secret を、
131
+ **試験が `THTH_APP_ENV_PATH` で隔離した app.env を置いたときだけ**読ませる(段 2)。
132
+
133
+ `FacebookAdapter.from_account` は `META_APP_SECRET` を読む。HOME を隔離していない試験が
134
+ 打った人の実物の `~/.config/thth/app.env` を読まないようにする(値は出ないが、読まない)。
135
+ """
136
+ from thth.meta import graph as meta_graph
137
+ original = meta_graph.load_app_secret
138
+
139
+ def isolated():
140
+ import os
141
+ return original() if os.environ.get("THTH_APP_ENV_PATH") else None
142
+ monkeypatch.setattr(meta_graph, "load_app_secret", isolated)
143
+
144
+
128
145
  @pytest.fixture(autouse=True)
129
146
  def no_threads_status_poll_wait(monkeypatch):
130
147
  """公開の結果が分からないときの container の問い合わせ(設計 3.3.1 §2)を
@@ -720,9 +720,11 @@ def test_T3_REGISTRYに5媒体が載っている():
720
720
  from thth.adapters import mastodon as mastodon_mod
721
721
  from thth.adapters import x as x_mod
722
722
  from thth.adapters import facebook as facebook_mod
723
- # 10/29: Facebook ページ(`thth/adapters/facebook.py`)を足した。
724
- assert adapters_mod.known_media() == ["bluesky", "facebook", "mastodon", "threads", "x"]
723
+ from thth.adapters import instagram as instagram_mod
724
+ # 10/29: Facebook ページ(`thth/adapters/facebook.py`)を足した。段 3: Instagram(`instagram.py`)。
725
+ assert adapters_mod.known_media() == ["bluesky", "facebook", "instagram", "mastodon", "threads", "x"]
725
726
  assert adapters_mod.REGISTRY["facebook"] is facebook_mod.FacebookAdapter
727
+ assert adapters_mod.REGISTRY["instagram"] is instagram_mod.InstagramAdapter
726
728
  assert adapters_mod.REGISTRY["bluesky"] is bluesky_mod.BlueskyAdapter
727
729
  assert adapters_mod.REGISTRY["mastodon"] is mastodon_mod.MastodonAdapter
728
730
  assert adapters_mod.REGISTRY["x"] is x_mod.XAdapter
@@ -92,8 +92,10 @@ def test_REGISTRYにfacebookが載り境界の語彙に収まる():
92
92
  cls = adapters_mod.REGISTRY["facebook"]
93
93
  assert cls is fb.FacebookAdapter
94
94
  assert set(cls.CAPABILITIES) <= base.KNOWN_CAPABILITIES
95
+ # 段 6(10/29): inbox・mentions(Webhook の記録から読む・`tests/test_meta_stage6.py`)。
95
96
  assert cls.capabilities() == {"recent_posts", "thread_read", "views", "link_preview",
96
- "account_insights", "carousel", "edit_post", "hide_reply"}
97
+ "account_insights", "carousel", "edit_post", "hide_reply", "quota",
98
+ "inbox", "mentions"}
97
99
  assert cls.TOKEN_KEYS == ("access_token",) and cls.has_token({"access_token": "x"})
98
100
  assert cls.TOKEN_NO_EXPIRY is True and cls.AUTH_NEEDS_APP_ENV is True
99
101
  assert cls.DELETE_PERMISSION == "pages_manage_posts"
@@ -87,12 +87,12 @@ def test_段0にだけあった5つのidの行き先():
87
87
 
88
88
  def test_SNSの4媒体の行は表の1行に集まり根拠は媒体ごと():
89
89
  row = limits_policy.rule("post.daily")
90
- # facebook は 10/29 段 0 で媒体の性質の台帳を持った(5 媒体)。
91
- assert set(row["ledger"]) == {"threads", "x", "bluesky", "mastodon", "facebook"}
90
+ # facebook は 10/29 段 0 で媒体の性質の台帳を持った(5 媒体)。instagram は段 3(6 媒体)。
91
+ assert set(row["ledger"]) == {"threads", "x", "bluesky", "mastodon", "facebook", "instagram"}
92
92
  traits = {m: limits_policy.resolve(row, cfg={"media": m})["trait"] for m in row["ledger"]}
93
93
  assert traits == {"threads": "threads.api_posts_24h", "x": "x.account_post_limit",
94
94
  "bluesky": "bluesky.pds_points", "mastodon": "mastodon.api_rate",
95
- "facebook": "facebook.api_rate"}
95
+ "facebook": "facebook.api_rate", "instagram": "instagram.publish_limit"}
96
96
  # Threads の反応は台帳に根拠が無い → thth の線
97
97
  assert limits_policy.resolve(limits_policy.rule("reaction.daily"), cfg={"media": "threads"})["trait"] \
98
98
  == "thth:reaction.daily"
@@ -456,7 +456,7 @@ def test_既定の数は柵の表から引く(ledger):
456
456
  for rule_id, row in (trait.get("defaults") or {}).items():
457
457
  if rule_id == "post.daily":
458
458
  row["count"] = 9
459
- for media in ("threads", "x", "bluesky", "mastodon", "facebook"):
459
+ for media in ("threads", "x", "bluesky", "mastodon", "facebook", "instagram"):
460
460
  edit_ledger(ledger, media, bump)
461
461
  assert accounts.GUARD_DEFAULTS["daily_max_posts"] == 9
462
462
  assert accounts.guard_limits({"media": "threads"})["daily_max_posts"] == 9
@@ -18,7 +18,7 @@ import pytest
18
18
 
19
19
  from thth import (account_cli, accounts, adapters, dashboard_cli, leave_gate, limits_config,
20
20
  limits_policy, media, media_capabilities, media_delivery, media_registry,
21
- media_traits, queuefile, scenario, thread_read)
21
+ media_traits, queuefile, scenario, thread_read, webhook)
22
22
 
23
23
  REGISTRY = media_registry.names()
24
24
  NON_ADAPTER = media_registry.NON_ADAPTER_NAMES
@@ -69,6 +69,8 @@ TABLES = {
69
69
  "dashboard_cli.MEDIUM_JA": lambda: set(dashboard_cli.MEDIUM_JA),
70
70
  "thread_read.SOURCE_BY_MEDIUM": lambda: set(thread_read.SOURCE_BY_MEDIUM),
71
71
  "thread_read.PERMISSION_BY_MEDIUM": lambda: set(thread_read.PERMISSION_BY_MEDIUM),
72
+ # Meta 計画 段 6: Webhook を購読する欄(Meta の Graph の媒体だけ・ほかは registry に理由)。
73
+ "webhook.SUBSCRIBE_FIELDS": lambda: set(webhook.SUBSCRIBE_FIELDS),
72
74
  **{f"dashboard/{level}.html THTH_MEDIUM": (lambda level=level: _template_medium(level))
73
75
  for level in ("all", "project", "campaign", "series")},
74
76
  }
@@ -126,9 +128,19 @@ def test_雛形のhandleは雛形の台帳とダミーの印に一致する():
126
128
 
127
129
 
128
130
  def test_添付を媒体が取りに来るのはrelayを渡す媒体だけ():
131
+ """段 3(10/29): relay を渡すかは registry の `media_fetch: pull` だけで決める(`meta.media.relay_for`)。
132
+
133
+ `media_delivery` は媒体名で relay を分けない(`relay_for` を呼ぶだけ)。段 2 までは
134
+ `MediaRelay(...) if cfg['media']=='threads'` の 1 行だったのを、段 3 で Instagram を足すときに置き換えた。
135
+ """
136
+ from thth.meta import media as meta_media
129
137
  source = inspect.getsource(media_delivery)
130
- relayed = set(re.findall(r"MediaRelay\([^)]*\) if cfg\['media'\]=='([a-z_]+)'", source))
131
- assert relayed, "media_delivery の relay の分岐を読めません(試験の正規表現を直す)"
138
+ assert "meta_media.relay_for(cfg,has_items=bool(items))" in source
139
+ assert not re.search(r"MediaRelay\([^)]*\) if cfg\['media'\]", source), "媒体名で relay を分ける行が戻っている"
132
140
  pulled = {name for name, medium in media_registry.MEDIA.items() if medium.media_fetch == "pull"}
133
- assert pulled == relayed
141
+ assert pulled == {"threads", "instagram"}
142
+ for name in media_registry.MEDIA:
143
+ relay = meta_media.relay_for({"media": name, "account": "demo-" + name}, has_items=True)
144
+ assert (relay is not None) == (name in pulled), name
145
+ assert meta_media.relay_for({"media": name, "account": "demo-" + name}, has_items=False) is None
134
146
  assert {m.media_fetch for m in media_registry.MEDIA.values()} <= {"push", "pull"}