gabriel / muse public
feat BREAKING task/version-tags-phase0-label-rename #5 / 7
AI Agent gabriel · 22 days ago · Jun 28, 2026 · Diff

feat: phase 0+2 — muse label command + muse tag version tags

Phase 0: rename label annotations from muse tag → muse label - Add muse/cli/commands/label.py with add/list/remove subcommands - JSON output uses label_id, label, labels, label_ids keys - Status strings: labelled / already_labelled - Register muse label in app.py alongside muse tag

Phase 2: reclaim muse tag for protocol-native version tags - Rewrite muse/cli/commands/tag.py with add/list/read/delete/latest - Validates semver (v-prefixed, X.Y.Z[-pre][+build]) - Storage in .muse/version-tags/<tag>.json - Stable-first ordering via semver_key()

Tests: - Update test_cmd_tag.py, test_cmd_tag_hardening.py, test_tag_supercharge.py to use muse label - Add tests/test_cmd_label.py (LB_01–LB_08, 22 tests) - Add tests/test_cmd_version_tags.py (VT_13–VT_25, 50 tests)

sha256:0f032ac8f3a8e412dc51f01636f05c33c087b9a7c8997165ab6561a31c27f7cf sha
+186 ~371 −18 symbols
sha256:3a995b42ce2dcd7c67b55a774fd0d16a2834b98340075fb8303a6669811a3d7c snapshot
+186
symbols added
~371
symbols modified
−18
symbols removed
0
dead code introduced
Semantic Changes 575 symbols
~ muse/cli/commands/label.py .py 41 symbols added
+ _LABEL_FORBIDDEN_RE variable variable _LABEL_FORBIDDEN_RE L77–77
+ _LabelAddJson class class _LabelAddJson L88–95
+ _LabelEntryJson class class _LabelEntryJson L80–85
+ _LabelListJson class class _LabelListJson L98–100
+ _LabelRemoveJson class class _LabelRemoveJson L103–108
+ _MAX_LABEL_LEN variable variable _MAX_LABEL_LEN L76–76
+ _label_namespace function function _label_namespace L111–112
+ _label_to_json function function _label_to_json L130–137
+ _sort_labels function function _sort_labels L140–145
+ _validate_label_name function function _validate_label_name L115–127
+ EnvelopeJson import import EnvelopeJson L70–70
+ ExitCode import import ExitCode L58–58
+ JsonValue import import JsonValue L57–57
+ TagRecord import import TagRecord L62–62
+ TypedDict import import TypedDict L55–55
+ argparse import import argparse L47–47
+ blob_id import import blob_id L57–57 ← moved from muse/cli/commands/tag.py
+ compute_tag_id import import compute_tag_id L62–62
+ datetime import import datetime L48–48
+ delete_tag import import delete_tag L62–62
+ fnmatch import import fnmatch L49–49 ← moved from muse/cli/commands/tag.py
+ get_all_tags import import get_all_tags L62–62
+ get_tags_for_commit import import get_tags_for_commit L62–62
+ json import import json L50–50
+ logging import import logging L51–51
+ make_envelope import import make_envelope L70–70
+ os import import os L52–52 ← moved from muse/cli/commands/tag.py
+ re import import re L53–53 ← moved from muse/cli/commands/tag.py
+ read_current_branch import import read_current_branch L60–60
+ read_repo_id import import read_repo_id L59–59
+ require_repo import import require_repo L59–59
+ resolve_commit_ref import import resolve_commit_ref L61–61
+ sanitize_display import import sanitize_display L71–71
+ start_timer import import start_timer L72–72
+ sys import import sys L54–54
+ write_tag import import write_tag L62–62 ← moved from muse/cli/commands/tag.py
+ logger variable variable logger L74–74
+ register function function register L148–217
+ run_add function function run_add L220–333
+ run_list function function run_list L336–392
+ run_remove function function run_remove L395–476
~ tests/test_cmd_label.py .py 44 symbols added
+ TestLB01LabelAddReturnsLabelId class class TestLB01LabelAddReturnsLabelId L82–109
+ test_add_json_has_label_id method method test_add_json_has_label_id L85–92
+ test_add_json_label_field_present method method test_add_json_label_field_present L102–109
+ test_add_json_label_id_is_sha256 method method test_add_json_label_id_is_sha256 L94–100
+ TestLB02LabelListReturnsLabelsKey class class TestLB02LabelListReturnsLabelsKey L116–148
+ test_list_entry_has_label_field method method test_list_entry_has_label_field L138–148
+ test_list_json_has_labels_key method method test_list_json_has_labels_key L119–127
+ test_list_json_no_tags_key_when_empty method method test_list_json_no_tags_key_when_empty L129–136
+ TestLB03StatusStrings class class TestLB03StatusStrings L155–173
+ test_duplicate_add_status_is_already_labelled method method test_duplicate_add_status_is_already_labelled L166–173
+ test_first_add_status_is_labelled method method test_first_add_status_is_labelled L158–164
+ TestLB04RemoveReturnsLabelIds class class TestLB04RemoveReturnsLabelIds L180–210
+ test_remove_json_has_label_ids method method test_remove_json_has_label_ids L183–191
+ test_remove_json_label_field method method test_remove_json_label_field L193–201
+ test_remove_not_found_has_label_ids method method test_remove_not_found_has_label_ids L203–210
+ TestLB05LabelCommandRegistered class class TestLB05LabelCommandRegistered L217–237
+ test_label_add_exits_zero method method test_label_add_exits_zero L220–224
+ test_label_list_exits_zero method method test_label_list_exits_zero L226–230
+ test_label_remove_exits_zero method method test_label_remove_exits_zero L232–237
+ TestLB06TagNoLongerHandlesLabels class class TestLB06TagNoLongerHandlesLabels L244–266
+ test_tag_add_emotion_label_rejected method method test_tag_add_emotion_label_rejected L253–257
+ test_tag_add_requires_version_format method method test_tag_add_requires_version_format L247–251
+ test_tag_list_returns_tags_key method method test_tag_list_returns_tags_key L259–266
+ TestLB07LabelAndTagCoexist class class TestLB07LabelAndTagCoexist L273–306
+ test_label_dir_is_dot_muse_tags method method test_label_dir_is_dot_muse_tags L294–299
+ test_label_storage_separate_from_version_tags method method test_label_storage_separate_from_version_tags L276–292
+ test_version_tag_dir_is_dot_muse_version_tags method method test_version_tag_dir_is_dot_muse_version_tags L301–306
+ TestLB08LabelRoundTrip class class TestLB08LabelRoundTrip L313–353
+ test_multiple_labels_round_trip method method test_multiple_labels_round_trip L341–353
+ test_round_trip method method test_round_trip L316–339
+ _env function function _env L27–28
+ _init_repo function function _init_repo L31–46
+ _make_commit function function _make_commit L49–75
+ cli variable variable cli L19–19
+ CliRunner import import CliRunner L15–15
+ annotations import import annotations L8–8
+ datetime import import datetime L10–10
+ fake_id import import fake_id L16–16
+ json import import json L11–11
+ muse_dir import import muse_dir L17–17
+ pathlib import import pathlib L12–12
+ pytest import import pytest L14–14
+ ref_path import import ref_path L17–17
+ runner variable variable runner L20–20
~ tests/test_cmd_version_tags.py .py 78 symbols added
+ TestVT13SubcommandRegistration class class TestVT13SubcommandRegistration L90–116
+ test_tag_help_shows_add method method test_tag_help_shows_add L93–96
+ test_tag_help_shows_delete method method test_tag_help_shows_delete L108–111
+ test_tag_help_shows_latest method method test_tag_help_shows_latest L113–116
+ test_tag_help_shows_list method method test_tag_help_shows_list L98–101
+ test_tag_help_shows_read method method test_tag_help_shows_read L103–106
+ TestVT14TagAddSuccess class class TestVT14TagAddSuccess L123–181
+ test_commit_id_present method method test_commit_id_present L152–157
+ test_created_at_is_iso8601 method method test_created_at_is_iso8601 L166–172
+ test_envelope_fields_present method method test_envelope_fields_present L174–181
+ test_semver_fields method method test_semver_fields L141–150
+ test_status_is_tagged method method test_status_is_tagged L126–132
+ test_tag_field method method test_tag_field L134–139
+ test_tag_id_starts_with_sha256 method method test_tag_id_starts_with_sha256 L159–164
+ TestVT15TagAddIdempotency class class TestVT15TagAddIdempotency L188–205
+ test_second_call_preserves_tag_id method method test_second_call_preserves_tag_id L200–205
+ test_second_call_status method method test_second_call_status L191–198
+ TestVT16TagAddValidation class class TestVT16TagAddValidation L212–249
+ test_control_character_rejected method method test_control_character_rejected L245–249
+ test_empty_string method method test_empty_string L223–227
+ test_label_style_rejected method method test_label_style_rejected L237–243
+ test_no_v_prefix method method test_no_v_prefix L215–221
+ test_not_semver method method test_not_semver L229–235
+ TestVT17TagAddMetadata class class TestVT17TagAddMetadata L256–279
+ test_author_propagated method method test_author_propagated L270–279
+ test_message_propagated method method test_message_propagated L259–268
+ TestVT18TagList class class TestVT18TagList L286–324
+ test_empty_list method method test_empty_list L289–295
+ test_entry_has_required_fields method method test_entry_has_required_fields L316–324
+ test_multiple_tags_sorted_latest_first method method test_multiple_tags_sorted_latest_first L306–314
+ test_one_tag method method test_one_tag L297–304
+ TestVT19TagListPreFlag class class TestVT19TagListPreFlag L331–364
+ _setup method method _setup L334–339
+ test_default_excludes_pre_releases method method test_default_excludes_pre_releases L341–347
+ test_pre_flag_includes_pre_releases method method test_pre_flag_includes_pre_releases L349–353
+ test_pre_order_stable_before_rc method method test_pre_order_stable_before_rc L355–364
+ TestVT20TagListHuman class class TestVT20TagListHuman L371–385
+ test_empty_prints_message method method test_empty_prints_message L374–378
+ test_one_tag_printed method method test_one_tag_printed L380–385
+ TestVT21TagRead class class TestVT21TagRead L392–427
+ test_read_existing_tag method method test_read_existing_tag L395–408
+ test_read_has_all_envelope_fields method method test_read_has_all_envelope_fields L419–427
+ test_read_returns_semver method method test_read_returns_semver L410–417
+ TestVT22TagReadNotFound class class TestVT22TagReadNotFound L434–449
+ test_invalid_tag_name_exits_1 method method test_invalid_tag_name_exits_1 L444–449
+ test_nonexistent_exits_1 method method test_nonexistent_exits_1 L437–442
+ TestVT23TagDelete class class TestVT23TagDelete L456–500
+ test_delete_existing method method test_delete_existing L459–467
+ test_delete_invalid_tag_name method method test_delete_invalid_tag_name L495–500
+ test_delete_nonexistent method method test_delete_nonexistent L469–475
+ test_delete_then_list_empty method method test_delete_then_list_empty L486–493
+ test_delete_twice_is_idempotent method method test_delete_twice_is_idempotent L477–484
+ TestVT24TagLatest class class TestVT24TagLatest L507–557
+ test_latest_has_commit_id method method test_latest_has_commit_id L544–550
+ test_latest_has_semver_field method method test_latest_has_semver_field L534–542
+ test_no_tags_null_fields method method test_no_tags_null_fields L552–557
+ test_no_tags_returns_null method method test_no_tags_returns_null L510–515
+ test_returns_highest_stable method method test_returns_highest_stable L525–532
+ test_single_stable_tag method method test_single_stable_tag L517–523
+ TestVT25TagLatestPre class class TestVT25TagLatestPre L564–600
+ test_no_pre_flag_stable_wins method method test_no_pre_flag_stable_wins L576–583
+ test_only_pre_releases_with_flag method method test_only_pre_releases_with_flag L585–592
+ test_only_pre_releases_without_flag_returns_null method method test_only_pre_releases_without_flag_returns_null L594–600
+ test_pre_flag_returns_rc_over_stable method method test_pre_flag_returns_rc_over_stable L567–574
+ _env function function _env L28–29
+ _init_repo function function _init_repo L32–47
+ _make_commit function function _make_commit L50–83
+ CliRunner import import CliRunner L16–16
+ Manifest import import Manifest L18–18
+ annotations import import annotations L9–9
+ content_hash import import content_hash L18–18
+ datetime import import datetime L11–11
+ json import import json L12–12
+ muse_dir import import muse_dir L19–19
+ pathlib import import pathlib L13–13
+ pytest import import pytest L15–15
+ ref_path import import ref_path L19–19
+ runner variable variable runner L21–21
~ muse/cli/commands/tag.py .py 22 symbols added, 18 symbols removed, 6 symbols modified
_MAX_TAG_LEN variable variable _MAX_TAG_LEN L86–86
_TAG_FORBIDDEN_RE variable variable _TAG_FORBIDDEN_RE L90–90
_TagRemoveJson class class _TagRemoveJson L114–119
_sort_tags function function _sort_tags L157–163
_tag_namespace function function _tag_namespace L121–123
_tag_to_json function function _tag_to_json L147–155
_validate_tag_name function function _validate_tag_name L125–145
TagRecord import import TagRecord L71–71
blob_id import import blob_id L66–66 → moved to muse/cli/commands/label.py
compute_tag_id import import compute_tag_id L71–71
delete_tag import import delete_tag L71–71
fnmatch import import fnmatch L58–58 → moved to muse/cli/commands/label.py
get_all_tags import import get_all_tags L71–71
get_tags_for_commit import import get_tags_for_commit L71–71
os import import os L61–61 → moved to muse/cli/commands/label.py
re import import re L62–62 → moved to muse/cli/commands/label.py
write_tag import import write_tag L71–71 → moved to muse/cli/commands/label.py
run_remove function function run_remove L564–677
+ _SemVerJson class class _SemVerJson L106–111
+ _TagDeleteJson class class _TagDeleteJson L150–153
+ _TagLatestJson class class _TagLatestJson L156–163
+ _TagReadJson class class _TagReadJson L140–147
+ _default_author function function _default_author L192–198
+ _is_pre_release function function _is_pre_release L201–202
+ _record_to_entry function function _record_to_entry L180–189
+ _semver_to_json function function _semver_to_json L170–177
+ SemVerTag import import SemVerTag L92–92
+ VersionTagRecord import import VersionTagRecord L82–82
+ _validate_version_tag_name import import _validate_version_tag_name L82–82
+ compute_version_tag_id import import compute_version_tag_id L82–82
+ delete_version_tag import import delete_version_tag L82–82
+ get_config_value import import get_config_value L93–93
+ list_version_tags import import list_version_tags L82–82
+ parse_semver import import parse_semver L92–92
+ read_version_tag import import read_version_tag L82–82
+ semver_key import import semver_key L82–82
+ write_version_tag import import write_version_tag L82–82
+ run_delete function function run_delete L480–523
+ run_latest function function run_latest L530–572
+ run_read function function run_read L417–473
~ tests/test_cmd_tag_hardening.py .py 189 symbols modified
~ _parse
~ _add
~ _ns
~ _v

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:0f032ac8f3a8e412dc51f01636f05c33c087b9a7c8997165ab6561a31c27f7cf --body "your comment"