gabriel / muse public
feat patch social task/social-domain-phase01 #1 / 1
AI Agent gabriel · 137 days ago · May 2, 2026 · Diff

feat(social): Phase 01 — muse social CLI surface

Adds the muse social subcommand tree and all pure state helpers.

- muse/cli/commands/social.py: register(), run_post, run_follow, run_unfollow, run_timeline, run_react, run_profile; helpers _build_post, _write_post, _write_follow, _delete_follow, _write_reaction, _read_profile, _write_profile, _load_posts. Posts are content-addressed on (body+reply_to+quote_of+created_at). Follows are per-handle files under graph/follows/ — set-union merge is automatic with no special logic needed. Timeline sorted newest-first; --limit caps output. - muse/cli/app.py: social_cmd registered under 'social' subcommand. - tests/test_social_cli.py: 62 tests across 8 classes — shape, state helpers, post, follow/unfollow, timeline, react, profile, docstrings. All RED before implementation, all GREEN after.

sha256:5c68c735f0c21219df9f1839e92201af4d2b15f62c4d396f2ea6a5e6b86bb02d sha
+106 ~122 symbols
sha256:dd42e3cd14abe143fc11f448a69fb0a2fa8c7f009b8a5d8f32f79be6bb39772b snapshot
+106
symbols added
~122
symbols modified
0
dead code introduced
Semantic Changes 228 symbols
~ muse/cli/commands/social.py .py 25 symbols added
+ _build_post function function _build_post L55–90
+ _content_id function function _content_id L50–52
+ _delete_follow function function _delete_follow L134–144
+ _load_posts function function _load_posts L217–240
+ _read_profile function function _read_profile L184–196
+ _utc_now function function _utc_now L45–47
+ _write_follow function function _write_follow L114–131
+ _write_post function function _write_post L93–111
+ _write_profile function function _write_profile L199–214
+ _write_reaction function function _write_reaction L147–181
+ annotations import import annotations L31–31
+ argparse import import argparse L33–33
+ datetime import import datetime L38–38
+ hashlib import import hashlib L34–34
+ json import import json L35–35
+ pathlib import import pathlib L36–36
+ sys import import sys L37–37
+ timezone import import timezone L38–38
+ register function function register L404–476
+ run_follow function function run_follow L275–290
+ run_post function function run_post L247–272
+ run_profile function function run_profile L357–397
+ run_react function function run_react L337–354
+ run_timeline function function run_timeline L310–334
+ run_unfollow function function run_unfollow L293–307
~ tests/test_social_cli.py .py 80 symbols added
+ TestSocialCliDocstrings class class TestSocialCliDocstrings L503–524
+ test_helper_functions_have_docstrings method method test_helper_functions_have_docstrings L517–524
+ test_module_has_docstring method method test_module_has_docstring L505–507
+ test_run_functions_have_docstrings method method test_run_functions_have_docstrings L509–515
+ TestSocialCliFollow class class TestSocialCliFollow L335–374
+ test_follow_help_exits_zero method method test_follow_help_exits_zero L337–339
+ test_follow_missing_handle_exits_nonzero method method test_follow_missing_handle_exits_nonzero L368–370
+ test_follow_multiple_handles method method test_follow_multiple_handles L352–358
+ test_unfollow_after_follow_removes_file method method test_unfollow_after_follow_removes_file L360–366
+ test_unfollow_help_exits_zero method method test_unfollow_help_exits_zero L341–343
+ test_unfollow_missing_handle_exits_nonzero method method test_unfollow_missing_handle_exits_nonzero L372–374
+ test_write_follow_idempotent method method test_write_follow_idempotent L345–350
+ TestSocialCliPost class class TestSocialCliPost L292–328
+ test_post_help_exits_zero method method test_post_help_exits_zero L294–296
+ test_post_json_has_body method method test_post_json_has_body L308–314
+ test_post_json_output_has_post_id method method test_post_json_output_has_post_id L298–306
+ test_post_missing_body_arg_exits_nonzero method method test_post_missing_body_arg_exits_nonzero L326–328
+ test_post_with_reply_to_stored method method test_post_with_reply_to_stored L316–324
+ TestSocialCliProfile class class TestSocialCliProfile L473–496
+ test_profile_help_exits_zero method method test_profile_help_exits_zero L475–477
+ test_profile_set_updates_bio method method test_profile_set_updates_bio L490–496
+ test_write_profile_content_stored method method test_write_profile_content_stored L484–488
+ test_write_profile_creates_file method method test_write_profile_creates_file L479–482
+ TestSocialCliReact class class TestSocialCliReact L434–466
+ test_react_creates_file_in_reactions_dir method method test_react_creates_file_in_reactions_dir L440–446
+ test_react_different_emojis_different_files method method test_react_different_emojis_different_files L448–455
+ test_react_help_exits_zero method method test_react_help_exits_zero L436–438
+ test_react_missing_args_exits_nonzero method method test_react_missing_args_exits_nonzero L464–466
+ test_react_same_emoji_idempotent method method test_react_same_emoji_idempotent L457–462
+ TestSocialCliShape class class TestSocialCliShape L73–122
+ test_help_lists_follow_subcommand method method test_help_lists_follow_subcommand L84–86
+ test_help_lists_post_subcommand method method test_help_lists_post_subcommand L80–82
+ test_help_lists_profile_subcommand method method test_help_lists_profile_subcommand L100–102
+ test_help_lists_react_subcommand method method test_help_lists_react_subcommand L96–98
+ test_help_lists_timeline_subcommand method method test_help_lists_timeline_subcommand L92–94
+ test_help_lists_unfollow_subcommand method method test_help_lists_unfollow_subcommand L88–90
+ test_module_has_docstring method method test_module_has_docstring L120–122
+ test_register_importable method method test_register_importable L116–118
+ test_run_functions_importable method method test_run_functions_importable L104–114
+ test_social_registered_in_app method method test_social_registered_in_app L75–78
+ TestSocialCliTimeline class class TestSocialCliTimeline L381–427
+ test_load_posts_empty_returns_empty_list method method test_load_posts_empty_returns_empty_list L387–391
+ test_replies_have_reply_to_field method method test_replies_have_reply_to_field L415–427
+ test_timeline_help_exits_zero method method test_timeline_help_exits_zero L383–385
+ test_timeline_limit method method test_timeline_limit L405–413
+ test_timeline_sorted_newest_first method method test_timeline_sorted_newest_first L393–403
+ TestSocialStateHelpers class class TestSocialStateHelpers L129–285
+ test_build_post_body_stored method method test_build_post_body_stored L138–141
+ test_build_post_deterministic method method test_build_post_deterministic L158–162
+ test_build_post_post_id_is_sha256_prefix method method test_build_post_post_id_is_sha256_prefix L143–146
+ test_build_post_reply_to_defaults_none method method test_build_post_reply_to_defaults_none L153–156
+ test_build_post_reply_to_stored method method test_build_post_reply_to_stored L148–151
+ test_build_post_returns_dict_with_required_keys method method test_build_post_returns_dict_with_required_keys L131–136
+ test_delete_follow_nonexistent_is_noop method method test_delete_follow_nonexistent_is_noop L223–228
+ test_delete_follow_removes_file method method test_delete_follow_removes_file L217–221
+ test_load_posts_empty_dir method method test_load_posts_empty_dir L265–268
+ test_load_posts_returns_list method method test_load_posts_returns_list L270–275
+ test_load_posts_sorted_newest_first method method test_load_posts_sorted_newest_first L277–285
+ test_read_profile_returns_none_when_missing method method test_read_profile_returns_none_when_missing L251–255
+ test_write_follow_content_has_handle method method test_write_follow_content_has_handle L209–215
+ test_write_follow_creates_file method method test_write_follow_creates_file L197–200
+ test_write_follow_filename_contains_handle method method test_write_follow_filename_contains_handle L202–207
+ test_write_post_creates_file method method test_write_post_creates_file L164–170
+ test_write_post_file_is_valid_json method method test_write_post_file_is_valid_json L172–179
+ test_write_post_file_under_posts_dir method method test_write_post_file_under_posts_dir L181–187
+ test_write_post_idempotent method method test_write_post_idempotent L189–195
+ test_write_reaction_content_has_emoji method method test_write_reaction_content_has_emoji L235–241
+ test_write_reaction_content_has_post_id method method test_write_reaction_content_has_post_id L243–249
+ test_write_reaction_creates_file method method test_write_reaction_creates_file L230–233
+ test_write_read_profile_roundtrip method method test_write_read_profile_roundtrip L257–263
+ annotations import import annotations L21–21
+ hashlib import import hashlib L23–23
+ json import import json L24–24
+ pathlib import import pathlib L25–25
+ pytest import import pytest L30–30
+ sys import import sys L26–26
+ time import import time L27–27
+ types import import types L28–28
+ invoke_social function function invoke_social L37–53
+ state_dir function function state_dir L61–66
← Older Oldest on task/social-domain-phase01
All commits
Newer → Latest on task/social-domain-phase01

0 comments

No comments yet. Be the first to start the discussion.

To add a comment, use the Muse CLI: muse hub commit comment sha256:5c68c735f0c21219df9f1839e92201af4d2b15f62c4d396f2ea6a5e6b86bb02d --body "your comment"