gabriel / muse public
v0.1.1Stable

Muse v0.1.1 — Server-side semantic analysis

e74bbfd6·snapshot 44aa1e2a72c4… ·Mar 24, 2026
Semantic analysis in progress
MuseHub is computing the symbol graph, API surface, and authorship graph. Refresh in a moment.
📝 Release Notes

Dotfile tracking with sane defaults, .museignore blocklist for tool caches, separation-of-concerns rules enforced across both repos. Semantic release analysis now runs server-side on MuseHub.

Changelog
1 breaking 262
·
Initial extraction from tellurstori/maestro Complete Muse VCS codebase extracted from the Maestro monorepo: - 33 service modules (merge, rebase, drift, checkout, motif, groove-check, etc.) - 85 CLI files (muse_cli package + 73 subcommands) - FastAPI HTTP router for /api/v1/muse/* endpoints - SQLAlchemy ORM models for muse_variations, muse_phrases, muse_note_changes - TourDeForce HTTP client - 96 test files (unit, CLI, e2e, integration) - 6 documentation files (architecture, protocol, reference) Some imports reference maestro.* packages — see README for standalone setup guidance.
12901c5a·cgcardona <gabriel@tellurstori.com>
·
Introduce Muse v2 architecture: domain-agnostic VCS with plugin interface - Add MuseDomainPlugin protocol (domain.py) — five-interface contract for any domain to plug into the Muse DAG engine - Add muse/core/ — file-based VCS engine (store, snapshot, object_store, merge_engine, repo) with no database dependency - Add muse/plugins/music/ — music domain reference implementation - Rewrite muse/cli/ with clean sync commands: init, commit, log, status, diff, show, branch, checkout, merge, reset, revert, cherry-pick, stash, tag - Add .github/workflows/ci.yml — pytest + mypy on PRs to main and dev - Remove FastAPI, SQLAlchemy, asyncpg, tourdeforce HTTP client - Update pyproject.toml: name=muse, version=0.1.1, minimal dependencies - Rewrite README with Muse v2 vision
d87ef453·Gabriel Cardona <gabriel@tellurstori.com>
·
Add spacetime domain to README per Muse v2 doc (#1) Co-authored-by: Gabriel Cardona <gabriel@tellurstori.com>
567a831f·Gabriel Cardona <cgcardona@gmail.com>
·
Replace Maestro-coupled tests with new architecture test suite Replace all legacy tests (which imported SQLAlchemy/maestro and could not collect) with 99 passing tests covering the new muse.core.* and muse.plugins.music.* architecture: - tests/test_core_store.py — file-based commit/snapshot/tag CRUD - tests/test_core_snapshot.py — content-addressed hashing and diffing - tests/test_core_merge_engine.py — three-way merge and base-finding logic - tests/test_cli_workflow.py — end-to-end CLI: init, commit, log, status, branch, checkout, merge, diff, tag, stash - tests/test_music_plugin.py — MuseDomainPlugin reference impl Also fixes two bugs discovered by tests: - branch listing now recurses into subdirs so feature/foo branches appear - music plugin merge no longer treats both-sides-deletion as a conflict
1d9234e8·Gabriel Cardona <gabriel@tellurstori.com>
·
Remove all Maestro legacy code; clean mypy across full muse/ package Deleted: - muse/plugins/music/services/ (35 files — all SQLAlchemy/Pydantic/maestro.*) - muse/cli/commands/emotion_diff.py (Maestro-coupled stub) - muse/cli/commands/groove_check.py (Maestro-coupled stub) - muse/cli/export_engine.py (httpx dependency) - muse/cli/hub_client.py (httpx dependency) - muse/cli/artifact_resolver.py (orphaned async SQLAlchemy helper) Updated: - muse/cli/app.py — removed lazy-import blocks for deleted stubs - pyproject.toml — mypy exclude list now empty; added strict overrides for test files - muse/cli/commands/* — cast _read_repo_id to str; typed stash generics; fixed show.py name collision; fixed cherry_pick.py None guard on parent commit Result: mypy muse/ passes clean (32 files, 0 errors) in ~3 s.
7ba4aa0b·Gabriel Cardona <gabriel@tellurstori.com>
·
Rewrite .cursorignore, .cursorrules, AGENTS.md for Muse v2 Replace AgentCeption-specific content with Muse-accurate rules: - .cursorignore: remove agentception/static refs, add dist/, .mypy_cache/ - .cursorrules: Muse stack, plugin architecture, verification commands, zero-tolerance typing rules, quick-reference table - AGENTS.md: full agent contract — architecture, branch discipline, layer rules, typing enforcement chain, verification checklist, scope of authority, anti-patterns
34096d23·Gabriel Cardona <gabriel@tellurstori.com>
·
Merge dev → main (#2) * Add spacetime domain to README per Muse v2 doc (#1) Co-authored-by: Gabriel Cardona <gabriel@tellurstori.com> * Replace Maestro-coupled tests with new architecture test suite Replace all legacy tests (which imported SQLAlchemy/maestro and could not collect) with 99 passing tests covering the new muse.core.* and muse.plugins.music.* architecture: - tests/test_core_store.py — file-based commit/snapshot/tag CRUD - tests/test_core_snapshot.py — content-addressed hashing and diffing - tests/test_core_merge_engine.py — three-way merge and base-finding logic - tests/test_cli_workflow.py — end-to-end CLI: init, commit, log, status, branch, checkout, merge, diff, tag, stash - tests/test_music_plugin.py — MuseDomainPlugin reference impl Also fixes two bugs discovered by tests: - branch listing now recurses into subdirs so feature/foo branches appear - music plugin merge no longer treats both-sides-deletion as a conflict * Remove all Maestro legacy code; clean mypy across full muse/ package Deleted: - muse/plugins/music/services/ (35 files — all SQLAlchemy/Pydantic/maestro.*) - muse/cli/commands/emotion_diff.py (Maestro-coupled stub) - muse/cli/commands/groove_check.py (Maestro-coupled stub) - muse/cli/export_engine.py (httpx dependency) - muse/cli/hub_client.py (httpx dependency) - muse/cli/artifact_resolver.py (orphaned async SQLAlchemy helper) Updated: - muse/cli/app.py — removed lazy-import blocks for deleted stubs - pyproject.toml — mypy exclude list now empty; added strict overrides for test files - muse/cli/commands/* — cast _read_repo_id to str; typed stash generics; fixed show.py name collision; fixed cherry_pick.py None guard on parent commit Result: mypy muse/ passes clean (32 files, 0 errors) in ~3 s. --------- Co-authored-by: Gabriel Cardona <gabriel@tellurstori.com>
ea1b8894·Gabriel Cardona <cgcardona@gmail.com>
·
Add tools/typing_audit.py — regex + AST violation scanner
cf6e72f3·Gabriel Cardona <gabriel@tellurstori.com>
·
feat: eliminate all Any/object/ignore — strict TypedDicts at every boundary Replace every untyped boundary in the codebase with a named TypedDict: - domain.py: SnapshotManifest, DeltaManifest replace LiveState=Any aliases - store.py: CommitDict, SnapshotDict, TagDict, RemoteCommitPayload; metadata → dict[str,str]; update_commit_metadata value: str - merge_engine.py: MergeStatePayload TypedDict; remove dict[str,object] annotations - config.py: AuthEntry, RemoteEntry, MuseConfig TypedDicts; rewrite _load_config with isinstance narrowing; _dump_toml handles known structure explicitly; remove all # type: ignore - midi_parser.py: MidiMeta, MusicXMLMeta TypedDicts replace dict[str,Any] - stash.py: StashEntry TypedDict replaces list[dict[str,Any]] - plugin.py: use SnapshotManifest/DeltaManifest constructors; direct field access; remove Any import - tests: SnapshotManifest in all test helpers and inline literals - CI: add typing audit step with --max-any 0 ratchet Audit: 0 violations. mypy: clean. Tests: 99/99 passing.
77f04a8f·Gabriel Cardona <gabriel@tellurstori.com>
·
docs: add type-contracts reference with full entity surface and Mermaid diagrams Comprehensive single source of truth for every named type boundary in the Muse VCS codebase — TypedDicts, dataclasses, Protocols, Enums, and TypeAliases — modelled after the AgentCeption type-contracts reference. Covers: domain protocol types (SnapshotManifest, DeltaManifest, MergeResult, DriftReport, MuseDomainPlugin), store wire-format and in-memory types (CommitDict/Record, SnapshotDict/Record, TagDict/Record, RemoteCommitPayload), merge engine (MergeStatePayload, MergeState), config (AuthEntry, RemoteEntry, MuseConfig, RemoteConfig), MIDI import (MidiMeta, MusicXMLMeta, NoteEvent, MuseImportData), stash (StashEntry), and error hierarchy (ExitCode, MuseCLIError, RepoNotFoundError). Includes 8 Mermaid class diagrams covering every entity relationship.
ac0b459d·Gabriel Cardona <gabriel@tellurstori.com>
·
Wire MuseDomainPlugin into all CLI commands via plugin registry All CLI state operations (commit, status, diff, merge, cherry-pick, stash) now dispatch through the MuseDomainPlugin protocol rather than calling core utilities directly. MusicPlugin is the live execution path, not just an isolated artifact. Changes: - muse/plugins/registry.py (new): _REGISTRY dict mapping domain names to plugin instances; resolve_plugin(root) reads domain from repo.json and returns the correct plugin; read_domain() exposed as public helper - muse/cli/commands/init.py: writes "domain": "music" to repo.json; adds --domain flag for future extensibility - muse/cli/commands/commit.py: plugin.snapshot(workdir)["files"] replaces build_snapshot_manifest() - muse/cli/commands/status.py: plugin.drift(committed_snap, workdir) replaces diff_workdir_vs_snapshot() - muse/cli/commands/diff.py: plugin.snapshot() + plugin.diff() replace build_snapshot_manifest() and the inline _print_diff() helper - muse/cli/commands/merge.py: plugin.merge(base, ours, theirs) replaces diff_snapshots() + detect_conflicts() + apply_merge(); conflict paths flow from MergeResult.conflicts directly into write_merge_state() - muse/cli/commands/cherry_pick.py: same plugin.merge() pattern - muse/cli/commands/stash.py: plugin.snapshot(workdir)["files"] replaces build_snapshot_manifest() - muse/domain.py: MergeResult.conflicts is now a list of file paths (not prose); CLI formats its own messages - muse/plugins/music/plugin.py: merge() returns sorted(real_conflicts) as paths - tests/test_plugin_registry.py (new): 12 unit tests for registry lookup, unknown-domain error, and default fallback behaviour - tests/test_cli_plugin_dispatch.py (new): 21 integration tests verifying plugin methods are called through each CLI command using mock.patch; confirms plugin results drive command output - tests/test_music_plugin.py: updated conflict assertion to match path semantics (result.conflicts == ["a.mid"])
a82406f1·Gabriel Cardona <gabriel@tellurstori.com>
·
test: bring core VCS coverage from 60% to 91% Add 5 new test modules covering show, reset, revert, log, and targeted gaps across checkout, tag, commit, diff, stash, branch, and core modules (object_store, repo, store, merge_engine). Exclude future-facing files (config.py, midi_parser.py, models.py) from coverage measurement via [tool.coverage.run] omit rules in pyproject.toml. 263 tests / 263 passing · 91% total coverage
c8984819·Gabriel Cardona <gabriel@tellurstori.com>
·
chore: expand .gitignore (venv, coverage, build, editor artifacts)
f408d081·Gabriel Cardona <gabriel@tellurstori.com>
·
feat: complete MuseDomainPlugin integration — apply(), incremental checkout revert.py: reuse parent commit's snapshot_id directly; eliminates the post-restore re-scan and redundant write_object_from_path calls. Objects are already in the content-addressed store. cherry_pick.py: use merged_manifest from plugin.merge() directly; objects sourced from existing snapshots need no re-write. checkout.py: replace full wipe+restore with incremental delta checkout via plugin.diff() + plugin.apply(). Only files that actually changed are touched — deleted paths are removed, added/modified paths are restored from the object store. plugin.apply() serves as the domain-level post-checkout hook. plugin.py: fix apply() — split the Path and dict code paths clearly. Path case rescans the workdir after physical changes (correct). Dict case applies removals in-memory and documents the limitation that added/modified hashes require target-side data not carried by the delta. All 5 MuseDomainPlugin methods are now wired into CLI commands: snapshot() → commit, stash diff() → diff, checkout (new) merge() → merge, cherry-pick drift() → status apply() → checkout (new) 279 tests / 279 passing · 91% coverage
cc9bbc18·Gabriel Cardona <gabriel@tellurstori.com>
·
docs: full sweep — domain-agnostic rewrite of all docs README.md: fix repository structure tree, package install instructions, and CLI examples (remove legacy groove-check/emotion-diff, add revert/show). docs/architecture/muse-vcs.md: complete rewrite from 8,751-line Maestro/ORM history to a clean 200-line domain-agnostic architecture reference covering the plugin protocol, core engine modules, commit data flow, merge/checkout algorithms, and command map. docs/architecture/muse-e2e-demo.md: rewrite from Docker/HTTP/JWT walkthrough to a plain CLI walkthrough (init → commit → branch → merge → conflict → cherry-pick → revert → stash → tag). docs/architecture/muse_vcs.md: deleted (exact duplicate of muse-vcs.md). docs/protocol/muse-protocol.md: repurposed from Stori-specific variation UX to the language-agnostic MuseDomainPlugin protocol specification, covering all five method contracts, snapshot format, naming conventions, and invariants. docs/protocol/muse-domain-concepts.md: new — answers the terminology question. Defines universal VCS terms, explains what "Variation" currently means and how it could generalize across domains, provides a cross-domain mapping table for music-specific terms (track, region, phrase, section, emotion), and gives guidance for new domain authors. docs/protocol/muse-variation-spec.md: repurposed from full variation UX spec to a scoped music-domain reference, clearly noting it is not part of the core Muse VCS engine. docs/reference/muse-attributes.md: generalized from music-specific examples (drums/*, keys/*) to a domain-agnostic format reference with a genomics example showing the format applies to any domain.
83fa3d6e·Gabriel Cardona <gabriel@tellurstori.com>
·
chore: full decoupling sweep — delete maestro/, scrub all external refs - Delete entire maestro/ directory (123 files) — last trace of the legacy codebase, fully removed from git history going forward - README.md: rewrite Origin section without naming prior projects; strip "(Stori/Maestro)" from variation spec link - AGENTS.md: replace all named-project references with generic phrasing; remove "Cursor" from MCP session description - .cursorrules: same — generic phrasing for project rules and MCP note - pyproject.toml: replace legacy-referencing mypy comment with clean note - muse/core/store.py: drop "replaces SQLAlchemy / Maestro" module docstring - muse/cli/midi_parser.py: replace "Maestro Docker image" install hint with plain pip install instruction - muse/cli/config.py: replace "musehub" example URL with generic host - docs/protocol/muse-variation-spec.md: drop "Stori DAW + Maestro backend" from scope preamble Zero remaining mentions of maestro, agentception, musehub, or named external projects anywhere in tracked source, docs, or config.
ce0ac593·Gabriel Cardona <gabriel@tellurstori.com>
·
feat: Tour de Force stress test + shareable D3 visualization tools/tour_de_force.py - 5-act narrative runner against a real temp Muse repo - Act 1: init + 3 foundation commits on main - Act 2: 3 divergent branches (alpha, beta, gamma) + 5 commits - Act 3: two clean merges (alpha fast-forward + beta 3-way) - Act 4: conflict/left + conflict/right → CONFLICT → resolve + commit - Act 5: cherry-pick, show, diff, stash/pop, revert, tag, log - 41 total operations; captures structured EventRecord + CommitNode DAG - Outputs artifacts/tour_de_force.json + calls renderer tools/render_html.py - Fetches D3.js v7 at render time, embeds inline (self-contained) - Interactive commit DAG: branch-colored nodes, bezier edges, merge-commit double rings, hover tooltips, zoom/pan - Animated replay: Play/Pause/Reset steps through all 41 ops, highlights each commit node as it was created - Operation log panel: terminal-style, per-act grouping, color-coded - Architecture section: plugin flow diagram + MuseDomainPlugin protocol table (all 5 methods with signatures) - Stats bar: commits · branches · merges · conflicts resolved · ops - Dark theme, ~318KB self-contained HTML tools/README.md — usage instructions for both tools .gitignore — add artifacts/ (generated, not committed)
4d0b1365·Gabriel Cardona <gabriel@tellurstori.com>
·
feat: implement .museignore — gitignore-style snapshot exclusion (#7) Adds .museignore support so users can declare which workspace files are never committed, identical to .gitignore syntax. - muse/core/ignore.py: pure parser (load_patterns) and filter (is_ignored) with full gitignore semantics — globs, **, negation, anchored patterns, directory-only patterns, last-rule-wins - MusicPlugin.snapshot(): reads .museignore from repo root and filters excluded paths before hashing; dotfiles remain unconditionally excluded - muse/domain.py: documents the .museignore contract in the MuseDomainPlugin.snapshot() docstring so all domain authors know they must honour it - docs/reference/museignore.md: full format reference with examples for music, genomics, simulation, and spatial domains - tests/test_core_ignore.py: 41 tests covering load_patterns, _matches internals, is_ignored rule evaluation, and end-to-end MusicPlugin.snapshot() integration Co-authored-by: Gabriel Cardona <gabriel@tellurstori.com>
5f1a074d·Gabriel Cardona <cgcardona@gmail.com>
·
chore: remove __pycache__ from git tracking (#9) * feat: implement .museignore — gitignore-style snapshot exclusion (#7) (#8) Adds .museignore support so users can declare which workspace files are never committed, identical to .gitignore syntax. - muse/core/ignore.py: pure parser (load_patterns) and filter (is_ignored) with full gitignore semantics — globs, **, negation, anchored patterns, directory-only patterns, last-rule-wins - MusicPlugin.snapshot(): reads .museignore from repo root and filters excluded paths before hashing; dotfiles remain unconditionally excluded - muse/domain.py: documents the .museignore contract in the MuseDomainPlugin.snapshot() docstring so all domain authors know they must honour it - docs/reference/museignore.md: full format reference with examples for music, genomics, simulation, and spatial domains - tests/test_core_ignore.py: 41 tests covering load_patterns, _matches internals, is_ignored rule evaluation, and end-to-end MusicPlugin.snapshot() integration Co-authored-by: Gabriel Cardona <gabriel@tellurstori.com> * chore: remove __pycache__ from git tracking The .gitignore already listed __pycache__/ and *.pyc, but these files were committed before that rule existed so they showed as perpetual modifications. Untrack them with git rm --cached; they will continue to exist locally but will no longer appear in git status. --------- Co-authored-by: Gabriel Cardona <gabriel@tellurstori.com>
5f5d4912·Gabriel Cardona <cgcardona@gmail.com>
·
feat: .museattributes + multidimensional MIDI merge (#11) * feat: implement .museignore — gitignore-style snapshot exclusion (#7) (#8) Adds .museignore support so users can declare which workspace files are never committed, identical to .gitignore syntax. - muse/core/ignore.py: pure parser (load_patterns) and filter (is_ignored) with full gitignore semantics — globs, **, negation, anchored patterns, directory-only patterns, last-rule-wins - MusicPlugin.snapshot(): reads .museignore from repo root and filters excluded paths before hashing; dotfiles remain unconditionally excluded - muse/domain.py: documents the .museignore contract in the MuseDomainPlugin.snapshot() docstring so all domain authors know they must honour it - docs/reference/museignore.md: full format reference with examples for music, genomics, simulation, and spatial domains - tests/test_core_ignore.py: 41 tests covering load_patterns, _matches internals, is_ignored rule evaluation, and end-to-end MusicPlugin.snapshot() integration Co-authored-by: Gabriel Cardona <gabriel@tellurstori.com> * chore: untrack __pycache__ files (#10) * feat: implement .museignore — gitignore-style snapshot exclusion (#7) Adds .museignore support so users can declare which workspace files are never committed, identical to .gitignore syntax. - muse/core/ignore.py: pure parser (load_patterns) and filter (is_ignored) with full gitignore semantics — globs, **, negation, anchored patterns, directory-only patterns, last-rule-wins - MusicPlugin.snapshot(): reads .museignore from repo root and filters excluded paths before hashing; dotfiles remain unconditionally excluded - muse/domain.py: documents the .museignore contract in the MuseDomainPlugin.snapshot() docstring so all domain authors know they must honour it - docs/reference/museignore.md: full format reference with examples for music, genomics, simulation, and spatial domains - tests/test_core_ignore.py: 41 tests covering load_patterns, _matches internals, is_ignored rule evaluation, and end-to-end MusicPlugin.snapshot() integration Co-authored-by: Gabriel Cardona <gabriel@tellurstori.com> * chore: remove __pycache__ from git tracking (#9) * feat: implement .museignore — gitignore-style snapshot exclusion (#7) (#8) Adds .museignore support so users can declare which workspace files are never committed, identical to .gitignore syntax. - muse/core/ignore.py: pure parser (load_patterns) and filter (is_ignored) with full gitignore semantics — globs, **, negation, anchored patterns, directory-only patterns, last-rule-wins - MusicPlugin.snapshot(): reads .museignore from repo root and filters excluded paths before hashing; dotfiles remain unconditionally excluded - muse/domain.py: documents the .museignore contract in the MuseDomainPlugin.snapshot() docstring so all domain authors know they must honour it - docs/reference/museignore.md: full format reference with examples for music, genomics, simulation, and spatial domains - tests/test_core_ignore.py: 41 tests covering load_patterns, _matches internals, is_ignored rule evaluation, and end-to-end MusicPlugin.snapshot() integration Co-authored-by: Gabriel Cardona <gabriel@tellurstori.com> * chore: remove __pycache__ from git tracking The .gitignore already listed __pycache__/ and *.pyc, but these files were committed before that rule existed so they showed as perpetual modifications. Untrack them with git rm --cached; they will continue to exist locally but will no longer appear in git status. --------- Co-authored-by: Gabriel Cardona <gabriel@tellurstori.com> --------- Co-authored-by: Gabriel Cardona <gabriel@tellurstori.com> * feat: implement .museattributes + multidimensional MIDI merge Muse now understands that a MIDI file has independent orthogonal axes. Two collaborators can change different dimensions of the same file without producing a conflict — the first VCS to do this. Core infrastructure - muse/core/attributes.py: AttributeRule, load_attributes(), resolve_strategy() parse and evaluate .museattributes first-match rules - muse/domain.py: MergeResult gains applied_strategies and dimension_reports fields; MuseDomainPlugin.merge() gains a repo_root keyword argument MIDI dimension engine (muse/plugins/music/midi_merge.py) - extract_dimensions(): parse MIDI bytes → four dimension slices (notes, harmonic, dynamic, structural) each with a SHA-256 content hash - dimension_conflict_detail(): per-dimension change report (unchanged / left_only / right_only / both) for human-readable diagnostics - merge_midi_dimensions(): three-way dimension merge with .museattributes strategy dispatch; reconstructs a valid type-0 MIDI file when all dimension conflicts can be resolved MusicPlugin.merge() (three-pass algorithm) Pass 1: file-level ours/theirs rules from .museattributes Pass 2: dimension-level MIDI merge for .mid files (reads from object store, applies per-dimension strategies, writes new merged object) Pass 3: remaining true conflicts + manual-forced paths CLI - muse merge: passes repo_root to plugin.merge(); reports ✔ auto-resolved paths and dimension-merge detail in output - muse attributes: new command displaying .museattributes rules in a tabular or JSON format Docs - docs/reference/muse-attributes.md: full reference rewritten to document the three-pass algorithm, all five dimension names, per-domain examples Tests: 383 passing (63 new) - tests/test_core_attributes.py: 25 parser and resolver unit tests - tests/test_music_midi_merge.py: 38 MIDI dimension tests covering classification, extraction, conflict detection, merge, reconstruction, strategy dispatch, and path-pattern matching * fix: use AttributeRule type directly in midi_merge.py (mypy list invariance) * fix: replace dict[str, object] and **kwargs: object with strict types (typing audit) --------- Co-authored-by: Gabriel Cardona <gabriel@tellurstori.com>
0e0cbf44·Gabriel Cardona <cgcardona@gmail.com>
·
feat: implement .museignore — gitignore-style snapshot exclusion (#7) (#8) Adds .museignore support so users can declare which workspace files are never committed, identical to .gitignore syntax. - muse/core/ignore.py: pure parser (load_patterns) and filter (is_ignored) with full gitignore semantics — globs, **, negation, anchored patterns, directory-only patterns, last-rule-wins - MusicPlugin.snapshot(): reads .museignore from repo root and filters excluded paths before hashing; dotfiles remain unconditionally excluded - muse/domain.py: documents the .museignore contract in the MuseDomainPlugin.snapshot() docstring so all domain authors know they must honour it - docs/reference/museignore.md: full format reference with examples for music, genomics, simulation, and spatial domains - tests/test_core_ignore.py: 41 tests covering load_patterns, _matches internals, is_ignored rule evaluation, and end-to-end MusicPlugin.snapshot() integration Co-authored-by: Gabriel Cardona <gabriel@tellurstori.com>
ed2c33d0·Gabriel Cardona <cgcardona@gmail.com>
·
chore: untrack __pycache__ files (#10) * feat: implement .museignore — gitignore-style snapshot exclusion (#7) Adds .museignore support so users can declare which workspace files are never committed, identical to .gitignore syntax. - muse/core/ignore.py: pure parser (load_patterns) and filter (is_ignored) with full gitignore semantics — globs, **, negation, anchored patterns, directory-only patterns, last-rule-wins - MusicPlugin.snapshot(): reads .museignore from repo root and filters excluded paths before hashing; dotfiles remain unconditionally excluded - muse/domain.py: documents the .museignore contract in the MuseDomainPlugin.snapshot() docstring so all domain authors know they must honour it - docs/reference/museignore.md: full format reference with examples for music, genomics, simulation, and spatial domains - tests/test_core_ignore.py: 41 tests covering load_patterns, _matches internals, is_ignored rule evaluation, and end-to-end MusicPlugin.snapshot() integration Co-authored-by: Gabriel Cardona <gabriel@tellurstori.com> * chore: remove __pycache__ from git tracking (#9) * feat: implement .museignore — gitignore-style snapshot exclusion (#7) (#8) Adds .museignore support so users can declare which workspace files are never committed, identical to .gitignore syntax. - muse/core/ignore.py: pure parser (load_patterns) and filter (is_ignored) with full gitignore semantics — globs, **, negation, anchored patterns, directory-only patterns, last-rule-wins - MusicPlugin.snapshot(): reads .museignore from repo root and filters excluded paths before hashing; dotfiles remain unconditionally excluded - muse/domain.py: documents the .museignore contract in the MuseDomainPlugin.snapshot() docstring so all domain authors know they must honour it - docs/reference/museignore.md: full format reference with examples for music, genomics, simulation, and spatial domains - tests/test_core_ignore.py: 41 tests covering load_patterns, _matches internals, is_ignored rule evaluation, and end-to-end MusicPlugin.snapshot() integration Co-authored-by: Gabriel Cardona <gabriel@tellurstori.com> * chore: remove __pycache__ from git tracking The .gitignore already listed __pycache__/ and *.pyc, but these files were committed before that rule existed so they showed as perpetual modifications. Untrack them with git rm --cached; they will continue to exist locally but will no longer appear in git status. --------- Co-authored-by: Gabriel Cardona <gabriel@tellurstori.com> --------- Co-authored-by: Gabriel Cardona <gabriel@tellurstori.com>
f28ca9eb·Gabriel Cardona <cgcardona@gmail.com>
·
feat: .museattributes + multidimensional MIDI merge (#12) * feat: implement .museignore — gitignore-style snapshot exclusion (#7) Adds .museignore support so users can declare which workspace files are never committed, identical to .gitignore syntax. - muse/core/ignore.py: pure parser (load_patterns) and filter (is_ignored) with full gitignore semantics — globs, **, negation, anchored patterns, directory-only patterns, last-rule-wins - MusicPlugin.snapshot(): reads .museignore from repo root and filters excluded paths before hashing; dotfiles remain unconditionally excluded - muse/domain.py: documents the .museignore contract in the MuseDomainPlugin.snapshot() docstring so all domain authors know they must honour it - docs/reference/museignore.md: full format reference with examples for music, genomics, simulation, and spatial domains - tests/test_core_ignore.py: 41 tests covering load_patterns, _matches internals, is_ignored rule evaluation, and end-to-end MusicPlugin.snapshot() integration Co-authored-by: Gabriel Cardona <gabriel@tellurstori.com> * chore: remove __pycache__ from git tracking (#9) * feat: implement .museignore — gitignore-style snapshot exclusion (#7) (#8) Adds .museignore support so users can declare which workspace files are never committed, identical to .gitignore syntax. - muse/core/ignore.py: pure parser (load_patterns) and filter (is_ignored) with full gitignore semantics — globs, **, negation, anchored patterns, directory-only patterns, last-rule-wins - MusicPlugin.snapshot(): reads .museignore from repo root and filters excluded paths before hashing; dotfiles remain unconditionally excluded - muse/domain.py: documents the .museignore contract in the MuseDomainPlugin.snapshot() docstring so all domain authors know they must honour it - docs/reference/museignore.md: full format reference with examples for music, genomics, simulation, and spatial domains - tests/test_core_ignore.py: 41 tests covering load_patterns, _matches internals, is_ignored rule evaluation, and end-to-end MusicPlugin.snapshot() integration Co-authored-by: Gabriel Cardona <gabriel@tellurstori.com> * chore: remove __pycache__ from git tracking The .gitignore already listed __pycache__/ and *.pyc, but these files were committed before that rule existed so they showed as perpetual modifications. Untrack them with git rm --cached; they will continue to exist locally but will no longer appear in git status. --------- Co-authored-by: Gabriel Cardona <gabriel@tellurstori.com> * feat: .museattributes + multidimensional MIDI merge (#11) * feat: implement .museignore — gitignore-style snapshot exclusion (#7) (#8) Adds .museignore support so users can declare which workspace files are never committed, identical to .gitignore syntax. - muse/core/ignore.py: pure parser (load_patterns) and filter (is_ignored) with full gitignore semantics — globs, **, negation, anchored patterns, directory-only patterns, last-rule-wins - MusicPlugin.snapshot(): reads .museignore from repo root and filters excluded paths before hashing; dotfiles remain unconditionally excluded - muse/domain.py: documents the .museignore contract in the MuseDomainPlugin.snapshot() docstring so all domain authors know they must honour it - docs/reference/museignore.md: full format reference with examples for music, genomics, simulation, and spatial domains - tests/test_core_ignore.py: 41 tests covering load_patterns, _matches internals, is_ignored rule evaluation, and end-to-end MusicPlugin.snapshot() integration Co-authored-by: Gabriel Cardona <gabriel@tellurstori.com> * chore: untrack __pycache__ files (#10) * feat: implement .museignore — gitignore-style snapshot exclusion (#7) Adds .museignore support so users can declare which workspace files are never committed, identical to .gitignore syntax. - muse/core/ignore.py: pure parser (load_patterns) and filter (is_ignored) with full gitignore semantics — globs, **, negation, anchored patterns, directory-only patterns, last-rule-wins - MusicPlugin.snapshot(): reads .museignore from repo root and filters excluded paths before hashing; dotfiles remain unconditionally excluded - muse/domain.py: documents the .museignore contract in the MuseDomainPlugin.snapshot() docstring so all domain authors know they must honour it - docs/reference/museignore.md: full format reference with examples for music, genomics, simulation, and spatial domains - tests/test_core_ignore.py: 41 tests covering load_patterns, _matches internals, is_ignored rule evaluation, and end-to-end MusicPlugin.snapshot() integration Co-authored-by: Gabriel Cardona <gabriel@tellurstori.com> * chore: remove __pycache__ from git tracking (#9) * feat: implement .museignore — gitignore-style snapshot exclusion (#7) (#8) Adds .museignore support so users can declare which workspace files are never committed, identical to .gitignore syntax. - muse/core/ignore.py: pure parser (load_patterns) and filter (is_ignored) with full gitignore semantics — globs, **, negation, anchored patterns, directory-only patterns, last-rule-wins - MusicPlugin.snapshot(): reads .museignore from repo root and filters excluded paths before hashing; dotfiles remain unconditionally excluded - muse/domain.py: documents the .museignore contract in the MuseDomainPlugin.snapshot() docstring so all domain authors know they must honour it - docs/reference/museignore.md: full format reference with examples for music, genomics, simulation, and spatial domains - tests/test_core_ignore.py: 41 tests covering load_patterns, _matches internals, is_ignored rule evaluation, and end-to-end MusicPlugin.snapshot() integration Co-authored-by: Gabriel Cardona <gabriel@tellurstori.com> * chore: remove __pycache__ from git tracking The .gitignore already listed __pycache__/ and *.pyc, but these files were committed before that rule existed so they showed as perpetual modifications. Untrack them with git rm --cached; they will continue to exist locally but will no longer appear in git status. --------- Co-authored-by: Gabriel Cardona <gabriel@tellurstori.com> --------- Co-authored-by: Gabriel Cardona <gabriel@tellurstori.com> * feat: implement .museattributes + multidimensional MIDI merge Muse now understands that a MIDI file has independent orthogonal axes. Two collaborators can change different dimensions of the same file without producing a conflict — the first VCS to do this. Core infrastructure - muse/core/attributes.py: AttributeRule, load_attributes(), resolve_strategy() parse and evaluate .museattributes first-match rules - muse/domain.py: MergeResult gains applied_strategies and dimension_reports fields; MuseDomainPlugin.merge() gains a repo_root keyword argument MIDI dimension engine (muse/plugins/music/midi_merge.py) - extract_dimensions(): parse MIDI bytes → four dimension slices (notes, harmonic, dynamic, structural) each with a SHA-256 content hash - dimension_conflict_detail(): per-dimension change report (unchanged / left_only / right_only / both) for human-readable diagnostics - merge_midi_dimensions(): three-way dimension merge with .museattributes strategy dispatch; reconstructs a valid type-0 MIDI file when all dimension conflicts can be resolved MusicPlugin.merge() (three-pass algorithm) Pass 1: file-level ours/theirs rules from .museattributes Pass 2: dimension-level MIDI merge for .mid files (reads from object store, applies per-dimension strategies, writes new merged object) Pass 3: remaining true conflicts + manual-forced paths CLI - muse merge: passes repo_root to plugin.merge(); reports ✔ auto-resolved paths and dimension-merge detail in output - muse attributes: new command displaying .museattributes rules in a tabular or JSON format Docs - docs/reference/muse-attributes.md: full reference rewritten to document the three-pass algorithm, all five dimension names, per-domain examples Tests: 383 passing (63 new) - tests/test_core_attributes.py: 25 parser and resolver unit tests - tests/test_music_midi_merge.py: 38 MIDI dimension tests covering classification, extraction, conflict detection, merge, reconstruction, strategy dispatch, and path-pattern matching * fix: use AttributeRule type directly in midi_merge.py (mypy list invariance) * fix: replace dict[str, object] and **kwargs: object with strict types (typing audit) --------- Co-authored-by: Gabriel Cardona <gabriel@tellurstori.com> --------- Co-authored-by: Gabriel Cardona <gabriel@tellurstori.com>
40673172·Gabriel Cardona <cgcardona@gmail.com>
·
Fix JS syntax errors in tour_de_force.html; update type-contracts docs Three literal newlines were embedded inside single-quoted JS string literals (join/split calls), causing a SyntaxError that prevented the DAG visualization from rendering. Replace with the escape sequence \n. Also update docs/reference/type-contracts.md, docs/protocol/muse-protocol.md, and docs/architecture/muse-vcs.md to reflect the MergeResult fields added in the .museattributes implementation (applied_strategies, dimension_reports) and the repo_root keyword added to MuseDomainPlugin.merge(). Two new sections document AttributeRule and the MIDI dimension merge types.
7fd3e008·Gabriel Cardona <gabriel@tellurstori.com>
·
Supercharge tour de force with multidimensional visualization Fix scroll sync: flip DAG to oldest-at-top so both the commit graph and the operation log scroll downward in the same direction during playback. Add Dimension State Matrix — a full-width heatmap showing all 5 musical dimensions (melodic, rhythmic, harmonic, dynamic, structural) across every commit. Colored cells indicate which dimensions changed; a red-bordered cell marks the one dimension that conflicted at the merge commit (structural), while the other four auto-merged cleanly. This makes the multidimensional advantage of Muse immediately legible vs. Git's binary file-level conflicts. Also add: - Per-dimension colored dots on each DAG node (5-dot ring) - Colored dimension pills in the operation log for each commit event - Dimension breakdown in the node hover tooltip - Active column highlight in the dimension matrix synced to playback - Conflict callout: "only 1 of 5 dimensions conflicted" Fix JS SyntaxError: literal newline bytes in join/split string literals replaced with proper \\n escape sequences (surviving regeneration).
9c7c594b·Gabriel Cardona <gabriel@tellurstori.com>
·
Fix dimension matrix showing no colors (hash-based lookup broken) DIM_DATA was keyed by hardcoded commit short IDs from a single run. Every tour regeneration produces fresh SHA-256 hashes, so all lookups returned undefined and no colors rendered. Replace the static hash-to-dims map with getDims(commit) and getConflicts(commit) — message-pattern functions that are stable across re-runs — and call _initDimMaps() at init time to populate DIM_DATA and DIM_CONFLICTS from the live DATA before rendering.
e06e743b·Gabriel Cardona <gabriel@tellurstori.com>
·
Add YouTube narration script for Tour de Force demo Covers: intro (multidimensional music theory), all 5 acts step by step, dimension matrix closing walkthrough, outro/MuseHub teaser, speaker Q&A notes, and suggested YouTube chapter markers.
b9be61e2·Gabriel Cardona <gabriel@tellurstori.com>
·
Add step forward/back buttons and keyboard shortcuts to tour ◀ and ▶ step buttons sit between Play and Reset, letting you navigate the demo one step at a time in either direction without restarting. Keyboard shortcuts: ← / → to step, Space to play/pause. Buttons go disabled at the boundaries (prev at step 0, next at the final step) and update correctly on reset and during auto-play.
0940e0ff·Gabriel Cardona <gabriel@tellurstori.com>
·
Expand Act 5 audition arc in tour script — cherry-pick, revert, roads not taken
b36478f5·Gabriel Cardona <gabriel@tellurstori.com>
·
feat(phase-1): typed delta algebra — replace DeltaManifest with StructuredDelta (#13) Implements Phase 1 of the supercharge plan end-to-end. ## What changed **Core type system (muse/domain.py)** - Remove `DeltaManifest` entirely. - Add `InsertOp`, `DeleteOp`, `MoveOp`, `ReplaceOp`, `PatchOp` TypedDicts — each discriminated by a `Literal["op-name"]` field for mypy narrowing. - Add `StructuredDelta` — `{domain, ops: list[DomainOp], summary}`. - `StateDelta = StructuredDelta` (was `DeltaManifest`). - `MergeResult` gains `op_log: list[DomainOp]`. - `DriftReport.delta` defaults to an empty `StructuredDelta`. - `MuseDomainPlugin.diff()` gains `repo_root: Path | None = None`. **Storage (muse/core/store.py)** - `CommitRecord` and `CommitDict` gain `structured_delta: StructuredDelta | None`. - Stored at commit time; deserialized on read for `muse show`. **Myers LCS MIDI diff (muse/plugins/music/midi_diff.py)** — new module - `NoteKey` TypedDict as the LCS comparison unit (5 fields). - `lcs_edit_script()`: O(nm) LCS DP + traceback → `list[EditStep]`. - `extract_notes()`: MIDI bytes → sorted `list[NoteKey]`. - `diff_midi_notes()`: top-level entry point → `StructuredDelta`. - Content IDs are deterministic SHA-256 of note field tuples. **Music plugin (muse/plugins/music/plugin.py)** - `diff()`: returns `StructuredDelta` with `InsertOp`/`DeleteOp` for file additions/removals, `ReplaceOp` for non-MIDI modifications, and `PatchOp` with note-level `child_ops` for MIDI files when `repo_root` is available. - `apply()`: handles `DeleteOp`/`ReplaceOp`/`InsertOp` in-memory; workdir path rescans as before. - `drift()`: counts op kinds instead of accessing old manifest keys. **CLI commands** - `commit.py`: computes and stores `structured_delta` against parent. - `show.py`: displays `commit.structured_delta` with per-op lines and `PatchOp` child_summary; falls back to manifest diff for old commits. - `diff.py`: calls `plugin.diff(repo_root=root)` and prints structured output. - `status.py`: extracts added/modified/deleted sets from `delta.ops`. - `checkout.py`: extracts removed/to_restore paths from `delta.ops`. **Tests** - `test_structured_delta.py` (new): 38 tests for all op types and plugin behaviour. - `test_midi_diff.py` (new): 29 tests for note extraction, LCS algorithm, and `diff_midi_notes()` end-to-end. - `test_music_plugin.py`: updated to StructuredDelta API. - `test_cli_plugin_dispatch.py`, `test_plugin_apply_and_checkout.py`, `test_cli_coverage_gaps.py`: updated for new delta shape. All 456 tests green. mypy strict: 0 errors. typing_audit --max-any 0: 0 violations. Co-authored-by: Gabriel Cardona <gabriel@tellurstori.com>
800ce22f·Gabriel Cardona <cgcardona@gmail.com>
·
fix: gitignore .muse/ — source tree is not a Muse-managed repo (#14) Co-authored-by: Gabriel Cardona <gabriel@tellurstori.com>
69e6af43·Gabriel Cardona <cgcardona@gmail.com>
·
feat(phase-2): domain schema declaration + diff algorithm library (#15) Implements Phase 2 of the supercharge plan: plugins now declare their data structure schema, and the core engine provides a shared diff algorithm library for free. ## New: muse/core/schema.py - `SequenceSchema`, `TreeSchema`, `TensorSchema`, `SetSchema`, `MapSchema` — five element schema TypedDicts covering the structural primitives of any domain. - `DimensionSpec` — a named semantic sub-dimension with its own schema. - `DomainSchema` — top-level plugin declaration used by the algorithm library and (in Phase 3) the operation-level merge engine. ## New: muse/core/diff_algorithms/ - `lcs.py` — `myers_ses()` / `detect_moves()` / `diff()` on `list[str]` (content IDs). Generic LCS usable by any sequence-schema domain. - `tree_edit.py` — LCS-based labeled ordered tree diff producing `ReplaceOp` (relabel), `InsertOp`, `DeleteOp`, `MoveOp`. Defines `TreeNode` frozen dataclass. - `numerical.py` — epsilon-tolerant sparse / block / full tensor diff on `list[float]`. - `set_ops.py` — hash-set algebra for unordered collections of content IDs. - `__init__.py` — `diff_by_schema()` dispatch + typed input containers (`SequenceInput`, `SetInput`, `TensorInput`, `MapInput`, `TreeInput`). ## Updated: muse/domain.py - `MuseDomainPlugin` gains a sixth method: `schema() -> DomainSchema`. ## Updated: muse/plugins/registry.py - `schema_for(domain) -> DomainSchema | None` — schema lookup without requiring a plugin instance. ## Updated: muse/plugins/music/plugin.py - `MusicPlugin.schema()` returns the full four-dimension `DomainSchema` (melodic/SequenceSchema, harmonic/SequenceSchema, dynamic/TensorSchema, structural/TreeSchema). ## Updated: muse/plugins/music/midi_diff.py - `lcs_edit_script()` is now a thin adapter over `lcs.myers_ses()` from the core library — music domain no longer has its own LCS copy. - `diff_midi_notes()` delegates the SES step to the shared core algorithm. ## New: tests/test_diff_algorithms.py — 54 tests ## New: tests/test_domain_schema.py — 20 tests Verification: mypy 0 errors · typing_audit 0 violations · 530 tests green Co-authored-by: Gabriel Cardona <gabriel@tellurstori.com>
c5b7bd6b·Gabriel Cardona <cgcardona@gmail.com>
·
docs: fix stale 'five interfaces' count in domain.py and music plugin; add Phase 2 completion checklist to supercharge plan
986c055e·Gabriel Cardona <gabriel@tellurstori.com>
·
feat(phase-3): operation-level merge engine — OT-based auto-merge for non-conflicting ops (#16) Implements Phase 3 of the supercharge plan: the merge engine now reasons over DomainOp trees rather than file-path sets, enabling sub-file auto-merge. Core OT engine (muse/core/op_transform.py) - MergeOpsResult dataclass with merged_ops, conflict_ops, is_clean - ops_commute() — commutativity oracle for all 25 DomainOp type pairs - transform() — position-adjusted (a', b') for commuting InsertOp pairs - _adjust_insert_positions() — counting-formula position adjustment for multiple concurrent insertions (correct for any N concurrent inserts) - merge_op_lists() — three-way merge at operation granularity: kept, consensus, and exclusive additions with OT conflict detection - merge_structured() — StructuredDelta convenience wrapper Domain protocol extension (muse/domain.py) - StructuredMergePlugin sub-protocol (runtime_checkable) with merge_ops() - Updated module docstring to document Phase 3 design Music plugin (muse/plugins/music/plugin.py + midi_diff.py) - MusicPlugin.merge_ops() — full reference implementation: - OT conflict classification via merge_op_lists - File-level ops (Insert/Delete/Replace) applied directly to manifest - PatchOps: one-side-only → take that side's content hash; both-sides with commuting child ops → MIDI note-level reconstruction - .museattributes strategy resolution for conflicting pairs - _merge_patch_ops() — loads base/ours/theirs MIDI from object store, builds content_id→NoteKey lookups, applies merged note ops, calls reconstruct_midi() and stores result - _note_content_id() — shared hash function for NoteKey identity - reconstruct_midi() in midi_diff.py — Type 0 MIDI writer (inverse of extract_notes), sorts events by tick, handles note_off ordering - isinstance(plugin, StructuredMergePlugin) assertion added CLI merge command (muse/cli/commands/merge.py) - Phase 3 dispatch: checks isinstance(plugin, StructuredMergePlugin) - Calls diff() for both branches then merge_ops(); falls back to merge() automatically for plugins without structured merge support Tests - tests/test_op_transform.py — 82 new tests: commutativity oracle (all 25 type pairs), OT transform diamond property, counting formula, merge_op_lists (3-way), merge_structured, MergeOpsResult - tests/test_core_merge_engine.py — 20 new tests: TestMergeStructuredIntegration and TestStructuredMergePluginProtocol classes Verification: mypy 0 errors, typing_audit 0 violations, 612 tests green Co-authored-by: Gabriel Cardona <gabriel@tellurstori.com>
53d2d9ce·Gabriel Cardona <cgcardona@gmail.com>
·
docs: fill docstring gaps identified in Phase 3 audit - DeleteOp, MoveOp, ReplaceOp: document every field (consistent with InsertOp) - MergeResult.is_clean: add property docstring - write_merge_state(): add Args block for all 6 keyword-only params - apply_resolution(): document destructive muse-work/ side-effect in docstring - diff_snapshots(): add Args/Returns block - apply_merge(): add full Args/Returns with merge semantics prose - MusicPlugin.merge(): add Args/Returns block; clarify left/right vs ours/theirs - _diff_modified_file(): add full Args/Returns with fallback condition - _read_branch, _read_repo_id, _restore_from_manifest: add one-liner docstrings
a55ffe59·Gabriel Cardona <gabriel@tellurstori.com>
·
feat(phase-4): CRDT semantics for convergent multi-agent writes (691 tests green) Implements the full Phase 4 CRDT primitive library and protocol: CRDT primitives (muse/core/crdts/): - VectorClock: causal ordering with increment, merge, happens_before, concurrent_with - LWWRegister: last-write-wins scalar with timestamp+author tiebreak - ORSet: observed-remove set with add-wins property and token tracking - RGA: replicated growable array with parent_id-tracked commutative join - AWMap: add-wins key-value map - GCounter: grow-only counter with per-agent slots Protocol extensions (muse/domain.py): - CRDTSnapshotManifest TypedDict: files + vclock + crdt_state + schema_version - CRDTPlugin @runtime_checkable protocol: crdt_schema(), join(), to_crdt_state(), from_crdt_state() Schema (muse/core/schema.py): - CRDTDimensionSpec TypedDict: per-dimension CRDT type declaration - CRDTPrimitive literal union: "lww_register" | "or_set" | "rga" | "aw_map" | "g_counter" Merge engine (muse/core/merge_engine.py): - crdt_join_snapshots(): Phase 4 entry point — always succeeds, no conflicts Tests (tests/test_crdts.py): - 79 new tests covering all three lattice laws (commutativity, associativity, idempotency) for every CRDT primitive, plus serialisation and merge engine integration Docs (docs/architecture/supercharge-plan.md): - Phase 4 marked complete; completion checklist added Verification: mypy zero errors, typing_audit zero violations, 691 tests green Co-authored-by: Gabriel Cardona <gabriel@tellurstori.com>
4e224376·Gabriel Cardona <cgcardona@gmail.com>
·
feat: comprehensive docs, scaffold plugin, and muse domains dashboard (#20) Architecture docs: - Update docs/architecture/muse-vcs.md to v1.0 covering all four supercharge phases with the full protocol stack, algorithm selection table, CRDT primitive reference, and CLI command map New guides: - docs/guide/plugin-authoring-guide.md — complete walkthrough for building a new domain plugin from Phase 1 through Phase 4, with tested code examples for every method, schema declaration, OT merge, CRDT implementation, and verification checklist - docs/guide/crdt-reference.md — deep-dive CRDT primer covering the three lattice laws, all six primitives with full API reference and concurrency examples, composition patterns, and when not to use CRDTs Scaffold plugin: - muse/plugins/scaffold/plugin.py — fully typed, mypy-strict, Phase 1–4 capable copy-paste domain template; every method is documented with contract, args, and returns; TODO markers guide exactly what to fill in - muse/plugins/scaffold/__init__.py — package init with usage instructions muse domains command: - muse/cli/commands/domains.py — domain plugin dashboard listing all registered domains, their Phase 1–4 capability levels, schema details, and active repo marker; --new <name> scaffolds a new plugin directory; --json emits machine-readable output for scripting - Registered scaffold plugin in registry.py so it appears in the dashboard Tour de Force update: - docs/demo/tour-de-force-script.md — adds Acts 6–9 covering Typed Delta Algebra, Domain Schema & diff algorithms, OT merge, and CRDT semantics; new CRDT live examples, ORSet semantics demo, and updated YouTube chapter markers; original 41 steps unchanged All: mypy clean, typing audit 0 violations, 691 tests green. Co-authored-by: Gabriel Cardona <gabriel@tellurstori.com>
1e566283·Gabriel Cardona <cgcardona@gmail.com>
·
fix: config and versioning audit — TOML attributes, v0.1.1, no Phase N labels - Versioning: all docs updated from v1.0 to v0.1.1; pyproject.toml unchanged - Delete docs/architecture/supercharge-plan.md (work shipped, artifact retired) - Replace every "Phase N" planning label in Python docstrings with descriptive architectural names (Typed Delta Algebra, Domain Schema, OT merge, CRDT) - .museattributes migrated from whitespace-delimited 3-column format to TOML: new AttributesMeta, AttributesRuleDict, MuseAttributesFile TypedDicts; [meta] domain validated against repo domain at merge time; source_line → source_index; init writes TOML template; all tests rewritten for TOML - config.toml: [domain] section added to template and _load_config parser; MuseConfig gains domain: dict[str, str] field - muse attributes command: displays [meta] domain header and source_index in JSON - MusicPlugin.load_attributes calls pass domain=_DOMAIN_TAG for validation - muse-protocol.md: schema() added as 6th required method; StructuredMergePlugin and CRDTPlugin documented as optional extensions with full contract tables - muse-attributes.md: full rewrite for TOML format with examples and template - type-contracts.md: StructuredDelta, DomainOp variants, DomainSchema, CRDTSnapshotManifest, MergeOpsResult, MuseAttributesFile all documented; MuseDomainPlugin updated to 6 methods; MuseConfig domain field added - README: legacy v1/v2 framing removed; repo structure map updated; six-method protocol with StructuredMergePlugin and CRDTPlugin shown - mypy: 0 errors | typing_audit: 0 violations | pytest: 697/697 passed Co-authored-by: Gabriel Cardona <gabriel@tellurstori.com>
45fd2148·Gabriel Cardona <cgcardona@gmail.com>
·
feat: Python 3.12 baseline, dep refresh, and docs navigation index Raises the minimum Python version to 3.12 and adopts the idiomatic PEP 695 `type X = Y` statement for all type alias declarations. Dep changes ----------- - requires-python: >=3.11 → >=3.12 - typer: >=0.9.0 → >=0.14.0 (0.24.1 latest, drops 3.9 support) - mido: >=1.3.0 → >=1.3.3 (latest stable) - Dropped `toml` — unused since we switched to stdlib `tomllib` - pytest: >=8.0.0 → >=9.0.0 - pytest-asyncio:>=0.23.0 → >=1.0.0 - pytest-cov: >=4.1.0 → >=7.0.0 - anyio: >=4.2.0 → >=4.9.0 - mypy: >=1.8.0 → >=1.19.0 - hatchling build dep pinned to >=1.29.0 - mypy `python_version` updated from 3.11 to 3.12 - Dropped `toml` from mypy overrides (no longer a dependency) Python 3.12 type alias syntax (PEP 695) ---------------------------------------- Converted all bare `X = Y` type alias declarations to the explicit `type X = Y` statement: - muse/domain.py: DomainOp, LiveState, StateSnapshot, StateDelta - muse/plugins/music/midi_merge.py: _MsgVal - muse/cli/midi_parser.py: RawMeta Docs ---- Added docs/README.md — a navigation index covering every document in the docs tree, a directory map, architecture overview, plugin quickstart, config file inventory, and testing command reference. Verification: mypy (0), typing_audit (0), pytest (697/697 green)
5a7035e3·Gabriel Cardona <gabriel@tellurstori.com>
·
feat: live Acts 6-9 in Tour de Force, drop Phase N labels everywhere Tour de Force (tools/tour_de_force.py) --------------------------------------- - Acts 6-9 now run real code — not narration stubs. Act 6 (Typed Delta Algebra): `muse show` + `muse show --json` + `muse log --stat` capture live StructuredDelta output. Act 7 (Domain Schema): `muse domains`, `muse domains --json`, and `muse domains --new genomics` (scaffold + auto-cleanup). Act 8 (OT Merge): two scenarios — independent InsertOps commute to a clean merge; identical-address ReplaceOps produce a genuine conflict. Act 9 (CRDT Primitives): direct API demo of ORSet add-wins, LWWRegister last-write-wins, GCounter monotonic join, and VectorClock causal ordering — all with real assertions. - Total tour: 69 operations in ~260ms (was 41). - Fixed type: ignore[union-attr] in render_html loading — replaced importlib.util dynamic spec loading with a clean sys.path + import. - Added ot-left/ot-right/ot-conflict-l/ot-conflict-r to BRANCH_COLORS. - ACT_TITLES dict extended with acts 6-9. domains.py ---------- - _CapabilityLevel → _CapabilityLabel; labels changed from "Phase N" to "Typed Deltas" / "Domain Schema" / "OT Merge" / "CRDT". - Updated module docstring, _capabilities docstring, and the `domains` command help text to use the new descriptive labels. Narration script (docs/demo/tour-de-force-script.md) ----------------------------------------------------- - Stripped "(Phase N)" suffix from all Act 6-9 headings. - Updated `muse domains` example output to show descriptive capability labels matching the new domains.py output. - OUTRO: "five-method" → "six-method"; "Phase 1/2/3/4" bullets replaced with "Typed Deltas / Domain Schema / OT Merge / CRDT Semantics". - Appendix Q&A: "Phase 3 OT and Phase 4 CRDT" → "OT Merge and CRDT Semantics"; test count updated to 697. - Chapter markers: stripped Phase N labels; "Muse v1.0" → "Muse v0.1.1". Verification: mypy (0), typing_audit (0), pytest (697/697 green)
d76aac35·Gabriel Cardona <gabriel@tellurstori.com>
·
fix: bump CI Python from 3.11 to 3.12 pyproject.toml requires-python was raised to >=3.12 and PEP 695 type aliases (type X = Y) were introduced in domain.py, midi_merge.py, and midi_parser.py — all of which are SyntaxErrors on Python 3.11. CI was still pinned to 3.11, causing every job to fail at collection.
3a3610b1·Gabriel Cardona <gabriel@tellurstori.com>
·
fix: five → six methods in tour-de-force Q&A appendix The MuseDomainPlugin protocol has six required methods (snapshot, diff, merge, drift, apply, schema). The appendix answer to "what does domain-agnostic mean?" still said five.
5d076129·Gabriel Cardona <gabriel@tellurstori.com>
·
fix: six methods everywhere — render_html, plugin guide, type-contracts, AGENTS - render_html.py: 'Implement 5 methods' → '6 methods' in the protocol box and the YourPlugin tile; add missing schema() row to the protocol table; '5 methods' → '6 methods' in the explainer prose. - docs/guide/plugin-authoring-guide.md: 'these five methods' → 'six' - docs/reference/type-contracts.md: ': 5 methods' → ': 6 methods' in the type tree; 'five methods' → 'six methods' in Diagram 1 caption. - AGENTS.md: 'the five-method contract' → 'the six-method contract' The sixth method is schema(), added when Domain Schema support was introduced. All remaining 'five' instances in the codebase refer to musical dimensions (5) or NoteKey fields (5), which are correct.
54c5e50d·Gabriel Cardona <gabriel@tellurstori.com>
·
fix: add schema() to every protocol method enumeration in docs - plugin-authoring-guide.md checklist: 'snapshot, diff, merge, drift, apply' → '...apply, schema all implemented' - type-contracts.md Mermaid class body: same — add / schema - artifacts/tour_de_force.html: regenerated from the already-fixed render_html.py; HTML now shows schema() row in the protocol table and '6 methods' everywhere (artifacts/ is gitignored so not staged)
d96a98a5·Gabriel Cardona <gabriel@tellurstori.com>
·
feat: supercharge Tour de Force acts 6-9 and build domain registry page (#28) Tour de Force (render_html.py): - Add act-jump navigation bar with per-act buttons and Reveal All - Acts 6-9 events now get 15-line output (was 5) and rich-act class - Act headers styled with per-act icon + color, always visible (not dimmed) - CRDT events (Act 9) rendered as API calls, not shell commands - buildDomainSection(): domain registry cards built from live Act 7 JSON - buildCRDTSection(): four CRDT primitive cards built from Act 9 events - Add Domain Dashboard + CRDT sections to HTML page below architecture - Add "Domain Registry →" cross-link in header - Cross-link from domain_registry.html back to tour_de_force.html Domain Registry (render_domain_registry.py — new tool): - Standalone page: "Version Anything · Domain Plugin Registry" - Animated domain ticker (music → genomics → 3D → financial → yours) - Six-method protocol table with stat strip (6 methods, 14 commands, 0 core changes) - "Build in Three Steps" section (scaffold → implement → use) - Highlighted scaffold code block with full GenomicsPlugin stub - Live domain cards from muse domains --json (falls back to static data) - Planned ecosystem cards (Genomics, 3D/Spatial, Financial, Simulation, YourDomain) - Three-tier distribution model (Local → pip package → MuseHub) - MuseHub teaser section with features and animated status dot - Nav links between Tour de Force and Domain Registry pages Co-authored-by: Gabriel Cardona <gabriel@tellurstori.com>
90e5a0eb·Gabriel Cardona <cgcardona@gmail.com>
·
refactor: Tour de Force is Acts 1-5 only; engine capabilities move to domain registry (#29) Tour de Force (tour_de_force.py, render_html.py): - Strip Acts 6-9 entirely — demo is now a clean 5-act music story (41 ops) - Remove OT-branch BRANCH_COLORS and act 6-9 ACT_TITLES entries - Remove buildDomainSection(), buildCRDTSection(), RICH_ACTS, CRDT_ACT JS - Replace domain/CRDT sections with a single registry callout banner ("Want to version something else? → Domain Registry & Plugin Guide") - Act jump bar, reveal-all, per-act icons still work for acts 1-5 Domain Registry (render_domain_registry.py): - Add _compute_crdt_demos() — runs ORSet/LWWRegister/GCounter/VectorClock live - Add _TYPED_DELTA_EXAMPLE and _OT_MERGE_EXAMPLE static accurate examples - Add Engine Capabilities section (4 full-width cards before the Build section): - Typed Delta Algebra: muse show --json output explained - Domain Schema: live domain cards from muse domains --json - OT Merge: both scenarios (commuting inserts + same-address conflict) - CRDT Primitives: four live demo cards computed at render time - Add cap-showcase-grid CSS with card, header, badge, output styles Co-authored-by: Gabriel Cardona <gabriel@tellurstori.com>
9a17aeee·Gabriel Cardona <cgcardona@gmail.com>
·
feat: add GitHub Pages deployment — landing page + CI build workflow (#30) - tools/render_index.py: generates artifacts/index.html — a beautiful dark-theme landing page presenting Tour de Force and Domain Registry as two CTA cards, with a feature strip and nav linking to GitHub and the plugin guide - .github/workflows/pages.yml: on every push to main, runs tour_de_force.py → tour_de_force.html render_domain_registry.py → domain_registry.html render_index.py → index.html then deploys all three to GitHub Pages via actions/deploy-pages@v4 - GitHub Pages enabled at https://cgcardona.github.io/muse/ (source: Actions) Co-authored-by: Gabriel Cardona <gabriel@tellurstori.com>
b7f3f7bd·Gabriel Cardona <cgcardona@gmail.com>
·
feat: domain registry is the landing page — muse / Version Anything hero (#31) - Hero redesigned: large mono "muse" wordmark (gradient blue→purple), "Version Anything" as the subtitle in light uppercase tracking, removing the previous eyebrow + gradient h1 pattern - render_domain_registry.py now writes both domain_registry.html AND index.html (identical) so the registry IS the landing page at / - Page title simplified to "Muse — Version Anything" - Remove tools/render_index.py (superseded — no longer a separate page) - pages.yml: drop the render_index.py step (one fewer build step) Co-authored-by: Gabriel Cardona <gabriel@tellurstori.com>
ffec191c·Gabriel Cardona <cgcardona@gmail.com>
·
fix: wordmark optical weight — UI font + balanced gradient (#32) Switch wordmark from monospace to system UI font: monospace fonts pack three strokes into 'm' at display sizes, making it read lighter than single-stroke neighbors. UI fonts are optically balanced for large weights. Change gradient from 135deg blue→purple to 90deg (horizontal) across a tighter perceptual range (#6ea8fe → #a78bfa → #c084fc) so all four letters sit at similar luminosity on a dark background. Scale 'Version Anything' subtitle down slightly and increase letter-spacing from 2px to 4px for better proportion against the larger wordmark. Co-authored-by: Gabriel Cardona <gabriel@tellurstori.com>
9abeaf80·Gabriel Cardona <cgcardona@gmail.com>
·
fix: VERSION ANYTHING — white, 700 weight, 26px, 6px tracking (#33) Co-authored-by: Gabriel Cardona <gabriel@tellurstori.com>
b43ae542·Gabriel Cardona <cgcardona@gmail.com>
·
fix: rename Tour de Force → Demo in all nav labels and page titles (#34) Co-authored-by: Gabriel Cardona <gabriel@tellurstori.com>
047c2a69·Gabriel Cardona <cgcardona@gmail.com>
·
fix: link MuseHub status line to github.com/cgcardona/musehub (#35) Co-authored-by: Gabriel Cardona <gabriel@tellurstori.com>
438b35f5·Gabriel Cardona <cgcardona@gmail.com>
·
fix: collapse stats into left column beside protocol table (#36) Co-authored-by: Gabriel Cardona <gabriel@tellurstori.com>
48a13b74·Gabriel Cardona <cgcardona@gmail.com>
·
feat: add inline JSON syntax highlighting to Typed Delta card (#37) Co-authored-by: Gabriel Cardona <gabriel@tellurstori.com>
5a35ce91·Gabriel Cardona <cgcardona@gmail.com>
·
feat: replace all emoji with inline SVG icons (Lucide style) (#38) Co-authored-by: Gabriel Cardona <gabriel@tellurstori.com>
6b8c455f·Gabriel Cardona <cgcardona@gmail.com>
·
feat: replace OT Merge pre-block with structured scenario cards (#39) Co-authored-by: Gabriel Cardona <gabriel@tellurstori.com>
9d134bb2·Gabriel Cardona <cgcardona@gmail.com>
·
feat: add Diff Algebra visualization section — five algorithm panels + StructuredDelta flow (#40) Co-authored-by: Gabriel Cardona <gabriel@tellurstori.com>
9f8e681d·Gabriel Cardona <cgcardona@gmail.com>
·
feat: replace CRDT pre blocks with purpose-built convergence visualizations Each of the four CRDTs in the Domain Registry now renders as a compact HTML diagram showing its specific convergence story rather than raw text output (which was overflowing its container): - ORSet: side-by-side concurrent add/remove replicas → join result with "add-wins" annotation - LWWRegister: stacked write timeline with winner badge → commutative join result - GCounter: proportional bar chart per agent slot → component-wise max total - VectorClock: grid of clock cells (self/zero/max) with ⊕ symbol → concurrent_with badge and merged clock All values remain live-computed from _compute_crdt_demos(); the HTML visualization is generated alongside the existing text output field and selected via html_output key in _render_capability_card.
86ea1e94·Gabriel Cardona <gabriel@tellurstori.com>
·
fix: prevent ORSet Replica B box from clipping in CRDT grid Add min-width:0 + overflow:hidden to .crdt-rep grid children so CSS grid constraint is respected, and text-overflow:ellipsis to .crdt-op so long remove() calls degrade gracefully instead of escaping the cell.
39b87a7a·Gabriel Cardona <gabriel@tellurstori.com>
·
fix: add rhythmic as 5th music domain dimension everywhere The MIDI merge has always recognised five user-facing dimensions (melodic, rhythmic, harmonic, dynamic, structural) via DIM_MAP in midi_merge.py, but schema(), its docstring, the Domain Registry card, and the test fixture all listed only four. - plugin.py schema(): add DimensionSpec(rhythmic) with SequenceSchema matching melodic; note the shared internal notes bucket in description - plugin.py docstrings: update "four" → "five" in schema() and merge() - render_domain_registry.py: add rhythmic row to the music domain card - test_domain_schema.py: rename test_schema_has_four_dimensions → test_schema_has_five_dimensions; extend expected names list
9e852bc1·Gabriel Cardona <gabriel@tellurstori.com>
·
refactor: reorder Domain Registry page sections into narrative arc Old order: Protocol → Diff Algebra → Capabilities → Build → Code → Registry → Ecosystem → Distribution → MuseHub New order: Protocol → Registry → Ecosystem → Capabilities → Diff Algebra → Build → Code → Distribution → MuseHub Narrative logic: 1. Protocol — the pitch: 6 methods, here's the contract 2. Registry — proof: it works today (music domain live) 3. Ecosystem — vision: here's where it's going 4. Capabilities — what you get for free (Typed Deltas, OT, CRDT) 5. Diff Algebra — deep dive for the curious (how it works inside) 6. Build — call to action: build your own in 3 steps 7. Code — scaffold detail 8. Distribution — how to share what you build 9. MuseHub — ecosystem endgame
105a26c4·Gabriel Cardona <gabriel@tellurstori.com>
·
fix: replace relative doc links with GitHub blob URLs ../docs/guide/*.md paths are dead on GitHub Pages since the docs/ directory is not part of the deployed artifact. Point all four occurrences (nav, code section ×2, footer) directly at the canonical GitHub blob URLs on main.
d3ff983b·Gabriel Cardona <gabriel@tellurstori.com>
·
fix: rename tour_de_force.html → demo.html and update all references - tools/tour_de_force.py: update docstring and output path - tools/render_domain_registry.py: update nav, hero CTA, and footer links - tools/README.md: update artifact table and open commands - docs/demo/tour-de-force-script.md: update file path in setup note - artifacts/tour_de_force.html: git mv'd to artifacts/demo.html
a495caeb·Gabriel Cardona <gabriel@tellurstori.com>
·
fix: remove Domain Registry link from demo page header
c338c089·Gabriel Cardona <gabriel@tellurstori.com>
·
fix: remove How Muse Works and Domain Registry callout from demo Both sections duplicate content now on the landing page (index.html). The demo page stays focused on the live VCS walkthrough.
b8d31584·Gabriel Cardona <gabriel@tellurstori.com>
·
fix: correct broken TOC anchors in plugin-authoring-guide.md Phase N prefixes were stripped from section headings in a prior commit but the TOC links were left pointing at the old slugs: #phase-1--core-protocol-required → #core-protocol-required #phase-2--domain-schema → #domain-schema #phase-3--operation-level-merge → #operation-level-merge-ot #phase-4--crdt-semantics → #crdt-semantics
e56564ca·Gabriel Cardona <gabriel@tellurstori.com>
·
refactor: use shared nav header on demo page Replace the bespoke header-top/tagline/version-badge layout with the same nav bar used on the landing page: muse · Demo (active) · Domain Registry · Plugin Guide · v{VERSION} Remove now-dead CSS: .header-top, header h1, .tagline, .header-nav-link, .version-badge. Trim stats-bar top padding to 16px (nav provides visual separation). The v{{VERSION}} token in the nav is still live-interpolated from the build data.
ed16b306·Gabriel Cardona <gabriel@tellurstori.com>
·
feat: replace all emojis on demo page with SVG icons Define a JS SVG icon library (music, branch, merge, conflict, revert, check, dot, zap, pause, eye) and use it throughout: - ACT_ICONS: 🎵🌿⚡🔀⏪ → music/branch/merge/conflict/revert SVGs - Dimension tooltip pills: ● → dot SVG, ⚡ → zap SVG - Event log conflict dim-pills: ⚡ → zap SVG - Dimension matrix cells: textContent ⚡/● → innerHTML zap/dot SVG - Play button: ⏸/✓ → pause/check SVGs (textContent → innerHTML) - Reveal All button: ✦ → eye SVG (textContent → innerHTML) - HTML conflict note: ⚡ → alert-triangle SVG, ✓ → check SVG Add .ico-inline, .ico-conflict, .ico-check CSS for the static HTML icons.
4b2cbea9·Gabriel Cardona <gabriel@tellurstori.com>
·
fix: remove Domain Registry from nav — muse logo already links home The logo links to index.html on both pages so the explicit Domain Registry nav item is redundant. Nav is now: muse | Demo | Plugin Guide | v0.1.1
9cdb32a8·Gabriel Cardona <gabriel@tellurstori.com>
·
feat: code-domain semantic commands + code tour de force demo (#54) * feat: add code domain plugin with AST-based semantic versioning Introduces muse/plugins/code/ — a first-class Muse plugin that treats source code as a structured system of named symbols rather than lines of text. Key capabilities ---------------- - Python AST parsing via stdlib `ast` (zero new dependencies). Every function, class, method, variable, and import becomes a named symbol with a stable content-addressed identity (SHA-256 of normalized AST). - Semantic content IDs: two functions that differ only in whitespace or comments share the same content_id; reformatting a file produces no structured delta. - Rename detection: same body_hash + different name → ReplaceOp annotated "renamed to <new>". - Move detection: same content_id at a different address across files → cross-file move annotation on the DeleteOp/InsertOp pair. - Symbol-level OT merge: two agents modifying different functions in the same file auto-merge (ops commute); concurrent edits to the same function produce a conflict at address "src/utils.py::function_name" rather than a coarse file conflict. - Language adapter protocol (LanguageAdapter) for future TypeScript, Swift, Go adapters — falls back to file-level raw-bytes tracking for unsupported file types. - Full MuseDomainPlugin + StructuredMergePlugin conformance. - 76 tests: unit (AST parser, symbol diff golden cases, cross-file move annotation), snapshot (museignore, pycache filter, stability), semantic diff (add/remove/rename/reformat functions), merge (symbol- level conflict detection), drift, schema, protocol conformance. - Registered as "code" domain in muse/plugins/registry.py. All gates pass: mypy strict (0 errors), typing_audit --max-any 0 (0 violations), pytest (773 tests, 0 failures). * feat: add tree-sitter multi-language support to code plugin Extends the code domain plugin from Python-only to 11 languages using tree-sitter — the same parsing technology used by GitHub Copilot, VS Code, Neovim, and Zed. Languages added (all backed by real CSTs, no regex): JavaScript / JSX / MJS / CJS — function, class, method extraction TypeScript / TSX — + interface, type alias, enum, abstract class Go — methods qualified with receiver type (Dog.Bark) Rust — impl methods qualified with type (Dog.bark) Java — class, interface, method, constructor, enum C — function_definition extraction C++ — + class_specifier and struct_specifier C# — class, interface, struct, method, constructor, enum Ruby — class, module, method, singleton_method Kotlin — function, class (with method nesting) All adapters compute content_id, body_hash, and signature_id from normalized CST text, enabling rename and implementation-change detection across all 11 languages. SEMANTIC_EXTENSIONS now covers 23 file extensions. Adds 11 tree-sitter dependencies to pyproject.toml. Includes 25 new tests covering symbol extraction, qualified name construction, rename detection, and adapter routing for every supported extension. * feat: add code-domain semantic commands and tour de force demo Three new CLI commands impossible in Git: - muse symbols: list every function, class, method in a snapshot, with content hashes, kind filter, file filter, and JSON output. - muse symbol-log <address>: track a single named symbol through the full commit DAG — creation, renames, signature changes, implementation changes, cross-file moves — including through rename events where the address itself changes. - muse detect-refactor: scan a commit range and emit a classified semantic refactoring report: RENAME (body_hash match), MOVE (content_id match), SIGNATURE, IMPLEMENTATION. Also adds: - docs/demo/tour-de-force-code.md: 11-act narration script for a code plugin demo — from first commit through symbol-level auto-merge, muse symbol-log, muse detect-refactor, and multi-language support. - docs/demo/README.md: demo hub presenting both the music and code demos side by side with the shared architecture explained. All three commands pass mypy --strict, zero typing_audit violations, 797 tests green. --------- Co-authored-by: Gabriel Cardona <gabriel@tellurstori.com>
062ae392·Gabriel Cardona <cgcardona@gmail.com>
·
feat: code domain plugin — AST-based semantic versioning (#53) Adds muse/plugins/code/ — a first-class Muse plugin treating source code as a structured system of named symbols. Includes tree-sitter support for 10 languages, semantic content IDs, rename/move detection, symbol-level OT merge. All gates: mypy strict 0 errors, typing_audit --max-any 0, 797 tests green.
f8c62a8b·Gabriel Cardona <cgcardona@gmail.com>
·
feat(code): supercharge code plugin with 9 new semantic commands Add a full suite of code-domain CLI commands that are structurally impossible in Git — treating the codebase as a typed, content-addressed symbol graph rather than a bag of text lines. New shared helper: - muse/plugins/code/_query.py: symbols_for_snapshot(), walk_commits(), walk_commits_range(), flat_symbol_ops(), touched_files(), file_pairs() New analysis commands (read-only): - muse grep: search the symbol graph by name, kind, language — not text - muse blame: per-symbol attribution — one answer per function, not per line - muse hotspots: symbol churn leaderboard — which functions change most - muse stable: symbol stability leaderboard — the bedrock of your codebase - muse coupling: semantic file co-change analysis — hidden dependencies - muse compare: deep semantic diff between any two historical snapshots - muse languages: language + symbol-type breakdown of any snapshot New agent-scale commands: - muse patch: surgical per-symbol modification — modify exactly one named symbol - muse query: symbol graph predicate DSL (kind/language/name/file/hash) All commands support --json for pipeline integration. All pass mypy strict, typing_audit --max-any 0, and 797 pytest tests. Update docs/demo/tour-de-force-code.md with all 12 commands as a full paradigm-shift narrative. Update docs/demo/README.md to reflect the complete command matrix.
2535fc53·Gabriel Cardona <gabriel@tellurstori.com>
·
feat(music): 9 new semantic commands — version control that understands music Add a full suite of music-domain CLI commands that are structurally impossible in Git — treating MIDI files as typed, content-addressed graphs of note events rather than binary blobs. New shared helper: - muse/plugins/music/_query.py: NoteInfo, load_track(), load_track_from_workdir(), key_signature_guess(), detect_chord(), notes_by_bar(), notes_to_midi_bytes(), walk_commits_for_track() New analysis commands (read-only): - muse notes: Every note as musical notation (pitch, beat, duration, velocity) - muse note-log: Note-level commit history — which notes changed in each commit - muse note-blame: Per-bar attribution — which commit introduced these notes? - muse harmony: Chord analysis + Krumhansl-Schmuckler key detection - muse piano-roll: ASCII piano roll visualization with bar separators - muse note-hotspots: Bar-level churn leaderboard across all tracks - muse velocity-profile: Dynamic range, RMS, and per-dynamic-level histogram New agent-scale commands: - muse transpose: Surgical pitch transformation — shift all notes by N semitones - muse mix: Combine notes from two MIDI tracks into a single output track All commands support --json for pipeline integration. All pass mypy strict, typing_audit --max-any 0, 797 pytest tests green. Add docs/demo/tour-de-force-music.md with a 9-act narrative. Update docs/demo/README.md and muse/cli/app.py to include both music and code domain commands together. Closes #56
26a36470·Gabriel Cardona <gabriel@tellurstori.com>
·
fix(code): close 4 architectural gaps — validation, deps, find-symbol, temporal query (#58) Patch syntax validation (gap 1) -------------------------------- - Add validate_source() to TreeSitterAdapter: parse content with the tree-sitter grammar for that language, walk the CST for ERROR/MISSING nodes, return a precise line-level error message. - Add validate_syntax(source, file_path) public function to ast_parser.py: dispatches to ast.parse for Python, validate_source for all tree-sitter languages, no-op for unsupported extensions. - patch.py: replace Python-only ast.parse guard with validate_syntax call. muse patch now validates syntax for all 11 supported languages before writing to disk. muse deps (gap 2 — no dependency graph) ----------------------------------------- - New command muse/cli/commands/deps.py. - File mode: extract import-kind symbols from the snapshot for a file (what does it import?). --reverse scans all other files' import symbols for references to the target (what imports it?). - Symbol mode (address contains ::): Python-only call extraction via ast.walk on the function node, collecting ast.Call targets as callees. --reverse scans all Python files in the snapshot for callers of the bare function name. - --commit for historical snapshots; --json for pipelines. muse find-symbol (gaps 3 & 4 — cross-branch, temporal hash search) -------------------------------------------------------------------- - New command muse/cli/commands/find_symbol.py. - Walks ALL CommitRecords in the object store (get_all_commits), sorted oldest-first, scanning InsertOps in each structured_delta. - --hash HASH: re-parses the snapshot blob for each matched InsertOp to obtain the exact content_id and filter by prefix. Finds when a specific function body first entered the repository, across every branch. - --name NAME: matches bare symbol name (exact or prefix with *). - --kind KIND: restricts to a symbol kind. - --all-branches: additionally enumerates every branch tip via .muse/refs/heads/ and checks its HEAD snapshot for current presence. - --first: deduplicate on content_id, keeping only the first appearance. muse query --all-commits (gap 4 — temporal hash= across history) ----------------------------------------------------------------- - New --all-commits flag on the existing muse query command. - When set, walks all commits ordered oldest-first, applies the full predicate DSL against each snapshot (re-parses blobs). - Deduplicates on content_id, annotating the first commit each unique hash was seen. "hash=a3f2c9 --all-commits" answers: when did this function body first appear, and on which branch? - Mutually exclusive with --commit. All gates: mypy strict 0 errors, typing_audit --max-any 0, 797 tests green. Co-authored-by: Gabriel Cardona <gabriel@tellurstori.com>
44b98511·Gabriel Cardona <cgcardona@gmail.com>
·
fix(ci): run only on push to main and PRs targeting main * fix(code): close 4 architectural gaps — validation, deps, find-symbol, temporal query Patch syntax validation (gap 1) -------------------------------- - Add validate_source() to TreeSitterAdapter: parse content with the tree-sitter grammar for that language, walk the CST for ERROR/MISSING nodes, return a precise line-level error message. - Add validate_syntax(source, file_path) public function to ast_parser.py: dispatches to ast.parse for Python, validate_source for all tree-sitter languages, no-op for unsupported extensions. - patch.py: replace Python-only ast.parse guard with validate_syntax call. muse patch now validates syntax for all 11 supported languages before writing to disk. muse deps (gap 2 — no dependency graph) ----------------------------------------- - New command muse/cli/commands/deps.py. - File mode: extract import-kind symbols from the snapshot for a file (what does it import?). --reverse scans all other files' import symbols for references to the target (what imports it?). - Symbol mode (address contains ::): Python-only call extraction via ast.walk on the function node, collecting ast.Call targets as callees. --reverse scans all Python files in the snapshot for callers of the bare function name. - --commit for historical snapshots; --json for pipelines. muse find-symbol (gaps 3 & 4 — cross-branch, temporal hash search) -------------------------------------------------------------------- - New command muse/cli/commands/find_symbol.py. - Walks ALL CommitRecords in the object store (get_all_commits), sorted oldest-first, scanning InsertOps in each structured_delta. - --hash HASH: re-parses the snapshot blob for each matched InsertOp to obtain the exact content_id and filter by prefix. Finds when a specific function body first entered the repository, across every branch. - --name NAME: matches bare symbol name (exact or prefix with *). - --kind KIND: restricts to a symbol kind. - --all-branches: additionally enumerates every branch tip via .muse/refs/heads/ and checks its HEAD snapshot for current presence. - --first: deduplicate on content_id, keeping only the first appearance. muse query --all-commits (gap 4 — temporal hash= across history) ----------------------------------------------------------------- - New --all-commits flag on the existing muse query command. - When set, walks all commits ordered oldest-first, applies the full predicate DSL against each snapshot (re-parses blobs). - Deduplicates on content_id, annotating the first commit each unique hash was seen. "hash=a3f2c9 --all-commits" answers: when did this function body first appear, and on which branch? - Mutually exclusive with --commit. All gates: mypy strict 0 errors, typing_audit --max-any 0, 797 tests green. * fix(ci): run only on push to main and PRs targeting main --------- Co-authored-by: Gabriel Cardona <gabriel@tellurstori.com>
ce4c9e03·Gabriel Cardona <cgcardona@gmail.com>
·
feat(code): add call-graph tier — impact, dead, coverage commands (#60) Adds three new code-domain commands that unlock the full power of the call-graph foundation laid by muse deps: muse impact <address> Transitive blast-radius analysis via BFS over the reverse call graph. Answers "if I change this function, what else could break?" — depths 1, 2, … until the closure is exhausted. Risk-level indicator (🟢/🟡/🔴). muse dead [--kind] [--exclude-tests] Dead code detection. A symbol is a candidate when its bare name appears in no ast.Call node AND its module is not imported anywhere in the snapshot. Distinguishes definite dead (module not imported) from soft dead (module imported but function never called directly). muse coverage <class_address> [--show-callers/--no-show-callers] Class interface call-coverage. Lists every method of a class, marks which ones are called anywhere in the snapshot, and prints a coverage percentage — no test suite required. Shared infrastructure: muse/plugins/code/_callgraph.py — ForwardGraph / ReverseGraph types, build_forward_graph, build_reverse_graph, transitive_callers BFS. deps.py refactored to import from _callgraph.py (no duplicate AST logic). All three commands support --json and --commit <REF>. mypy: 0 errors · typing_audit: 0 violations · pytest: 797 passed. Co-authored-by: Gabriel Cardona <gabriel@tellurstori.com>
25a0c523·Gabriel Cardona <cgcardona@gmail.com>
·
feat(phase-1): typed delta algebra — replace DeltaManifest with StructuredDelta Implements Phase 1 of the supercharge plan end-to-end. ## What changed **Core type system (muse/domain.py)** - Remove `DeltaManifest` entirely. - Add `InsertOp`, `DeleteOp`, `MoveOp`, `ReplaceOp`, `PatchOp` TypedDicts — each discriminated by a `Literal["op-name"]` field for mypy narrowing. - Add `StructuredDelta` — `{domain, ops: list[DomainOp], summary}`. - `StateDelta = StructuredDelta` (was `DeltaManifest`). - `MergeResult` gains `op_log: list[DomainOp]`. - `DriftReport.delta` defaults to an empty `StructuredDelta`. - `MuseDomainPlugin.diff()` gains `repo_root: Path | None = None`. **Storage (muse/core/store.py)** - `CommitRecord` and `CommitDict` gain `structured_delta: StructuredDelta | None`. - Stored at commit time; deserialized on read for `muse show`. **Myers LCS MIDI diff (muse/plugins/music/midi_diff.py)** — new module - `NoteKey` TypedDict as the LCS comparison unit (5 fields). - `lcs_edit_script()`: O(nm) LCS DP + traceback → `list[EditStep]`. - `extract_notes()`: MIDI bytes → sorted `list[NoteKey]`. - `diff_midi_notes()`: top-level entry point → `StructuredDelta`. - Content IDs are deterministic SHA-256 of note field tuples. **Music plugin (muse/plugins/music/plugin.py)** - `diff()`: returns `StructuredDelta` with `InsertOp`/`DeleteOp` for file additions/removals, `ReplaceOp` for non-MIDI modifications, and `PatchOp` with note-level `child_ops` for MIDI files when `repo_root` is available. - `apply()`: handles `DeleteOp`/`ReplaceOp`/`InsertOp` in-memory; workdir path rescans as before. - `drift()`: counts op kinds instead of accessing old manifest keys. **CLI commands** - `commit.py`: computes and stores `structured_delta` against parent. - `show.py`: displays `commit.structured_delta` with per-op lines and `PatchOp` child_summary; falls back to manifest diff for old commits. - `diff.py`: calls `plugin.diff(repo_root=root)` and prints structured output. - `status.py`: extracts added/modified/deleted sets from `delta.ops`. - `checkout.py`: extracts removed/to_restore paths from `delta.ops`. **Tests** - `test_structured_delta.py` (new): 38 tests for all op types and plugin behaviour. - `test_midi_diff.py` (new): 29 tests for note extraction, LCS algorithm, and `diff_midi_notes()` end-to-end. - `test_music_plugin.py`: updated to StructuredDelta API. - `test_cli_plugin_dispatch.py`, `test_plugin_apply_and_checkout.py`, `test_cli_coverage_gaps.py`: updated for new delta shape. All 456 tests green. mypy strict: 0 errors. typing_audit --max-any 0: 0 violations.
d7054e63·Gabriel Cardona <gabriel@tellurstori.com>
·
ci: only run on PRs targeting main (dev→main gate)
7887bec7·Gabriel Cardona <gabriel@tellurstori.com>
·
feat(code): Phase 2 — query v2 predicate grammar + muse query-history muse query upgrades to a full v2 predicate grammar: - OR, NOT, and parenthesis grouping via recursive descent parser - New predicate keys: qualified_name, body_hash, signature_id, lineno_gt, lineno_lt (no external dependencies) - All operators: = ~= ^= $= != (>= <= for lineno) - JSON output now includes schema_version:2 wrapper and end_lineno - Backward-compatible: existing "key=value" "key~=value" still work New module muse/plugins/code/_predicate.py: - Token → Parser → Predicate pipeline - Public API: parse_query(str | list[str]) → Predicate - Drives both muse query and muse query-history muse query-history PREDICATE... [--from REF] [--to REF]: - Walks a commit range, accumulating per-symbol history - Reports: first seen, last seen, commit count, change count - JSON with schema_version:2 - Answers: "find all Python functions introduced after v1.0" New store function walk_commits_between() for bounded range walking. mypy: 0 errors · typing_audit: 0 violations · pytest: 797 passed.
0590ede7·Gabriel Cardona <gabriel@tellurstori.com>
·
feat(code): Phase 1 — lineage, api-surface, codemap, clones, checkout-symbol, semantic-cherry-pick (#62) Six new code-domain commands, all additive with zero schema changes: muse lineage ADDRESS Full provenance chain of a symbol through commit history: created, renamed_from, moved_from, copied_from, modified, deleted. Rename/move detected by matching content_id across Insert+Delete pairs. muse api-surface [--diff REF] Public API surface at a snapshot. With --diff REF shows added/removed/ changed public symbols between two commits. Public = kind in {function,class,method,...} and name not starting with _. muse codemap [--top N] Semantic topology: modules ranked by size, import in-degree, import cycle detection via DFS, high-centrality symbols, boundary files (high fan-out, zero fan-in). Reveals codebase structure at a glance. muse clones [--tier exact|near|both] Exact clones: same body_hash at different addresses (copy-paste). Near-clones: same signature_id, different body_hash (same contract, diverged implementation). Cluster output with member addresses. muse checkout-symbol ADDRESS --commit REF [--dry-run] Restore a single symbol from a historical commit into the working tree. Only the target symbol's lines change; everything else is untouched. --dry-run prints the unified diff without writing. muse semantic-cherry-pick ADDRESS... --from REF [--dry-run] [--json] Cherry-pick named symbols from a historical commit, not entire files. Applies each symbol patch to the working tree at the symbol's current location; appends at end if symbol is not in the current tree. All six commands support --json and --commit REF (where applicable). mypy: 0 errors · typing_audit: 0 violations · pytest: 797 passed. Co-authored-by: Gabriel Cardona <gabriel@tellurstori.com>
b4e8aaf2·Gabriel Cardona <cgcardona@gmail.com>
·
feat(code): Phase 3 — .muse/indices/ infrastructure and muse index command New module muse/core/indices.py: - symbol_history index: address → chronological event timeline (commit_id, op, content_id, body_hash, signature_id per event) - hash_occurrence index: body_hash → list of addresses that share it - load/save helpers for both indexes - index_info() for status reporting - All indexes: schema_version:1, updated_at timestamp, fully rebuildable New command muse index (multi-command Typer app): - muse index status: show present/absent/corrupt status and entry counts - muse index rebuild: walk full commit history to rebuild symbol_history and/or hash_occurrence; --index NAME for selective rebuild Design: - Indexes are derived, optional, and fully rebuildable — zero impact on repository correctness if absent - symbol_history enables O(1) lineage/symbol-log instead of O(commits) scan - hash_occurrence enables O(1) clone detection and hash= queries - Incremental updates can be wired into the commit hook in a future phase mypy: 0 errors · typing_audit: 0 violations · pytest: 797 passed.
6647e2c9·Gabriel Cardona <gabriel@tellurstori.com>
·
feat(code): Phase 5 — multi-agent coordination layer New storage layer muse/core/coordination.py: - .muse/coordination/reservations/<uuid>.json advisory symbol leases - .muse/coordination/intents/<uuid>.json declared operations - Reservation: address list + run_id + branch + TTL-based expiry - Intent: extends a reservation with operation type and detail string - All records: write-once, schema_version:1, expiry-enforced by is_active() Six new coordination commands: muse reserve ADDRESS... --run-id ID --ttl N --op OP Advisory symbol reservation. Warns if addresses already reserved by another agent. Never blocks — purely coordination signal. muse intent ADDRESS... --op OP --detail TEXT --reservation-id UUID Declares a specific operation (rename/move/extract/delete/...) before executing it. Enables forecast to predict conflicts more accurately. muse forecast [--branch B] [--json] Predicts merge conflicts from active reservations and intents. Three conflict types: address_overlap (1.0), blast_radius_overlap (0.75), operation_conflict (0.9). Uses Python call graph for blast-radius. muse plan-merge OURS THEIRS [--json] Dry-run semantic merge plan. Classifies diverging symbols into: symbol_edit_overlap, rename_edit, delete_use, no_conflict. Reads committed snapshots — does not modify anything. muse shard --agents N [--language LANG] [--json] Partitions the codebase into N low-coupling work zones using import graph connectivity + greedy component partitioning balanced by symbol count. Reports cross-shard edges as coupling score. muse reconcile [--json] Reads coordination state + branch divergence, recommends merge ordering (fewer conflicts first) and integration strategy (fast-forward / rebase / manual) for each active branch. mypy: 0 errors · typing_audit: 0 violations · pytest: 797 passed.
8557932f·Gabriel Cardona <gabriel@tellurstori.com>
·
feat(code): Phase 4 — metadata_id, canonical_key, composite refactor classification (#65) SymbolRecord gains two new fields (backward-compatible: "" for pre-v2 records): metadata_id SHA-256 of symbol metadata that wraps the body without being part of it: decorators + async flag for Python functions, decorator list + bases for Python classes. Stubbed ("") for tree-sitter adapters — future adapters can enrich this by reading modifier/annotation nodes. canonical_key Stable machine handle: {file}#{scope}#{kind}#{name}#{lineno}. Disambiguates overloads and nested scopes. Unique within a snapshot. Enables agent-to-agent symbol handoff without re-querying. New classification helpers: muse/plugins/code/_refactor_classify.py classify_exact() hash-based exact classification: rename | move | rename+move | signature_only | impl_only | metadata_only | full_rewrite classify_composite() batch heuristic classification across a set of added/removed symbols — detects extract, inline, split, merge with confidence scores and evidence RefactorClassification full typed result (to_dict() → JSON-ready) muse detect-refactor --json now emits schema_version:2 with total count. Python _make_*_record() helpers thread file_path and class_prefix for accurate canonical_key computation. tree-sitter TreeSitterAdapter uses scope prefix extracted from the qualified_name. mypy: 0 errors · typing_audit: 0 violations · pytest: 797 passed. Co-authored-by: Gabriel Cardona <gabriel@tellurstori.com>
e35a0a2e·Gabriel Cardona <cgcardona@gmail.com>
·
feat(code): Phase 6 — ConflictRecord taxonomy, muse breakage, muse invariants MergeResult v2 — ConflictRecord taxonomy New ConflictRecord dataclass in domain.py carries structured conflict metadata alongside the existing conflicts: list[str]: - conflict_type: symbol_edit_overlap | rename_edit | move_edit | delete_use | dependency_conflict | file_level (legacy) - ours_summary / theirs_summary: short change descriptions - addresses: list[str] of involved symbol addresses Added as MergeResult.conflict_records: list[ConflictRecord] (default []). Fully backward-compatible — existing callers that don't populate it continue to work; plugins that implement StructuredMergePlugin can enrich it. muse breakage Detects symbol-level structural breakage in the working tree vs HEAD: - stale_import: imports a symbol no longer in the HEAD snapshot - missing_interface_method: class body missing methods found in HEAD Purely structural — no code execution, no type checker, no network. Operates on the committed symbol graph + current working-tree parse. Supports --language filter and --json output. muse invariants Enforces architectural rules from .muse/invariants.toml: - no_cycles: import graph must be acyclic (DFS cycle detection) - forbidden_dependency: source_pattern must not import forbidden_pattern - layer_boundary: lower layers must not import from upper layers - required_test: public functions must have corresponding test functions Minimal built-in TOML parser — no extra dependencies. All rules run against the committed snapshot; no working-tree parsing. Creates invariants.toml if absent — guided onboarding message shown. Supports --commit REF to check historical snapshots and --json output. mypy: 0 errors · typing_audit: 0 violations · pytest: 797 passed.
49d82b61·Gabriel Cardona <gabriel@tellurstori.com>
·
feat(code): Phase 7 — semantic versioning metadata on StructuredDelta + CommitRecord SemVerBump type alias (Literal["major", "minor", "patch", "none"]) StructuredDelta gains two optional v2 fields: sem_ver_bump: inferred impact of this delta on the public API breaking_changes: sorted list of symbol addresses whose public contract was removed or incompatibly changed infer_sem_ver_bump(delta) → (SemVerBump, list[str]) Pure function in domain.py, domain-agnostic: delete public symbol → major + address added to breaking_changes rename public symbol → major + address added to breaking_changes signature_only change → major + breaking insert public symbol → minor impl_only (body changes) → patch metadata/formatting only → none PatchOp child_ops → recurse into children (structural) CommitRecord gains: sem_ver_bump: SemVerBump = "none" (default for legacy + non-code) breaking_changes: list[str] = [] commit command: After computing structured_delta, calls infer_sem_ver_bump() and stores both on the delta and the CommitRecord. First commit (no parent) stays at "none" / []. muse log: Long-form view now shows SemVer: MAJOR / MINOR / PATCH when non-none, plus first 3 breaking-change addresses ("+N more" when list is longer). mypy: 0 errors · typing_audit: 0 violations · pytest: 797 passed.
b69599f3·Gabriel Cardona <gabriel@tellurstori.com>
·
Merge PR #68 — Phase 7 semantic versioning metadata on StructuredDelta + CommitRecord Clean merge — no conflicts. mypy: 0 errors · typing_audit: 0 violations · pytest: 797 passed.
f214943b·Gabriel Cardona <gabriel@tellurstori.com>
·
feat(code): Phase 7 — semantic versioning metadata on StructuredDelta + CommitRecord (#68) SemVerBump type alias (Literal["major", "minor", "patch", "none"]) StructuredDelta gains two optional v2 fields: sem_ver_bump: inferred impact of this delta on the public API breaking_changes: sorted list of symbol addresses whose public contract was removed or incompatibly changed infer_sem_ver_bump(delta) → (SemVerBump, list[str]) Pure function in domain.py, domain-agnostic: delete public symbol → major + address added to breaking_changes rename public symbol → major + address added to breaking_changes signature_only change → major + breaking insert public symbol → minor impl_only (body changes) → patch metadata/formatting only → none PatchOp child_ops → recurse into children (structural) CommitRecord gains: sem_ver_bump: SemVerBump = "none" (default for legacy + non-code) breaking_changes: list[str] = [] commit command: After computing structured_delta, calls infer_sem_ver_bump() and stores both on the delta and the CommitRecord. First commit (no parent) stays at "none" / []. muse log: Long-form view now shows SemVer: MAJOR / MINOR / PATCH when non-none, plus first 3 breaking-change addresses ("+N more" when list is longer). mypy: 0 errors · typing_audit: 0 violations · pytest: 797 passed. Co-authored-by: Gabriel Cardona <gabriel@tellurstori.com>
a748f814·Gabriel Cardona <cgcardona@gmail.com>
·
feat: code + music domain plugins — semantic versioning for code and MIDI (#57) * feat: code-domain semantic commands + code tour de force demo (#54) * feat: add code domain plugin with AST-based semantic versioning Introduces muse/plugins/code/ — a first-class Muse plugin that treats source code as a structured system of named symbols rather than lines of text. Key capabilities ---------------- - Python AST parsing via stdlib `ast` (zero new dependencies). Every function, class, method, variable, and import becomes a named symbol with a stable content-addressed identity (SHA-256 of normalized AST). - Semantic content IDs: two functions that differ only in whitespace or comments share the same content_id; reformatting a file produces no structured delta. - Rename detection: same body_hash + different name → ReplaceOp annotated "renamed to <new>". - Move detection: same content_id at a different address across files → cross-file move annotation on the DeleteOp/InsertOp pair. - Symbol-level OT merge: two agents modifying different functions in the same file auto-merge (ops commute); concurrent edits to the same function produce a conflict at address "src/utils.py::function_name" rather than a coarse file conflict. - Language adapter protocol (LanguageAdapter) for future TypeScript, Swift, Go adapters — falls back to file-level raw-bytes tracking for unsupported file types. - Full MuseDomainPlugin + StructuredMergePlugin conformance. - 76 tests: unit (AST parser, symbol diff golden cases, cross-file move annotation), snapshot (museignore, pycache filter, stability), semantic diff (add/remove/rename/reformat functions), merge (symbol- level conflict detection), drift, schema, protocol conformance. - Registered as "code" domain in muse/plugins/registry.py. All gates pass: mypy strict (0 errors), typing_audit --max-any 0 (0 violations), pytest (773 tests, 0 failures). * feat: add tree-sitter multi-language support to code plugin Extends the code domain plugin from Python-only to 11 languages using tree-sitter — the same parsing technology used by GitHub Copilot, VS Code, Neovim, and Zed. Languages added (all backed by real CSTs, no regex): JavaScript / JSX / MJS / CJS — function, class, method extraction TypeScript / TSX — + interface, type alias, enum, abstract class Go — methods qualified with receiver type (Dog.Bark) Rust — impl methods qualified with type (Dog.bark) Java — class, interface, method, constructor, enum C — function_definition extraction C++ — + class_specifier and struct_specifier C# — class, interface, struct, method, constructor, enum Ruby — class, module, method, singleton_method Kotlin — function, class (with method nesting) All adapters compute content_id, body_hash, and signature_id from normalized CST text, enabling rename and implementation-change detection across all 11 languages. SEMANTIC_EXTENSIONS now covers 23 file extensions. Adds 11 tree-sitter dependencies to pyproject.toml. Includes 25 new tests covering symbol extraction, qualified name construction, rename detection, and adapter routing for every supported extension. * feat: add code-domain semantic commands and tour de force demo Three new CLI commands impossible in Git: - muse symbols: list every function, class, method in a snapshot, with content hashes, kind filter, file filter, and JSON output. - muse symbol-log <address>: track a single named symbol through the full commit DAG — creation, renames, signature changes, implementation changes, cross-file moves — including through rename events where the address itself changes. - muse detect-refactor: scan a commit range and emit a classified semantic refactoring report: RENAME (body_hash match), MOVE (content_id match), SIGNATURE, IMPLEMENTATION. Also adds: - docs/demo/tour-de-force-code.md: 11-act narration script for a code plugin demo — from first commit through symbol-level auto-merge, muse symbol-log, muse detect-refactor, and multi-language support. - docs/demo/README.md: demo hub presenting both the music and code demos side by side with the shared architecture explained. All three commands pass mypy --strict, zero typing_audit violations, 797 tests green. --------- Co-authored-by: Gabriel Cardona <gabriel@tellurstori.com> * feat: code domain plugin — AST-based semantic versioning (#53) Adds muse/plugins/code/ — a first-class Muse plugin treating source code as a structured system of named symbols. Includes tree-sitter support for 10 languages, semantic content IDs, rename/move detection, symbol-level OT merge. All gates: mypy strict 0 errors, typing_audit --max-any 0, 797 tests green. * feat(code): supercharge code plugin with 9 new semantic commands Add a full suite of code-domain CLI commands that are structurally impossible in Git — treating the codebase as a typed, content-addressed symbol graph rather than a bag of text lines. New shared helper: - muse/plugins/code/_query.py: symbols_for_snapshot(), walk_commits(), walk_commits_range(), flat_symbol_ops(), touched_files(), file_pairs() New analysis commands (read-only): - muse grep: search the symbol graph by name, kind, language — not text - muse blame: per-symbol attribution — one answer per function, not per line - muse hotspots: symbol churn leaderboard — which functions change most - muse stable: symbol stability leaderboard — the bedrock of your codebase - muse coupling: semantic file co-change analysis — hidden dependencies - muse compare: deep semantic diff between any two historical snapshots - muse languages: language + symbol-type breakdown of any snapshot New agent-scale commands: - muse patch: surgical per-symbol modification — modify exactly one named symbol - muse query: symbol graph predicate DSL (kind/language/name/file/hash) All commands support --json for pipeline integration. All pass mypy strict, typing_audit --max-any 0, and 797 pytest tests. Update docs/demo/tour-de-force-code.md with all 12 commands as a full paradigm-shift narrative. Update docs/demo/README.md to reflect the complete command matrix. * feat(music): 9 new semantic commands — version control that understands music Add a full suite of music-domain CLI commands that are structurally impossible in Git — treating MIDI files as typed, content-addressed graphs of note events rather than binary blobs. New shared helper: - muse/plugins/music/_query.py: NoteInfo, load_track(), load_track_from_workdir(), key_signature_guess(), detect_chord(), notes_by_bar(), notes_to_midi_bytes(), walk_commits_for_track() New analysis commands (read-only): - muse notes: Every note as musical notation (pitch, beat, duration, velocity) - muse note-log: Note-level commit history — which notes changed in each commit - muse note-blame: Per-bar attribution — which commit introduced these notes? - muse harmony: Chord analysis + Krumhansl-Schmuckler key detection - muse piano-roll: ASCII piano roll visualization with bar separators - muse note-hotspots: Bar-level churn leaderboard across all tracks - muse velocity-profile: Dynamic range, RMS, and per-dynamic-level histogram New agent-scale commands: - muse transpose: Surgical pitch transformation — shift all notes by N semitones - muse mix: Combine notes from two MIDI tracks into a single output track All commands support --json for pipeline integration. All pass mypy strict, typing_audit --max-any 0, 797 pytest tests green. Add docs/demo/tour-de-force-music.md with a 9-act narrative. Update docs/demo/README.md and muse/cli/app.py to include both music and code domain commands together. Closes #56 --------- Co-authored-by: Gabriel Cardona <gabriel@tellurstori.com>
f2e2c3ea·Gabriel Cardona <cgcardona@gmail.com>
·
Add comprehensive docs and supercharged tests for Code Domain V2 (#70) Documentation - docs/reference/code-domain-v2.md: complete 11-section reference covering all 7 phases — symbol identity model, every new command, predicate grammar, index infrastructure, coordination layer, merge engine v2, semantic versioning, call-graph tier, architecture internals, and full type reference. Tests (7 new test files, 273 new tests → 1070 total) - test_predicate.py: tokeniser, all 10 keys × 7 operators, OR/NOT/AND/grouping, parse_query list mode, 9 error cases. - test_refactor_classify.py: classify_exact (all 8 classifications), classify_composite (exact rename/move/rename+move detection, inferred extract), RefactorClassification.to_dict. - test_callgraph.py: build_forward_graph, build_reverse_graph, transitive_callers including cycle-safety, diamond dependency, max_depth limit. - test_coordination.py: Reservation and Intent create/load/active/round-trip, TTL expiry, corrupt-file resilience. - test_indices.py: SymbolHistoryEntry round-trip, symbol_history and hash_occurrence save/load/schema compliance, index_info absent/present/corrupt reporting. - test_sem_ver.py: infer_sem_ver_bump for all op types, ConflictRecord dataclass, major-wins precedence, multiple breaking changes accumulation. - test_code_commands_v2.py: 96 CLI integration tests covering all Phase 1–7 commands and call-graph tier (lineage, api-surface, codemap, clones, checkout-symbol, semantic-cherry-pick, query v2, query-history, index rebuild/status, detect-refactor schema v2, reserve, intent, forecast, plan-merge, shard, reconcile, breakage, invariants, commit sem_ver_bump, impact, dead, coverage, deps, find-symbol, patch). Co-authored-by: Gabriel Cardona <gabriel@tellurstori.com>
dfa7b7aa·Gabriel Cardona <cgcardona@gmail.com>
·
refactor: strip phase/v2 workflow labels from all source, tests, and docs Remove all "Phase N", "Code Domain V2", and "v2" workflow labels that bled into permanent codebase artifacts. Rename files that carried the label in their name (test_code_commands_v2 → test_code_commands, code-domain-v2.md → code-domain.md) and scrub matching docstrings, inline comments, section headers, and help text across muse/, tests/, and docs/. No logic changes; pure label removal. Co-authored-by: Gabriel Cardona <gabriel@tellurstori.com>
d855b718·Gabriel Cardona <cgcardona@gmail.com>
·
feat: god-tier MIDI dimension expansion + full supercharge architecture Expands the music plugin from 4 internal merge buckets to 21 fully independent MIDI dimensions, adds stable entity identity with MutateOp tracking, agent provenance with HMAC signing, an append-only op log, voice-aware Music RGA CRDT, music query DSL, invariants engine, and hierarchical bar-chunk manifests. MIDI dimension expansion (midi_merge.py, plugin.py): - From 4 buckets (notes/harmonic/dynamic/structural) to 21 internal dims - pitch_bend, channel_pressure, poly_pressure now independent dimensions - CC split by controller: cc_modulation, cc_volume, cc_pan, cc_expression, cc_sustain, cc_portamento, cc_sostenuto, cc_soft_pedal, cc_reverb, cc_chorus, cc_other — all independently mergeable - tempo_map, time_signatures, track_structure remain non-independent - Backward-compatible user-facing aliases (melodic/rhythmic/harmonic/ dynamic/structural still work) New core modules: - muse/core/provenance.py: AgentIdentity, HMAC-SHA256 signing, key I/O - muse/core/op_log.py: OpEntry, Lamport timestamps, append-only log, checkpointing, replay, to_structured_delta New music plugin modules: - muse/plugins/music/entity.py: NoteEntity, EntityIndex, entity ID assignment, entity-aware diff with MutateOp - muse/plugins/music/_music_query.py: tokenizer, recursive descent parser, evaluator, run_query over commit history - muse/plugins/music/_invariants.py: InvariantRule/Report/Violation, 4 built-in checks (max_polyphony, pitch_range, key_consistency, no_parallel_fifths) - muse/plugins/music/manifest.py: BarChunk, TrackManifest, MusicManifest, hierarchical per-bar manifests, partial diff by bar hash - muse/plugins/music/_crdt_notes.py: NotePosition, RGANoteEntry, MusicRGA with voice-aware position ordering (bass→soprano) Domain types (domain.py): FieldMutation, MutateOp, EntityProvenance Store types (core/store.py): 6 agent provenance fields on CommitRecord CLI: muse music-query, muse music-check registered in app.py Benchmark: tools/benchmark.py harness for parse/diff/merge/query/RGA Tests: 215 passing across 7 new test suites Docs: 5 architecture documents in docs/
6d8ca4ac·Gabriel Cardona <gabriel@tellurstori.com>
·
refactor: rename music→midi domain, strip all 5-dim backward compat - Rename muse/plugins/music/ → muse/plugins/midi/ (git mv, history preserved) - Rename _music_query.py → _midi_query.py - Rename CLI commands: music_check → midi_check, music_query → midi_query with updated command names (music-check→midi-check, music-query→midi-query) - Change _DOMAIN_TAG from "music" to "midi" in plugin.py - Rename MusicPlugin → MidiPlugin everywhere; update registry and all imports - Change default domain: "music" → "midi" in init.py and registry.py - Remove all 5-dimension backward-compat aliases from DIM_ALIAS: melodic, rhythmic, harmonic, dynamic, structural — gone entirely - Rewrite test_domain_schema.py: tests all 21 MIDI dimensions by name, schema kind, and independence flag; asserts "music" no longer in registry - Rewrite test_music_midi_merge.py: tests all 21-dim _classify_event routing, per-dimension conflict detection, independent auto-merge, and strategy rules; removes all references to old coarse dimension names - Update test_core_attributes.py examples to use new MIDI dimension names - Update all doc files and README.md: replace 5-dim descriptions with 21-dimension MIDI schema; update module paths and class names throughout
9ee9c39c·Gabriel Cardona <gabriel@tellurstori.com>
·
feat: implement 3 missing plan items — property tests, format_version, schema auto-dispatch Three gaps from the supercharge plan now fully implemented: 1. **hypothesis property-based tests** (`tests/test_property_based.py`) - All six CRDT types (LWWRegister, VectorClock, ORSet, RGA, AWMap, GCounter) verified against commutativity, associativity, and idempotency with @given - LCS round-trip: self-diff → empty ops, empty-base, empty-target, content provenance, op-count upper bound - OT diamond property: for arbitrary concurrent InsertOp pairs, applying transform() in either order produces identical final sequences 2. **Bug fixes discovered by hypothesis** - LWWRegister.join: non-commutative when (timestamp, author) tie but values differ — added value as deterministic tiebreaker - RGA.join: non-commutative when same element ID has different values across replicas — added lexicographic value tiebreaker 3. **CommitRecord.format_version** (`muse/core/store.py`) - New field tracks schema evolution: 1=base, 2=structured_delta, 3=sem_ver, 4=agent provenance; new commits write version 4; old JSON defaults to 1 4. **snapshot_diff() auto-dispatch** (`muse/core/diff_algorithms/__init__.py`) - Public function: given a DomainSchema + two SnapshotManifests → StructuredDelta - ScaffoldPlugin.diff() now delegates to snapshot_diff() — zero set-algebra boilerplate - New plugin authors call snapshot_diff(self.schema(), base, target) for free file-level diffs without implementing diff() from scratch All verified: 1319 pytest tests green, mypy 0 errors, typing_audit 0 violations.
d1dfb412·Gabriel Cardona <gabriel@tellurstori.com>
·
refactor: remove supercharge/god-tier/singularity references from all source files Replace informal hype labels with neutral technical language: - store.py: "supercharge plan" → "architecture phase" - diff_algorithms/__init__.py: "supercharge plan" → "domain schema architecture" - _crdt_notes.py: "supercharge plan" → "live collaboration architecture" - Rename docs/architecture/supercharge-plan.md → architecture-plan.md and update the document title to match
daf9b815·Gabriel Cardona <gabriel@tellurstori.com>
·
feat: god-tier MIDI dimension expansion + full supercharge architecture (#73) * feat: god-tier MIDI dimension expansion + full supercharge architecture Expands the music plugin from 4 internal merge buckets to 21 fully independent MIDI dimensions, adds stable entity identity with MutateOp tracking, agent provenance with HMAC signing, an append-only op log, voice-aware Music RGA CRDT, music query DSL, invariants engine, and hierarchical bar-chunk manifests. MIDI dimension expansion (midi_merge.py, plugin.py): - From 4 buckets (notes/harmonic/dynamic/structural) to 21 internal dims - pitch_bend, channel_pressure, poly_pressure now independent dimensions - CC split by controller: cc_modulation, cc_volume, cc_pan, cc_expression, cc_sustain, cc_portamento, cc_sostenuto, cc_soft_pedal, cc_reverb, cc_chorus, cc_other — all independently mergeable - tempo_map, time_signatures, track_structure remain non-independent - Backward-compatible user-facing aliases (melodic/rhythmic/harmonic/ dynamic/structural still work) New core modules: - muse/core/provenance.py: AgentIdentity, HMAC-SHA256 signing, key I/O - muse/core/op_log.py: OpEntry, Lamport timestamps, append-only log, checkpointing, replay, to_structured_delta New music plugin modules: - muse/plugins/music/entity.py: NoteEntity, EntityIndex, entity ID assignment, entity-aware diff with MutateOp - muse/plugins/music/_music_query.py: tokenizer, recursive descent parser, evaluator, run_query over commit history - muse/plugins/music/_invariants.py: InvariantRule/Report/Violation, 4 built-in checks (max_polyphony, pitch_range, key_consistency, no_parallel_fifths) - muse/plugins/music/manifest.py: BarChunk, TrackManifest, MusicManifest, hierarchical per-bar manifests, partial diff by bar hash - muse/plugins/music/_crdt_notes.py: NotePosition, RGANoteEntry, MusicRGA with voice-aware position ordering (bass→soprano) Domain types (domain.py): FieldMutation, MutateOp, EntityProvenance Store types (core/store.py): 6 agent provenance fields on CommitRecord CLI: muse music-query, muse music-check registered in app.py Benchmark: tools/benchmark.py harness for parse/diff/merge/query/RGA Tests: 215 passing across 7 new test suites Docs: 5 architecture documents in docs/ * refactor: rename music→midi domain, strip all 5-dim backward compat - Rename muse/plugins/music/ → muse/plugins/midi/ (git mv, history preserved) - Rename _music_query.py → _midi_query.py - Rename CLI commands: music_check → midi_check, music_query → midi_query with updated command names (music-check→midi-check, music-query→midi-query) - Change _DOMAIN_TAG from "music" to "midi" in plugin.py - Rename MusicPlugin → MidiPlugin everywhere; update registry and all imports - Change default domain: "music" → "midi" in init.py and registry.py - Remove all 5-dimension backward-compat aliases from DIM_ALIAS: melodic, rhythmic, harmonic, dynamic, structural — gone entirely - Rewrite test_domain_schema.py: tests all 21 MIDI dimensions by name, schema kind, and independence flag; asserts "music" no longer in registry - Rewrite test_music_midi_merge.py: tests all 21-dim _classify_event routing, per-dimension conflict detection, independent auto-merge, and strategy rules; removes all references to old coarse dimension names - Update test_core_attributes.py examples to use new MIDI dimension names - Update all doc files and README.md: replace 5-dim descriptions with 21-dimension MIDI schema; update module paths and class names throughout * feat: implement 3 missing plan items — property tests, format_version, schema auto-dispatch Three gaps from the supercharge plan now fully implemented: 1. **hypothesis property-based tests** (`tests/test_property_based.py`) - All six CRDT types (LWWRegister, VectorClock, ORSet, RGA, AWMap, GCounter) verified against commutativity, associativity, and idempotency with @given - LCS round-trip: self-diff → empty ops, empty-base, empty-target, content provenance, op-count upper bound - OT diamond property: for arbitrary concurrent InsertOp pairs, applying transform() in either order produces identical final sequences 2. **Bug fixes discovered by hypothesis** - LWWRegister.join: non-commutative when (timestamp, author) tie but values differ — added value as deterministic tiebreaker - RGA.join: non-commutative when same element ID has different values across replicas — added lexicographic value tiebreaker 3. **CommitRecord.format_version** (`muse/core/store.py`) - New field tracks schema evolution: 1=base, 2=structured_delta, 3=sem_ver, 4=agent provenance; new commits write version 4; old JSON defaults to 1 4. **snapshot_diff() auto-dispatch** (`muse/core/diff_algorithms/__init__.py`) - Public function: given a DomainSchema + two SnapshotManifests → StructuredDelta - ScaffoldPlugin.diff() now delegates to snapshot_diff() — zero set-algebra boilerplate - New plugin authors call snapshot_diff(self.schema(), base, target) for free file-level diffs without implementing diff() from scratch All verified: 1319 pytest tests green, mypy 0 errors, typing_audit 0 violations. --------- Co-authored-by: Gabriel Cardona <gabriel@tellurstori.com>
b9f920d1·Gabriel Cardona <cgcardona@gmail.com>
·
feat: Code Domain V2 — complete 7-phase roadmap (dev → main) (#69) * feat(phase-1): typed delta algebra — replace DeltaManifest with StructuredDelta Implements Phase 1 of the supercharge plan end-to-end. ## What changed **Core type system (muse/domain.py)** - Remove `DeltaManifest` entirely. - Add `InsertOp`, `DeleteOp`, `MoveOp`, `ReplaceOp`, `PatchOp` TypedDicts — each discriminated by a `Literal["op-name"]` field for mypy narrowing. - Add `StructuredDelta` — `{domain, ops: list[DomainOp], summary}`. - `StateDelta = StructuredDelta` (was `DeltaManifest`). - `MergeResult` gains `op_log: list[DomainOp]`. - `DriftReport.delta` defaults to an empty `StructuredDelta`. - `MuseDomainPlugin.diff()` gains `repo_root: Path | None = None`. **Storage (muse/core/store.py)** - `CommitRecord` and `CommitDict` gain `structured_delta: StructuredDelta | None`. - Stored at commit time; deserialized on read for `muse show`. **Myers LCS MIDI diff (muse/plugins/music/midi_diff.py)** — new module - `NoteKey` TypedDict as the LCS comparison unit (5 fields). - `lcs_edit_script()`: O(nm) LCS DP + traceback → `list[EditStep]`. - `extract_notes()`: MIDI bytes → sorted `list[NoteKey]`. - `diff_midi_notes()`: top-level entry point → `StructuredDelta`. - Content IDs are deterministic SHA-256 of note field tuples. **Music plugin (muse/plugins/music/plugin.py)** - `diff()`: returns `StructuredDelta` with `InsertOp`/`DeleteOp` for file additions/removals, `ReplaceOp` for non-MIDI modifications, and `PatchOp` with note-level `child_ops` for MIDI files when `repo_root` is available. - `apply()`: handles `DeleteOp`/`ReplaceOp`/`InsertOp` in-memory; workdir path rescans as before. - `drift()`: counts op kinds instead of accessing old manifest keys. **CLI commands** - `commit.py`: computes and stores `structured_delta` against parent. - `show.py`: displays `commit.structured_delta` with per-op lines and `PatchOp` child_summary; falls back to manifest diff for old commits. - `diff.py`: calls `plugin.diff(repo_root=root)` and prints structured output. - `status.py`: extracts added/modified/deleted sets from `delta.ops`. - `checkout.py`: extracts removed/to_restore paths from `delta.ops`. **Tests** - `test_structured_delta.py` (new): 38 tests for all op types and plugin behaviour. - `test_midi_diff.py` (new): 29 tests for note extraction, LCS algorithm, and `diff_midi_notes()` end-to-end. - `test_music_plugin.py`: updated to StructuredDelta API. - `test_cli_plugin_dispatch.py`, `test_plugin_apply_and_checkout.py`, `test_cli_coverage_gaps.py`: updated for new delta shape. All 456 tests green. mypy strict: 0 errors. typing_audit --max-any 0: 0 violations. * ci: only run on PRs targeting main (dev→main gate) * feat: code-domain semantic commands + code tour de force demo (#54) * feat: add code domain plugin with AST-based semantic versioning Introduces muse/plugins/code/ — a first-class Muse plugin that treats source code as a structured system of named symbols rather than lines of text. Key capabilities ---------------- - Python AST parsing via stdlib `ast` (zero new dependencies). Every function, class, method, variable, and import becomes a named symbol with a stable content-addressed identity (SHA-256 of normalized AST). - Semantic content IDs: two functions that differ only in whitespace or comments share the same content_id; reformatting a file produces no structured delta. - Rename detection: same body_hash + different name → ReplaceOp annotated "renamed to <new>". - Move detection: same content_id at a different address across files → cross-file move annotation on the DeleteOp/InsertOp pair. - Symbol-level OT merge: two agents modifying different functions in the same file auto-merge (ops commute); concurrent edits to the same function produce a conflict at address "src/utils.py::function_name" rather than a coarse file conflict. - Language adapter protocol (LanguageAdapter) for future TypeScript, Swift, Go adapters — falls back to file-level raw-bytes tracking for unsupported file types. - Full MuseDomainPlugin + StructuredMergePlugin conformance. - 76 tests: unit (AST parser, symbol diff golden cases, cross-file move annotation), snapshot (museignore, pycache filter, stability), semantic diff (add/remove/rename/reformat functions), merge (symbol- level conflict detection), drift, schema, protocol conformance. - Registered as "code" domain in muse/plugins/registry.py. All gates pass: mypy strict (0 errors), typing_audit --max-any 0 (0 violations), pytest (773 tests, 0 failures). * feat: add tree-sitter multi-language support to code plugin Extends the code domain plugin from Python-only to 11 languages using tree-sitter — the same parsing technology used by GitHub Copilot, VS Code, Neovim, and Zed. Languages added (all backed by real CSTs, no regex): JavaScript / JSX / MJS / CJS — function, class, method extraction TypeScript / TSX — + interface, type alias, enum, abstract class Go — methods qualified with receiver type (Dog.Bark) Rust — impl methods qualified with type (Dog.bark) Java — class, interface, method, constructor, enum C — function_definition extraction C++ — + class_specifier and struct_specifier C# — class, interface, struct, method, constructor, enum Ruby — class, module, method, singleton_method Kotlin — function, class (with method nesting) All adapters compute content_id, body_hash, and signature_id from normalized CST text, enabling rename and implementation-change detection across all 11 languages. SEMANTIC_EXTENSIONS now covers 23 file extensions. Adds 11 tree-sitter dependencies to pyproject.toml. Includes 25 new tests covering symbol extraction, qualified name construction, rename detection, and adapter routing for every supported extension. * feat: add code-domain semantic commands and tour de force demo Three new CLI commands impossible in Git: - muse symbols: list every function, class, method in a snapshot, with content hashes, kind filter, file filter, and JSON output. - muse symbol-log <address>: track a single named symbol through the full commit DAG — creation, renames, signature changes, implementation changes, cross-file moves — including through rename events where the address itself changes. - muse detect-refactor: scan a commit range and emit a classified semantic refactoring report: RENAME (body_hash match), MOVE (content_id match), SIGNATURE, IMPLEMENTATION. Also adds: - docs/demo/tour-de-force-code.md: 11-act narration script for a code plugin demo — from first commit through symbol-level auto-merge, muse symbol-log, muse detect-refactor, and multi-language support. - docs/demo/README.md: demo hub presenting both the music and code demos side by side with the shared architecture explained. All three commands pass mypy --strict, zero typing_audit violations, 797 tests green. --------- Co-authored-by: Gabriel Cardona <gabriel@tellurstori.com> * feat: code domain plugin — AST-based semantic versioning (#53) Adds muse/plugins/code/ — a first-class Muse plugin treating source code as a structured system of named symbols. Includes tree-sitter support for 10 languages, semantic content IDs, rename/move detection, symbol-level OT merge. All gates: mypy strict 0 errors, typing_audit --max-any 0, 797 tests green. * feat(code): supercharge code plugin with 9 new semantic commands Add a full suite of code-domain CLI commands that are structurally impossible in Git — treating the codebase as a typed, content-addressed symbol graph rather than a bag of text lines. New shared helper: - muse/plugins/code/_query.py: symbols_for_snapshot(), walk_commits(), walk_commits_range(), flat_symbol_ops(), touched_files(), file_pairs() New analysis commands (read-only): - muse grep: search the symbol graph by name, kind, language — not text - muse blame: per-symbol attribution — one answer per function, not per line - muse hotspots: symbol churn leaderboard — which functions change most - muse stable: symbol stability leaderboard — the bedrock of your codebase - muse coupling: semantic file co-change analysis — hidden dependencies - muse compare: deep semantic diff between any two historical snapshots - muse languages: language + symbol-type breakdown of any snapshot New agent-scale commands: - muse patch: surgical per-symbol modification — modify exactly one named symbol - muse query: symbol graph predicate DSL (kind/language/name/file/hash) All commands support --json for pipeline integration. All pass mypy strict, typing_audit --max-any 0, and 797 pytest tests. Update docs/demo/tour-de-force-code.md with all 12 commands as a full paradigm-shift narrative. Update docs/demo/README.md to reflect the complete command matrix. * feat(music): 9 new semantic commands — version control that understands music Add a full suite of music-domain CLI commands that are structurally impossible in Git — treating MIDI files as typed, content-addressed graphs of note events rather than binary blobs. New shared helper: - muse/plugins/music/_query.py: NoteInfo, load_track(), load_track_from_workdir(), key_signature_guess(), detect_chord(), notes_by_bar(), notes_to_midi_bytes(), walk_commits_for_track() New analysis commands (read-only): - muse notes: Every note as musical notation (pitch, beat, duration, velocity) - muse note-log: Note-level commit history — which notes changed in each commit - muse note-blame: Per-bar attribution — which commit introduced these notes? - muse harmony: Chord analysis + Krumhansl-Schmuckler key detection - muse piano-roll: ASCII piano roll visualization with bar separators - muse note-hotspots: Bar-level churn leaderboard across all tracks - muse velocity-profile: Dynamic range, RMS, and per-dynamic-level histogram New agent-scale commands: - muse transpose: Surgical pitch transformation — shift all notes by N semitones - muse mix: Combine notes from two MIDI tracks into a single output track All commands support --json for pipeline integration. All pass mypy strict, typing_audit --max-any 0, 797 pytest tests green. Add docs/demo/tour-de-force-music.md with a 9-act narrative. Update docs/demo/README.md and muse/cli/app.py to include both music and code domain commands together. Closes #56 * fix(code): close 4 architectural gaps — validation, deps, find-symbol, temporal query (#58) Patch syntax validation (gap 1) -------------------------------- - Add validate_source() to TreeSitterAdapter: parse content with the tree-sitter grammar for that language, walk the CST for ERROR/MISSING nodes, return a precise line-level error message. - Add validate_syntax(source, file_path) public function to ast_parser.py: dispatches to ast.parse for Python, validate_source for all tree-sitter languages, no-op for unsupported extensions. - patch.py: replace Python-only ast.parse guard with validate_syntax call. muse patch now validates syntax for all 11 supported languages before writing to disk. muse deps (gap 2 — no dependency graph) ----------------------------------------- - New command muse/cli/commands/deps.py. - File mode: extract import-kind symbols from the snapshot for a file (what does it import?). --reverse scans all other files' import symbols for references to the target (what imports it?). - Symbol mode (address contains ::): Python-only call extraction via ast.walk on the function node, collecting ast.Call targets as callees. --reverse scans all Python files in the snapshot for callers of the bare function name. - --commit for historical snapshots; --json for pipelines. muse find-symbol (gaps 3 & 4 — cross-branch, temporal hash search) -------------------------------------------------------------------- - New command muse/cli/commands/find_symbol.py. - Walks ALL CommitRecords in the object store (get_all_commits), sorted oldest-first, scanning InsertOps in each structured_delta. - --hash HASH: re-parses the snapshot blob for each matched InsertOp to obtain the exact content_id and filter by prefix. Finds when a specific function body first entered the repository, across every branch. - --name NAME: matches bare symbol name (exact or prefix with *). - --kind KIND: restricts to a symbol kind. - --all-branches: additionally enumerates every branch tip via .muse/refs/heads/ and checks its HEAD snapshot for current presence. - --first: deduplicate on content_id, keeping only the first appearance. muse query --all-commits (gap 4 — temporal hash= across history) ----------------------------------------------------------------- - New --all-commits flag on the existing muse query command. - When set, walks all commits ordered oldest-first, applies the full predicate DSL against each snapshot (re-parses blobs). - Deduplicates on content_id, annotating the first commit each unique hash was seen. "hash=a3f2c9 --all-commits" answers: when did this function body first appear, and on which branch? - Mutually exclusive with --commit. All gates: mypy strict 0 errors, typing_audit --max-any 0, 797 tests green. Co-authored-by: Gabriel Cardona <gabriel@tellurstori.com> * fix(ci): run only on push to main and PRs targeting main * fix(code): close 4 architectural gaps — validation, deps, find-symbol, temporal query Patch syntax validation (gap 1) -------------------------------- - Add validate_source() to TreeSitterAdapter: parse content with the tree-sitter grammar for that language, walk the CST for ERROR/MISSING nodes, return a precise line-level error message. - Add validate_syntax(source, file_path) public function to ast_parser.py: dispatches to ast.parse for Python, validate_source for all tree-sitter languages, no-op for unsupported extensions. - patch.py: replace Python-only ast.parse guard with validate_syntax call. muse patch now validates syntax for all 11 supported languages before writing to disk. muse deps (gap 2 — no dependency graph) ----------------------------------------- - New command muse/cli/commands/deps.py. - File mode: extract import-kind symbols from the snapshot for a file (what does it import?). --reverse scans all other files' import symbols for references to the target (what imports it?). - Symbol mode (address contains ::): Python-only call extraction via ast.walk on the function node, collecting ast.Call targets as callees. --reverse scans all Python files in the snapshot for callers of the bare function name. - --commit for historical snapshots; --json for pipelines. muse find-symbol (gaps 3 & 4 — cross-branch, temporal hash search) -------------------------------------------------------------------- - New command muse/cli/commands/find_symbol.py. - Walks ALL CommitRecords in the object store (get_all_commits), sorted oldest-first, scanning InsertOps in each structured_delta. - --hash HASH: re-parses the snapshot blob for each matched InsertOp to obtain the exact content_id and filter by prefix. Finds when a specific function body first entered the repository, across every branch. - --name NAME: matches bare symbol name (exact or prefix with *). - --kind KIND: restricts to a symbol kind. - --all-branches: additionally enumerates every branch tip via .muse/refs/heads/ and checks its HEAD snapshot for current presence. - --first: deduplicate on content_id, keeping only the first appearance. muse query --all-commits (gap 4 — temporal hash= across history) ----------------------------------------------------------------- - New --all-commits flag on the existing muse query command. - When set, walks all commits ordered oldest-first, applies the full predicate DSL against each snapshot (re-parses blobs). - Deduplicates on content_id, annotating the first commit each unique hash was seen. "hash=a3f2c9 --all-commits" answers: when did this function body first appear, and on which branch? - Mutually exclusive with --commit. All gates: mypy strict 0 errors, typing_audit --max-any 0, 797 tests green. * fix(ci): run only on push to main and PRs targeting main --------- Co-authored-by: Gabriel Cardona <gabriel@tellurstori.com> * feat(code): add call-graph tier — impact, dead, coverage commands (#60) Adds three new code-domain commands that unlock the full power of the call-graph foundation laid by muse deps: muse impact <address> Transitive blast-radius analysis via BFS over the reverse call graph. Answers "if I change this function, what else could break?" — depths 1, 2, … until the closure is exhausted. Risk-level indicator (🟢/🟡/🔴). muse dead [--kind] [--exclude-tests] Dead code detection. A symbol is a candidate when its bare name appears in no ast.Call node AND its module is not imported anywhere in the snapshot. Distinguishes definite dead (module not imported) from soft dead (module imported but function never called directly). muse coverage <class_address> [--show-callers/--no-show-callers] Class interface call-coverage. Lists every method of a class, marks which ones are called anywhere in the snapshot, and prints a coverage percentage — no test suite required. Shared infrastructure: muse/plugins/code/_callgraph.py — ForwardGraph / ReverseGraph types, build_forward_graph, build_reverse_graph, transitive_callers BFS. deps.py refactored to import from _callgraph.py (no duplicate AST logic). All three commands support --json and --commit <REF>. mypy: 0 errors · typing_audit: 0 violations · pytest: 797 passed. Co-authored-by: Gabriel Cardona <gabriel@tellurstori.com> * feat(code): Phase 2 — query v2 predicate grammar + muse query-history muse query upgrades to a full v2 predicate grammar: - OR, NOT, and parenthesis grouping via recursive descent parser - New predicate keys: qualified_name, body_hash, signature_id, lineno_gt, lineno_lt (no external dependencies) - All operators: = ~= ^= $= != (>= <= for lineno) - JSON output now includes schema_version:2 wrapper and end_lineno - Backward-compatible: existing "key=value" "key~=value" still work New module muse/plugins/code/_predicate.py: - Token → Parser → Predicate pipeline - Public API: parse_query(str | list[str]) → Predicate - Drives both muse query and muse query-history muse query-history PREDICATE... [--from REF] [--to REF]: - Walks a commit range, accumulating per-symbol history - Reports: first seen, last seen, commit count, change count - JSON with schema_version:2 - Answers: "find all Python functions introduced after v1.0" New store function walk_commits_between() for bounded range walking. mypy: 0 errors · typing_audit: 0 violations · pytest: 797 passed. * feat(code): Phase 3 — .muse/indices/ infrastructure and muse index command New module muse/core/indices.py: - symbol_history index: address → chronological event timeline (commit_id, op, content_id, body_hash, signature_id per event) - hash_occurrence index: body_hash → list of addresses that share it - load/save helpers for both indexes - index_info() for status reporting - All indexes: schema_version:1, updated_at timestamp, fully rebuildable New command muse index (multi-command Typer app): - muse index status: show present/absent/corrupt status and entry counts - muse index rebuild: walk full commit history to rebuild symbol_history and/or hash_occurrence; --index NAME for selective rebuild Design: - Indexes are derived, optional, and fully rebuildable — zero impact on repository correctness if absent - symbol_history enables O(1) lineage/symbol-log instead of O(commits) scan - hash_occurrence enables O(1) clone detection and hash= queries - Incremental updates can be wired into the commit hook in a future phase mypy: 0 errors · typing_audit: 0 violations · pytest: 797 passed. * feat(code): Phase 5 — multi-agent coordination layer New storage layer muse/core/coordination.py: - .muse/coordination/reservations/<uuid>.json advisory symbol leases - .muse/coordination/intents/<uuid>.json declared operations - Reservation: address list + run_id + branch + TTL-based expiry - Intent: extends a reservation with operation type and detail string - All records: write-once, schema_version:1, expiry-enforced by is_active() Six new coordination commands: muse reserve ADDRESS... --run-id ID --ttl N --op OP Advisory symbol reservation. Warns if addresses already reserved by another agent. Never blocks — purely coordination signal. muse intent ADDRESS... --op OP --detail TEXT --reservation-id UUID Declares a specific operation (rename/move/extract/delete/...) before executing it. Enables forecast to predict conflicts more accurately. muse forecast [--branch B] [--json] Predicts merge conflicts from active reservations and intents. Three conflict types: address_overlap (1.0), blast_radius_overlap (0.75), operation_conflict (0.9). Uses Python call graph for blast-radius. muse plan-merge OURS THEIRS [--json] Dry-run semantic merge plan. Classifies diverging symbols into: symbol_edit_overlap, rename_edit, delete_use, no_conflict. Reads committed snapshots — does not modify anything. muse shard --agents N [--language LANG] [--json] Partitions the codebase into N low-coupling work zones using import graph connectivity + greedy component partitioning balanced by symbol count. Reports cross-shard edges as coupling score. muse reconcile [--json] Reads coordination state + branch divergence, recommends merge ordering (fewer conflicts first) and integration strategy (fast-forward / rebase / manual) for each active branch. mypy: 0 errors · typing_audit: 0 violations · pytest: 797 passed. * feat(code): Phase 6 — ConflictRecord taxonomy, muse breakage, muse invariants MergeResult v2 — ConflictRecord taxonomy New ConflictRecord dataclass in domain.py carries structured conflict metadata alongside the existing conflicts: list[str]: - conflict_type: symbol_edit_overlap | rename_edit | move_edit | delete_use | dependency_conflict | file_level (legacy) - ours_summary / theirs_summary: short change descriptions - addresses: list[str] of involved symbol addresses Added as MergeResult.conflict_records: list[ConflictRecord] (default []). Fully backward-compatible — existing callers that don't populate it continue to work; plugins that implement StructuredMergePlugin can enrich it. muse breakage Detects symbol-level structural breakage in the working tree vs HEAD: - stale_import: imports a symbol no longer in the HEAD snapshot - missing_interface_method: class body missing methods found in HEAD Purely structural — no code execution, no type checker, no network. Operates on the committed symbol graph + current working-tree parse. Supports --language filter and --json output. muse invariants Enforces architectural rules from .muse/invariants.toml: - no_cycles: import graph must be acyclic (DFS cycle detection) - forbidden_dependency: source_pattern must not import forbidden_pattern - layer_boundary: lower layers must not import from upper layers - required_test: public functions must have corresponding test functions Minimal built-in TOML parser — no extra dependencies. All rules run against the committed snapshot; no working-tree parsing. Creates invariants.toml if absent — guided onboarding message shown. Supports --commit REF to check historical snapshots and --json output. mypy: 0 errors · typing_audit: 0 violations · pytest: 797 passed. * feat(code): Phase 7 — semantic versioning metadata on StructuredDelta + CommitRecord SemVerBump type alias (Literal["major", "minor", "patch", "none"]) StructuredDelta gains two optional v2 fields: sem_ver_bump: inferred impact of this delta on the public API breaking_changes: sorted list of symbol addresses whose public contract was removed or incompatibly changed infer_sem_ver_bump(delta) → (SemVerBump, list[str]) Pure function in domain.py, domain-agnostic: delete public symbol → major + address added to breaking_changes rename public symbol → major + address added to breaking_changes signature_only change → major + breaking insert public symbol → minor impl_only (body changes) → patch metadata/formatting only → none PatchOp child_ops → recurse into children (structural) CommitRecord gains: sem_ver_bump: SemVerBump = "none" (default for legacy + non-code) breaking_changes: list[str] = [] commit command: After computing structured_delta, calls infer_sem_ver_bump() and stores both on the delta and the CommitRecord. First commit (no parent) stays at "none" / []. muse log: Long-form view now shows SemVer: MAJOR / MINOR / PATCH when non-none, plus first 3 breaking-change addresses ("+N more" when list is longer). mypy: 0 errors · typing_audit: 0 violations · pytest: 797 passed. * feat(code): Phase 1 — lineage, api-surface, codemap, clones, checkout-symbol, semantic-cherry-pick (#62) Six new code-domain commands, all additive with zero schema changes: muse lineage ADDRESS Full provenance chain of a symbol through commit history: created, renamed_from, moved_from, copied_from, modified, deleted. Rename/move detected by matching content_id across Insert+Delete pairs. muse api-surface [--diff REF] Public API surface at a snapshot. With --diff REF shows added/removed/ changed public symbols between two commits. Public = kind in {function,class,method,...} and name not starting with _. muse codemap [--top N] Semantic topology: modules ranked by size, import in-degree, import cycle detection via DFS, high-centrality symbols, boundary files (high fan-out, zero fan-in). Reveals codebase structure at a glance. muse clones [--tier exact|near|both] Exact clones: same body_hash at different addresses (copy-paste). Near-clones: same signature_id, different body_hash (same contract, diverged implementation). Cluster output with member addresses. muse checkout-symbol ADDRESS --commit REF [--dry-run] Restore a single symbol from a historical commit into the working tree. Only the target symbol's lines change; everything else is untouched. --dry-run prints the unified diff without writing. muse semantic-cherry-pick ADDRESS... --from REF [--dry-run] [--json] Cherry-pick named symbols from a historical commit, not entire files. Applies each symbol patch to the working tree at the symbol's current location; appends at end if symbol is not in the current tree. All six commands support --json and --commit REF (where applicable). mypy: 0 errors · typing_audit: 0 violations · pytest: 797 passed. Co-authored-by: Gabriel Cardona <gabriel@tellurstori.com> * feat(code): Phase 4 — metadata_id, canonical_key, composite refactor classification (#65) SymbolRecord gains two new fields (backward-compatible: "" for pre-v2 records): metadata_id SHA-256 of symbol metadata that wraps the body without being part of it: decorators + async flag for Python functions, decorator list + bases for Python classes. Stubbed ("") for tree-sitter adapters — future adapters can enrich this by reading modifier/annotation nodes. canonical_key Stable machine handle: {file}#{scope}#{kind}#{name}#{lineno}. Disambiguates overloads and nested scopes. Unique within a snapshot. Enables agent-to-agent symbol handoff without re-querying. New classification helpers: muse/plugins/code/_refactor_classify.py classify_exact() hash-based exact classification: rename | move | rename+move | signature_only | impl_only | metadata_only | full_rewrite classify_composite() batch heuristic classification across a set of added/removed symbols — detects extract, inline, split, merge with confidence scores and evidence RefactorClassification full typed result (to_dict() → JSON-ready) muse detect-refactor --json now emits schema_version:2 with total count. Python _make_*_record() helpers thread file_path and class_prefix for accurate canonical_key computation. tree-sitter TreeSitterAdapter uses scope prefix extracted from the qualified_name. mypy: 0 errors · typing_audit: 0 violations · pytest: 797 passed. Co-authored-by: Gabriel Cardona <gabriel@tellurstori.com> * feat(code): Phase 7 — semantic versioning metadata on StructuredDelta + CommitRecord (#68) SemVerBump type alias (Literal["major", "minor", "patch", "none"]) StructuredDelta gains two optional v2 fields: sem_ver_bump: inferred impact of this delta on the public API breaking_changes: sorted list of symbol addresses whose public contract was removed or incompatibly changed infer_sem_ver_bump(delta) → (SemVerBump, list[str]) Pure function in domain.py, domain-agnostic: delete public symbol → major + address added to breaking_changes rename public symbol → major + address added to breaking_changes signature_only change → major + breaking insert public symbol → minor impl_only (body changes) → patch metadata/formatting only → none PatchOp child_ops → recurse into children (structural) CommitRecord gains: sem_ver_bump: SemVerBump = "none" (default for legacy + non-code) breaking_changes: list[str] = [] commit command: After computing structured_delta, calls infer_sem_ver_bump() and stores both on the delta and the CommitRecord. First commit (no parent) stays at "none" / []. muse log: Long-form view now shows SemVer: MAJOR / MINOR / PATCH when non-none, plus first 3 breaking-change addresses ("+N more" when list is longer). mypy: 0 errors · typing_audit: 0 violations · pytest: 797 passed. Co-authored-by: Gabriel Cardona <gabriel@tellurstori.com> * Merge PR #68 — Phase 7 semantic versioning metadata on StructuredDelta + CommitRecord Clean merge — no conflicts. mypy: 0 errors · typing_audit: 0 violations · pytest: 797 passed. --------- Co-authored-by: Gabriel Cardona <gabriel@tellurstori.com>
e51a1033·Gabriel Cardona <cgcardona@gmail.com>
·
feat: code domain leverages core invariants, query engine, manifests, provenance, CRDT annotations, and op-log - Extract domain-agnostic InvariantChecker protocol and BaseReport/BaseViolation types into muse/core/invariants.py; MidiChecker updated to satisfy the protocol - Extract generic commit-history walk into muse/core/query_engine.py (CommitEvaluator, walk_history, format_matches) - Code domain: max_complexity, no_circular_imports, no_dead_exports, test_coverage_floor invariant rules (muse/plugins/code/_invariants.py) - Code domain: hierarchical FileEntry/ModuleManifest/PackageManifest/CodeManifest for partial re-parse (muse/plugins/code/manifest.py) - Code domain: query DSL evaluator with TypeGuard-narrowed CodeField/CodeOp Literals (_code_query.py) - Code domain: op-log integration recording PatchOp/child ops into the append-only log (_op_log_integration.py) - CommitRecord gains format_version=5, reviewed_by (ORSet), test_runs (GCounter), agent provenance fields - CLI: muse annotate, muse check, muse code-check, muse code-query commands added and wired into app.py - muse commit wires --agent-id/--model-id/--toolchain-id/--sign env-driven provenance - tomli fallback removed (Python 3.12 ships tomllib in stdlib); zero type: ignore, zero Any, zero bare collections - 1396 tests green; mypy strict + typing_audit --max-any 0 both pass
766ee24d·Gabriel Cardona <gabriel@tellurstori.com>
·
chore: add .hypothesis/ to .gitignore
545d8f02·Gabriel Cardona <gabriel@tellurstori.com>
·
Add mission-critical stress test suite (9 new files, 1716 tests total) (#76) Nine stress test modules targeting every complex data structure and algorithm in Muse: commit DAG traversal and merge-base BFS, content-addressed object store, all CRDT primitives (LWWRegister / ORSet / RGA / AWMap / GCounter / VectorClock), three-way merge engine, diff algorithms (LCS / tree / numerical / set / snapshot), 21-dimension MIDI merge, store provenance and format evolution, query engine DSL, and full E2E CLI workflows (init → commit → branch → merge → revert → stash → reset → tag → annotate). All 1716 tests pass; mypy zero errors; typing audit zero violations. Co-authored-by: Gabriel Cardona <gabriel@tellurstori.com>
119290fc·Gabriel Cardona <cgcardona@gmail.com>
·
docs: complete type-contracts.md refresh for v0.2.0 (#78) Brings the type reference fully up to date with the codebase: New sections: - CRDT Primitive Types (VectorClock, LWWRegister, ORSet, RGA, AWMap, GCounter) — class APIs, wire-format TypedDicts, lattice contracts - Op Log Types (OpEntry, OpLogCheckpoint, OpLog) - Diff Algorithm Types (SequenceInput/SetInput/TensorInput/MapInput/ TreeInput, EditStep, EditKind, TreeNode, DiffInput) - Code Plugin Types (SymbolKind, SymbolRecord, SymbolTree, LanguageAdapter, LangSpec, PythonAdapter, TreeSitterAdapter) Updated sections: - DomainOp: added MutateOp (with FieldMutation) — five leaf variants now documented; PatchOp recursion made explicit - StructuredDelta: sem_ver_bump and breaking_changes fields added; correct total=False noted - MergeResult: op_log and conflict_records fields added; ConflictRecord documented - CommitRecord/CommitDict: all 20 fields documented including structured_delta, sem_ver_bump, agent provenance, format_version 1–5 evolution table, reviewed_by (ORSet), test_runs (GCounter) - DomainSchema/ElementSchema: individual schema TypedDicts each get their own field table; MapSchema recursive value_schema documented - MIDI: INTERNAL_DIMS corrected to 21 dimensions with full list; NON_INDEPENDENT_DIMS added; DIM_ALIAS documented - MuseConfig: domain: dict[str, str] section added Mermaid diagrams: 11 total (was 9), all updated: 1 Domain Protocol and Plugin Contract (updated) 2 DomainOp Discriminated Union (new — shows FieldMutation and recursion) 3 Domain Schema and Diff Input Pairing (new — ElementSchema ↔ DiffInput) 4 CRDT Primitive Lattice (new — all 5 CRDTs) 5 Store Wire-Format and In-Memory Dataclasses (updated) 6 Op Log Types (new) 7 Merge Engine State (updated — adds MergeOpsResult) 8 Attributes and MIDI Dimension Merge (updated) 9 Code Plugin Types (new) 10 Configuration, Import, Stash, and Errors (consolidated) 11 Full Entity Dependency Overview (updated) Co-authored-by: Gabriel Cardona <gabriel@tellurstori.com>
e831ac24·Gabriel Cardona <cgcardona@gmail.com>
·
chore: bump to v0.1.2, require Python 3.13 - version: 0.1.1 → 0.1.2 - requires-python: >=3.12 → >=3.13 (venv already runs 3.13.2; all 1716 tests pass; mypy clean on 3.13 target) - mypy python_version: 3.12 → 3.13 - README and docs/README version strings updated to match Python 3.13 benefits relevant to this codebase: - 5–15% interpreter speedup - Better error messages (more specific, with suggestions) - copy.replace() for frozen dataclasses (MergeState, EditStep, TreeNode) - TypeVar defaults (PEP 696) for future generic work
fbca9c83·Gabriel Cardona <gabriel@tellurstori.com>
·
refactor: full Python 3.13 idiom pass - TypeGuard → TypeIs in _code_query.py: TypeIs additionally narrows the False branch (PEP 742), making _is_code_field / _is_code_op more precise - Callable / Iterator imports moved from typing → collections.abc in query_engine.py, _predicate.py, _query.py (canonical source since 3.9) - dataclasses.replace() → copy.replace() in midi_parser.py (PEP 698, Python 3.13 generic form; works on dataclasses, namedtuples, and any class implementing __replace__) mypy: 0 errors · typing_audit: 0 violations · 1716 tests green
675d76ba·Gabriel Cardona <gabriel@tellurstori.com>
·
chore: tighten dep floors to match installed versions All packages are already at their latest release; this commit advances the >= minimums in pyproject.toml so new installs get the same tested versions rather than potentially pulling in older releases. Runtime: typer >=0.14.0 → >=0.24.0 tree-sitter >=0.24.0 → >=0.25.0 (0.25 has API changes from 0.24) ts-javascript >=0.23.0 → >=0.25.0 ts-typescript >=0.23.0 → >=0.23.2 ts-java >=0.23.0 → >=0.23.5 ts-go >=0.23.0 → >=0.25.0 ts-c >=0.23.0 → >=0.24.1 ts-cpp >=0.23.0 → >=0.23.4 ts-c-sharp >=0.23.0 → >=0.23.1 ts-ruby >=0.23.0 → >=0.23.1 ts-kotlin >=1.0.0 → >=1.1.0 Dev: pytest >=9.0.0 → >=9.0.2 pytest-asyncio >=1.0.0 → >=1.3.0 anyio >=4.9.0 → >=4.12.0 mypy >=1.19.0 → >=1.19.1 hypothesis >=6.0.0 → >=6.100.0 mypy: 0 errors · typing_audit: 0 violations · 1716 tests green
9fb51900·Gabriel Cardona <gabriel@tellurstori.com>
·
fix: rename tour-de-force demo files to demo-{domain} tour-de-force-music.md → demo-midi.md tour-de-force-code.md → demo-code.md tour-de-force-script.md → demo-script.md Updated all references in docs/demo/README.md, docs/README.md, docs/reference/code-domain.md, and the cross-links at the bottom of each demo file. Also corrected the stale "v2 · Python 3.11 · music" footer in docs/demo/README.md to "v0.1.2 · Python 3.13 · MIDI".
05d6326d·Gabriel Cardona <gabriel@tellurstori.com>
·
ci: upgrade to Python 3.13, opt in to Node.js 24 runner - python-version: "3.12" → "3.13" in both ci.yml and pages.yml - FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true added to both workflows so actions/checkout@v4 and actions/setup-python@v5 run on Node 24 before it becomes the forced default on June 2 2026 - Rename stale step name "Generate Tour de Force" → "Generate demo pages"
fa688757·Gabriel Cardona <gabriel@tellurstori.com>
·
feat: supercharge .museattributes — base/union strategies, priority, comment, code domain wiring * feat: supercharge .museattributes — new strategies, priority, comment, code plugin wiring ## muse/core/attributes.py - Add `base` strategy: revert to the common merge-base version, discarding changes from both branches. Useful for generated files, lock files, or any path that must stay at a known-good state during a merge. - `union` strategy now fully documented: include all additions from both sides; honour deletions only when both agree; prefer left for binary blobs. - Add `comment` field to AttributeRule (documentation, zero runtime cost). - Add `priority` field to AttributeRule (int, default 0); load_attributes now stable-sorts rules by priority descending so higher-priority rules are always evaluated before lower-priority ones, regardless of file order. - VALID_STRATEGIES: 5 → 6 (base added). - AttributesRuleDict updated to total=False to accommodate optional fields. - Full module docstring rewrite covering all six strategies and all five fields. ## muse/plugins/code/plugin.py - CodePlugin.merge() now loads and respects .museattributes (was a documented gap — repo_root was accepted but never consulted). - All six strategies implemented at file level: ours, theirs, base, union, manual, auto. manual also fires on one-sided auto-resolved paths. - applied_strategies dict populated and returned in MergeResult. - CodePlugin.merge_ops() now applies attribute resolution at the symbol-level conflict_addresses set: extracts the file path from "file::symbol" addresses and checks rules, so "src/**/*.py" / strategy = "ours" suppresses symbol conflicts in those files, not just file-level manifest conflicts. - applied_strategies from both file-level and op-level resolution merged. ## muse/cli/commands/init.py - _museattributes_template completely rewritten with: - All six strategies with plain-English descriptions - All five rule fields documented inline - Domain-specific commented examples for MIDI, code, and generic repos - priority and comment fields demonstrated ## tests/test_core_attributes.py (+30 tests) - TestNewStrategies: base and union in VALID_STRATEGIES, parsed, resolved. - TestCommentField: parsed, defaults to empty, ignored at runtime. - TestPriorityField: parsed, defaults to 0, higher overrides lower, equal preserves order, negatives allowed, affects all strategies. - TestFullRuleComposition: round-trip of all new fields together, MIDI and code priority sort scenarios. ## tests/test_code_plugin_attributes.py (NEW, 14 tests) - Integration tests for CodePlugin.merge() × .museattributes: one test class per strategy (ours, theirs, base, union, manual, auto) plus priority ordering, no-repo-root degradation, and applied_strategies propagation through merge_ops(). mypy: 0 errors · typing_audit: 0 violations · 1747 tests green * docs: rewrite muse-attributes.md for v0.1.2 - Documents all six strategies including new base and union semantics - Documents comment and priority fields with usage guidance - Explains priority-based rule evaluation order with examples - Adds MIDI dimension reference (all 21 dims + aliases) - Adds code domain section (file-level and symbol-level wiring) - Four complete annotated examples: MIDI, code, genomics, any-domain - Documents applied_strategies in MergeResult and its CLI output - Links to related docs and type-contracts --------- Co-authored-by: Gabriel Cardona <gabriel@tellurstori.com>
12559ad7·Gabriel Cardona <cgcardona@gmail.com>
·
docs: update README for v0.1.2 - Code domain: promoted from 'planned' to 'second shipped domain' with full description (symbol-level merge, 11 languages, tree-sitter, DSL) - Repo structure: added code/ plugin, query_engine.py, stress test files, correct demo/ names, expanded commands list, code plugin attributes test - Dependencies: added tree-sitter, removed stale 'toml', corrected labels - Documentation section: added Code Domain link, expanded .museattributes description (six strategies, priority, comment), removed stale v0.1.1 tag - 'music query DSL' -> 'MIDI query DSL' in MIDI section - Footer: Muse v0.1.2 · Python 3.13 Co-authored-by: Gabriel Cardona <gabriel@tellurstori.com>
d59384eb·Gabriel Cardona <cgcardona@gmail.com>
·
feat: upgrade to Python 3.14, drop from __future__ import annotations Python 3.14.3 is the current stable release. PEP 649 (deferred annotation evaluation) is now the language default, making from __future__ import annotations redundant in every file. Changes ─────── - requires-python: >=3.13 → >=3.14 - mypy python_version: 3.13 → 3.14 - CI workflows: python-version "3.13" → "3.14" in both ci.yml and pages.yml - from __future__ import annotations removed from all 179 Python files (muse/, tests/, tools/) — annotations are now deferred by PEP 649 - .cursorrules: removed "Every Python file has from __future__ import annotations" rule; bumped stack version to Python 3.14 - AGENTS.md: same rule removed from Code Standards section - README.md + docs/demo/README.md: Python 3.13 → 3.14 in version footer and requirements list mypy: 0 errors · typing_audit: 0 violations · 1747 tests green Co-authored-by: Gabriel Cardona <gabriel@tellurstori.com>
e6786943·Gabriel Cardona <cgcardona@gmail.com>
·
Update GitHub Pages landing and demo pages for v0.1.2 - Rename MusicPlugin → MidiPlugin in arch diagram; add CodePlugin as second active plugin (symbol-level OT, 11 languages, tree-sitter AST) - Fallback domain data: "music" → "midi" with 10 named dimensions; add full "code" domain entry with 5 symbol dimensions and .museattributes capability - Ticker: both "midi" and "code" marked active; was just "music" - Protocol section intro updated to list MIDI and code as shipped domains - Ecosystem section lead updated: MIDI + code shipped, rest planned - Footer: v0.1.1 → v0.1.2, add Python 3.14 - Demo page default version fallback: 0.1.1 → 0.1.2
4fc807a6·Gabriel Cardona <gabriel@tellurstori.com>
·
Document all 6 CRDT primitives and MusicRGA on landing page - Add RGA and AWMap to _compute_crdt_demos(): live demos with HTML visualizations render when running under Python 3.14 venv - RGA demo: two agents insert notes concurrently; ID-ordered merge produces deterministic [C4, E4, G4] sequence - AWMap demo: agent A sets tempo, agent B sets key_sig; join has both (add-wins — concurrent removes cannot evict new tokens) - Update CRDT section text: "four" → "six battle-tested convergent data structures"; add MusicRGA paragraph explaining voice-aware note ordering (bass→tenor→alto→soprano) for concurrent MIDI edits - MIDI domain card: add "MusicRGA" capability pill and update description to mention voice-aware CRDT ordering
fb5abd2b·Gabriel Cardona <gabriel@tellurstori.com>
·
Rename MusicRGA → MidiRGA and purge all 'music plugin' terminology The MIDI plugin is the domain; 'music' was a legacy holdover. Class / file renames: - MusicRGA → MidiRGA throughout muse/plugins/midi/_crdt_notes.py - tests/test_music_plugin.py → tests/test_midi_plugin.py (git mv) - docs/crdt-music-rga.md → docs/crdt-midi-rga.md (git mv) Module docstrings (muse/plugins/midi/): - "Muse music plugin" → "Muse MIDI plugin" in midi_diff, manifest, midi_merge, _invariants, entity, __init__ Domain string literals: - domain="music" → domain="midi" in tour_de_force.py, render_html.py - to_structured_delta("music") → ("midi") in op_log.py example - _ICONS["music"] key → _ICONS["midi"] in render_domain_registry.py Agent / workspace rules: - AGENTS.md: MusicPlugin → MidiPlugin, music domain → MIDI domain, test_music_plugin.py → test_midi_plugin.py - .cursorrules: same, plus quick-reference table row updated Tests: - test_core_merge_engine.py: test_music_plugin_isinstance → test_midi_plugin_isinstance - tests/test_crdts.py: "music plugin" comment → "MIDI plugin" Docs: - demo-script.md: MusicPlugin.snapshot/merge → MidiPlugin.* - supercharge-plan.md: test_music_plugin_* refs → test_midi_plugin_* - muse-protocol.md, op-log.md, type-contracts.md: "music" example → "midi" - plugin-authoring-guide.md: "music": MidiPlugin() → "midi": MidiPlugin() - muse-domain-concepts, muse-variation-spec, muse-vcs: music plugin → MIDI plugin - render_domain_registry.py: MusicRGA → MidiRGA capability pill + CRDT text
04004b82·Gabriel Cardona <gabriel@tellurstori.com>
·
Remove all 'Tour de Force' verbiage — rename to Demo everywhere File rename: - tools/tour_de_force.py → tools/demo.py (git mv) Internal changes in tools/demo.py: - Module docstring: "Tour de Force" → "Demo" - Class TourData → DemoData - Output file: tour_de_force.json → demo.json - Print messages: "Tour de Force" → "Demo" - argparse description updated tools/render_html.py: - Docstring, TourData → DemoData, all json path references CI (.github/workflows/pages.yml): - run: python tools/tour_de_force.py → python tools/demo.py Docs: - docs/demo/demo-script.md: title + body references - docs/demo/demo-code.md: title "Tour de Force" removed - docs/demo/demo-midi.md: title "Music Plugin — Tour de Force" → "MIDI Plugin — Demo" - tools/README.md: section heading, file references, artifact table
fd2b304c·Gabriel Cardona <gabriel@tellurstori.com>
·
feat: add 21-dimensional MIDI demo page (Bach BWV 846 × Muse VCS) Self-contained GitHub Pages demo at midi-demo.html: - 533 authentic notes from Bach Prelude No. 1 in C Major (BWV 846) sourced from the music21 corpus (public domain, CC0) - Piano roll (D3.js SVG): 4-octave range, 3-voice color coding, scrollable 762px time axis with bar grid, animated playhead - Salamander Grand Piano audio via Tone.js (real piano samples from CDN) - Commit DAG (D3 SVG): 8 commits across 3 branches with edge routing, glow selection, branch color lanes, typewriter command log - 21-dimension panel: per-dimension activity bars with glow, grouped by category (core / expression / CC / effects / meta) - Dimension heatmap: commits × 21 dimensions grid with intensity color - CLI reference: 12 MIDI-specific commands with all flags and return values - Keyboard shortcuts: arrow keys (prev/next commit), space (play/pause) - tools/render_midi_demo.py: typed Python generator (mypy clean) - pages.yml: new "Generate MIDI demo page" step - Landing page: nav link + hero CTA for midi-demo.html
8c11c369·Gabriel Cardona <gabriel@tellurstori.com>
·
fix: escape backslash-n in JS split() call inside Python template string (#97) The template string used '\n' (Python newline) inside a single-quoted JavaScript string literal, producing a literal newline in the output and causing SyntaxError: Invalid or unexpected token in the browser. Co-authored-by: Gabriel Cardona <gabriel@tellurstori.com>
8d1c0847·Gabriel Cardona <cgcardona@gmail.com>
·
feat: redesign .museignore as TOML with domain-scoped sections (#100) * feat: redesign .museignore as TOML with domain-scoped sections Replace the flat gitignore-style .museignore with a TOML format that has [global] and [domain.<name>] sections. Global patterns apply to every domain; domain-specific sections are loaded only when the active plugin matches, keeping each plugin's ignore rules isolated from every other. The is_ignored() and _matches() functions are unchanged — callers now use load_ignore_config() + resolve_patterns(config, domain) instead of load_patterns(). Also adds from __future__ import annotations to all muse/ modules that were missing it, fixing pre-existing forward-reference NameErrors in TypedDicts (PatchOp/DomainOp, MapSchema/ElementSchema) and dataclasses (AWMap, GCounter, TreeNode, etc.) that broke test collection. * feat: complete .museignore TOML integration across codebase - muse init now generates a domain-aware .museignore TOML template alongside .museattributes; template includes [global] + [domain.<name>] sections pre-filled for the chosen domain (midi/code/genomics/simulation/ spatial; generic stub for unknown domains). - ScaffoldPlugin.snapshot() now honours .museignore via load_ignore_config + resolve_patterns — the reference template for new plugin authors now demonstrates the correct ignore contract. - Docstrings updated in provenance.py, manifest.py, entity.py to reference the TOML format and correct sections ([global] for keys, [domain.midi] for rebuildable caches). - Docs updated: architecture/muse-vcs.md, reference/type-contracts.md, reference/muse-attributes.md, agent-provenance.md, entity-identity.md all now describe the TOML format and include concrete examples. - Tests: 8 new init tests (valid TOML, global section, domain section for midi/code, no-overwrite on reinit, parseable by load_ignore_config); 8 new scaffold plugin snapshot tests (global ignore, domain-specific, cross-domain isolation, negation, domain negation overrides global). --------- Co-authored-by: Gabriel Cardona <gabriel@tellurstori.com>
bda49bdb·Gabriel Cardona <cgcardona@gmail.com>
·
feat: add remote sync commands (remote, clone, fetch, pull, push, ls-remote) Implements the full porcelain + plumbing surface for MuseHub sync.
99c4ff79·Gabriel Cardona <cgcardona@gmail.com>
·
feat: overhaul MIDI demo — funky groove, multi-synth audio, DAW track view (#99) Replace Bach arpeggios with an original "Groove in Em" composition: - 5 instruments: drums (kick/snare/hat/ghost/crash), bass, electric piano, lead synth, brass A (staccato stabs) + brass B (legato pads) - 96 BPM, E minor, 8-bar loop per commit (~20 s of audio each) - Multi-instrument Tone.js synthesis: MembraneSynth (kick), NoiseSynth (snare/ghost), MetalSynth (hi-hat/crash), MonoSynth (bass), PolySynth-FMSynth (electric piano), PolySynth-Synth (lead/brass) 5-act VCS narrative (13 commits, 4 branches): Act 1 Foundation · Act 2 Divergence (feat/groove + feat/harmony) Act 3 Clean Merge · Act 4 Conflict (cc_reverb on brass branches) Act 5 Resolution — all 21 MIDI dimensions active, v1.0 tag Visual improvements: - DAW-style multi-track view (per-instrument rows with pitch mapping) - Fixed pause/resume (saves position, resumes from pausedAt) - Heatmap column highlight on commit select - Branch legend with color-coded blobs - Cleaner command log with ⚠/✓/$ line classification All 1747 tests pass · 0 mypy errors · 0 typing_audit violations Co-authored-by: Gabriel Cardona <gabriel@tellurstori.com>
47f42f27·Gabriel Cardona <cgcardona@gmail.com>
·
feat: three-tier CLI architecture — plumbing, core porcelain, semantic porcelain (#104) Formally separates Muse's command surface into three tiers with clean namespace boundaries and strict contracts: Tier 1 — Plumbing (muse plumbing …) 12 new machine-readable, JSON-outputting, pipeable commands that expose the raw engine: hash-object, cat-object, rev-parse, ls-files, read-commit, read-snapshot, commit-tree, update-ref, commit-graph, pack-objects, unpack-objects, ls-remote (moved from top-level). Tier 2 — Core Porcelain (muse … top-level, unchanged paths) All VCS commands stay at the root: init, commit, status, log, diff, show, branch, checkout, merge, reset, revert, cherry-pick, stash, tag, remote, clone, fetch, pull, push, check, annotate, domains, attributes. Tier 3 — Semantic Porcelain (muse midi …, muse code …, muse coord …) All domain-specific commands moved into namespaced sub-Typers: 11 midi commands, 30 code commands, 6 coord commands. Also: - ApplyResult TypedDict replaces bare int return from apply_pack(), giving callers structured counts of commits/snapshots/objects written vs skipped. - docs/reference/cli-tiers.md — authoritative tier reference with JSON schemas for every Tier 1 output format and extension guide for new domains. - 41 new plumbing tests (test_cli_plumbing.py), all tests updated for new command paths: 1903 total passing. Co-authored-by: Gabriel Cardona <gabriel@tellurstori.com>
189a2e45·Gabriel Cardona <cgcardona@gmail.com>
·
fix(docs): update all stale command paths and version refs for three-tier CLI (#106) - Replace every old flat-namespace command (muse notes, muse harmony, muse hotspots, muse patch, muse query, muse reserve …) with the correct three-tier path (muse midi …, muse code …, muse coord …) across all 12 affected doc files. - Update docs/README.md: replace "15 commands" count with three-tier description; add cli-tiers.md to Quick Navigation and directory map. - Expand CLI Command Reference in muse-vcs.md into three explicit tier sections (Tier 1 plumbing, Tier 2 porcelain, Tier 3 semantic). - Fix module tree in muse-vcs.md: "14 commands + domains" → accurate Tier 1/2 description. - Bump version references from v0.1.1 → v0.1.2 across all docs. - Update test count from 691 → 1903 in muse-vcs.md. Co-authored-by: Gabriel Cardona <gabriel@tellurstori.com>
9ba2cecc·Gabriel Cardona <cgcardona@gmail.com>
·
fix: escape backslash-n in JS split() call inside Python template string (#97) (#98) The template string used '\n' (Python newline) inside a single-quoted JavaScript string literal, producing a literal newline in the output and causing SyntaxError: Invalid or unexpected token in the browser. Co-authored-by: Gabriel Cardona <gabriel@tellurstori.com>
a62ee920·Gabriel Cardona <cgcardona@gmail.com>
·
feat(demo): midi-demo UX overhaul + track artifacts in git (#110) - Remove hero section; fold title, stats, and instrument tags into nav bar - Commit nav uses ↑ ↓ arrows (matches vertical DAG); play button moves above piano roll alongside scrub controls - Scrub bar: ⏮ ◀◀ ▶▶ ⏭ buttons reposition playhead without corrupting timeline (seekPos pattern replaces stale _pauseStartWall bug) - Remove VCS Demo link from index.html nav and hero CTA; update MIDI Demo button label - Command Log relocated from left column to right, beside the DAW piano roll - 21 MIDI Dimensions panel relocated beside the heatmap - DAW and heatmap panels pinned to 720 px; command log and dims panels flex to fill remaining width - Heatmap active-column highlight: glow fill, border, dimmed inactive columns, bold SHA label - Fix <s> strikethrough in CLI reference card (rename placeholder to <strategy>) - Unignore artifacts/*.html; keep *.json ignored Co-authored-by: Gabriel Cardona <gabriel@tellurstori.com>
bb7b9661·Gabriel Cardona <cgcardona@gmail.com>
·
fix(ci): deploy committed artifacts instead of regenerating from scripts (#112) Artifacts are now hand-edited and committed to git, so the generation steps (demo.py, render_midi_demo.py, render_domain_registry.py) would overwrite those edits on every deploy. Skip them and upload directly. Co-authored-by: Gabriel Cardona <gabriel@tellurstori.com>
6f003e3a·Gabriel Cardona <cgcardona@gmail.com>
·
fix(ci): run CI only on PRs targeting main, not direct pushes (#113) Co-authored-by: Gabriel Cardona <gabriel@tellurstori.com>
b129fc47·Gabriel Cardona <cgcardona@gmail.com>
·
feat(demo): replace porcelain CLI reference with muse midi semantic porcelain commands (#114) Replace the 12 generic VCS commands with all 11 muse midi Tier 3 semantic porcelain commands: notes, note-log, note-blame, harmony, piano-roll, hotspots, velocity-profile, transpose, mix, query, check. Flags and return values sourced directly from the command implementations. Update section heading to 'muse midi — Semantic Porcelain Commands'. Co-authored-by: Gabriel Cardona <gabriel@tellurstori.com>
fd827880·Gabriel Cardona <cgcardona@gmail.com>
·
feat(midi-demo): add live output examples to all 11 semantic porcelain command cards Each command card in the CLI reference section now shows a syntax-highlighted terminal example — real-looking output with colour-coded pitches, velocities, chord names, heatmap glyphs, and invariant reports — so visitors can see what each command produces without running it.
a702a354·Gabriel Cardona <gabriel@tellurstori.com>
·
feat(code-demo): interactive code plugin demo — Hexagon API Gateway Adds artifacts/code-demo.html: a full interactive demo for the muse code semantic porcelain, structured to mirror the MIDI demo in quality and depth. The scenario — 4 AI agents build a Python API gateway in parallel: - D3 commit DAG showing 3 concurrent branches merging with 0 conflicts - Force-positioned symbol graph (28 symbols, 8 files, typed by kind + agent) - Three graph modes: Graph · Dead Code (dead symbols glow) · Impact (blast radius) - 21 code dimensions heatmap across all 6 commits - Per-commit analyze panel with pre-rendered muse code command output - Symbol info panel: kind, file, agent, born SHA, callers, callees - Agent activity panel with per-agent commit and symbol ownership - 14 CLI command cards with syntax-highlighted example output - Nav cross-links between MIDI Demo ↔ Code Demo ↔ index.html
61b9592c·Gabriel Cardona <gabriel@tellurstori.com>
·
feat(midi): add 20 new semantic porcelain commands (#120) Implements a comprehensive suite of musical analysis, transformation, and multi-agent coordination commands for the MIDI plugin, each impossible in Git's binary-blob model. New analysis commands (read-only, support --commit and --json): muse midi rhythm — syncopation, swing ratio, quantisation accuracy muse midi scale — scale/mode detection (15 scales, all roots) muse midi contour — melodic contour shape (arch/ascending/wave/…) muse midi density — notes-per-beat per bar, textural arc muse midi tension — harmonic tension curve from interval dissonance muse midi cadence — authentic/deceptive/half/plagal cadence detection muse midi motif — recurring interval-pattern (motif) detection muse midi voice-leading — parallel fifths/octaves and large-leap lint muse midi instrumentation — per-channel note range, register, velocity map muse midi tempo — BPM estimation via IOI voting muse midi compare — semantic diff across key, rhythm, density, swing New transformation commands (write to working tree, support --dry-run): muse midi quantize — snap onsets to a rhythmic grid (8 grid values) muse midi humanize — add timing/velocity jitter for human feel muse midi invert — melodic inversion around a pivot pitch muse midi retrograde — reverse pitch order (retrograde transformation) muse midi arpeggiate — convert chord voicings to arpeggio sequences muse midi normalize — rescale velocities to a target dynamic range New multi-agent and search commands: muse midi shard — partition composition into bar-range shards muse midi agent-map — bar-level blame across commit history muse midi find-phrase — similarity search for a phrase across history Supporting changes: muse/plugins/midi/_analysis.py — pure typed analysis helpers (8 functions) tests/test_midi_semantic.py — 64 new tests covering all new commands muse/cli/app.py — updated docstring and registrations Passes mypy (0 errors, 168 files), typing_audit (0 violations), and the full pytest suite (1967/1967 green). Co-authored-by: Gabriel Cardona <gabriel@tellurstori.com>
630bfa59·Gabriel Cardona <cgcardona@gmail.com>
·
feat(demo): full 31-command MIDI porcelain reference + docs (#121) Expands the MIDI demo page and documentation to cover all 31 semantic porcelain commands introduced across the original 11 and the new 20. midi-demo.html: - CLI reference section now organised into 8 named groups with header cards (icon · title · desc · command count) - Group 1 — Notation & Visualization (notes, piano-roll, instrumentation) - Group 2 — Pitch, Harmony & Scale (harmony, scale, contour, tension, cadence) - Group 3 — Rhythm & Dynamics (rhythm, tempo, density, velocity-profile) - Group 4 — Structure & Voice Leading (motif, voice-leading, compare) - Group 5 — History & Attribution (note-log, note-blame, hotspots) - Group 6 — Multi-Agent Intelligence (agent-map, find-phrase, shard, query) - Group 7 — Transformation (transpose, invert, retrograde, quantize, humanize, arpeggiate, normalize, mix) - Group 8 — Invariants & Quality Gates (check) - All 31 cards include live terminal examples with syntax-highlighted output - Added .cli-group-section/.cli-group-hd CSS; group count badges docs/demo/demo-midi.md: - Added Acts X–XV covering all 20 new commands with full CLI output examples, usage snippets, and agent workflow notes - Full 31-command matrix table at the bottom, grouped by category - Updated "For AI Agents" section to reference all six capability dimensions Co-authored-by: Gabriel Cardona <gabriel@tellurstori.com>
2cf7f853·Gabriel Cardona <cgcardona@gmail.com>
·
docs: rename demo-midi.md → midi-demo.md and add full MIDI domain reference - Rename docs/demo/demo-midi.md → docs/demo/midi-demo.md to match the HTML artifact - Add docs/reference/midi-domain.md: canonical reference for all 31 MIDI semantic porcelain commands with full flag tables, JSON schemas, and type definitions - Expand docs/reference/cli-tiers.md MIDI section from 11 to 31 commands, grouped by category (notation, harmony, rhythm, structure, history, multi-agent, transformation, invariants) - Update docs/README.md: add MIDI/Code domain reference links to navigation table and directory map - Update docs/demo/README.md: fix filename reference and expand command list - Update README.md and docs/reference/code-domain.md: fix stale demo-midi.md links
1f0a7cd9·Gabriel Cardona <gabriel@tellurstori.com>
·
fix: escape HTML angle brackets in midi-demo card renderer <S> in flag text (e.g. --strength <S>, --min-score <S>) was parsed by the browser as the <s> (strikethrough) element, bleeding strikethrough through the rest of each card. Add an esc() helper and apply it to cmd, desc, flags, and ret before inserting into innerHTML. The example field is left unescaped as it contains intentional HTML span tags.
c932f684·Gabriel Cardona <gabriel@tellurstori.com>
·
feat: align code-demo nav with midi-demo nav style Replaces the flat tag-row header in code-demo.html with the same three-zone layout used in midi-demo.html: - Left: 'Muse' logo (monospace, accent colour) - Centre: title 'Hexagon API Gateway · Muse VCS' + metadata row (6 acts · 6 commits · 4 branches · 25 symbols, module tags, conflict/agent/language badges) - Right: v0.1.2 version badge + MIDI Demo / Code Demo links Nav height bumped 52px → 60px and body padding-top updated to match.
f33a55d4·Gabriel Cardona <gabriel@tellurstori.com>
·
feat: add all 29 code semantic porcelain commands to code-demo Expands the CLI reference section at the bottom of code-demo.html from 14 commands to all 29, grouped into 7 categories matching the MIDI demo pattern (group header with icon, label, description, and count badge): 1. Snapshot & Inventory — symbols, languages, grep, codemap 2. Symbol Identity & Hist — blame, symbol-log, lineage, detect-refactor 3. Query & Search — query, query-history, find-symbol, code-query 4. Architecture & Quality — hotspots, stable, coupling, compare, clones, coverage, api-surface 5. Impact & Dependencies — deps, impact, dead, breakage 6. Write & Transform — patch, checkout-symbol, semantic-cherry-pick 7. Infrastructure & Gates — index, invariants, check Renderer updated to use GROUPS array + HTML-escape (esc()) for all text fields; example blocks with intentional span tags are exempt.
4ca766c9·Gabriel Cardona <gabriel@tellurstori.com>
·
fix: escape HTML angle brackets in midi-demo card renderer (#125) <S> in flag text (e.g. --strength <S>, --min-score <S>) was parsed by the browser as the <s> (strikethrough) element, bleeding strikethrough through the rest of each card. Add an esc() helper and apply it to cmd, desc, flags, and ret before inserting into innerHTML. The example field is left unescaped as it contains intentional HTML span tags. Co-authored-by: Gabriel Cardona <gabriel@tellurstori.com>
ce9bbf9d·Gabriel Cardona <cgcardona@gmail.com>
·
feat: scale code-demo up 25% via zoom:1.25 on html element Removes the need to manually browser-zoom to 125% on every load. A single CSS zoom on the root scales all layout, fonts, borders, fixed nav, and SVG panels proportionally without touching individual px values.
739e7c79·Gabriel Cardona <gabriel@tellurstori.com>
·
fix: prevent cli-card grid overflow with min-width:0 Grid items default to min-width:auto, allowing content wider than the grid column to burst out of the track. Setting min-width:0 + overflow:hidden constrains each card to its column width regardless of content length.
53891919·Gabriel Cardona <gabriel@tellurstori.com>
·
feat(code-demo): unify visual theme with MIDI demo - Match :root variables (--bg, --card, --card2, --border, --muted, --gold) to MIDI demo values so both pages share the same dark base - Add gradient text to .nav-title (white→accent), matching MIDI's treatment - Change nav-tag and nav-badge border-radius to 20px (pill shape) - Add tinted background fill to .nav-badge (consistent with MIDI badge) - Upgrade nav-tag.green/.gold backgrounds to match MIDI pill pattern - Move demo links from nav-right to the left (logo → separator → links → center → badge), matching MIDI's three-zone nav layout exactly - Switch nav from position:fixed to position:sticky; drop body padding-top - Match nav backdrop: rgba(13,17,24,0.92) instead of rgba(9,13,22,.92)
9cff8d6a·Gabriel Cardona <gabriel@tellurstori.com>
·
fix(code-demo): flip DAG to oldest-top newest-bottom (timeline order)
d3c4c1e1·Gabriel Cardona <gabriel@tellurstori.com>
·
fix(code-demo): graph mode buttons now actually filter the symbol graph - Replace svgEl.className= with svgEl.setAttribute('class',...): SVG elements expose className as a read-only SVGAnimatedString, so direct assignment silently fails and the graph-dead / graph-impact CSS classes never applied - Impact mode: auto-select Router.dispatch on entry so the impact subgraph is visible immediately rather than dimming everything with no feedback
cdb4a82a·Gabriel Cardona <gabriel@tellurstori.com>
·
fix(code-demo): impact mode updates per commit when navigating DAG - Replace hardcoded COMMIT_EDGES[5] with COMMIT_EDGES[state.cur] in selectSymbol so impact highlighting and callee/caller info always reflect the currently selected commit's call graph - Re-apply selectSymbol in selectCommit when graphMode==='impact' so navigating commits in impact mode re-computes the impact subgraph against the new commit's edges
3d0c2e1d·Gabriel Cardona <gabriel@tellurstori.com>
·
Merge dev into main (#128) * fix: escape HTML angle brackets in midi-demo card renderer <S> in flag text (e.g. --strength <S>, --min-score <S>) was parsed by the browser as the <s> (strikethrough) element, bleeding strikethrough through the rest of each card. Add an esc() helper and apply it to cmd, desc, flags, and ret before inserting into innerHTML. The example field is left unescaped as it contains intentional HTML span tags. * feat: align code-demo nav with midi-demo nav style Replaces the flat tag-row header in code-demo.html with the same three-zone layout used in midi-demo.html: - Left: 'Muse' logo (monospace, accent colour) - Centre: title 'Hexagon API Gateway · Muse VCS' + metadata row (6 acts · 6 commits · 4 branches · 25 symbols, module tags, conflict/agent/language badges) - Right: v0.1.2 version badge + MIDI Demo / Code Demo links Nav height bumped 52px → 60px and body padding-top updated to match. * feat: add all 29 code semantic porcelain commands to code-demo Expands the CLI reference section at the bottom of code-demo.html from 14 commands to all 29, grouped into 7 categories matching the MIDI demo pattern (group header with icon, label, description, and count badge): 1. Snapshot & Inventory — symbols, languages, grep, codemap 2. Symbol Identity & Hist — blame, symbol-log, lineage, detect-refactor 3. Query & Search — query, query-history, find-symbol, code-query 4. Architecture & Quality — hotspots, stable, coupling, compare, clones, coverage, api-surface 5. Impact & Dependencies — deps, impact, dead, breakage 6. Write & Transform — patch, checkout-symbol, semantic-cherry-pick 7. Infrastructure & Gates — index, invariants, check Renderer updated to use GROUPS array + HTML-escape (esc()) for all text fields; example blocks with intentional span tags are exempt. --------- Co-authored-by: Gabriel Cardona <gabriel@tellurstori.com>
f15d098c·Gabriel Cardona <cgcardona@gmail.com>
·
fix(code-demo): replace emoji nav buttons with clean SVG chevron icons
809f3497·Gabriel Cardona <gabriel@tellurstori.com>
·
chore: merge dev into main (#135) * fix: escape HTML angle brackets in midi-demo card renderer <S> in flag text (e.g. --strength <S>, --min-score <S>) was parsed by the browser as the <s> (strikethrough) element, bleeding strikethrough through the rest of each card. Add an esc() helper and apply it to cmd, desc, flags, and ret before inserting into innerHTML. The example field is left unescaped as it contains intentional HTML span tags. * feat: align code-demo nav with midi-demo nav style Replaces the flat tag-row header in code-demo.html with the same three-zone layout used in midi-demo.html: - Left: 'Muse' logo (monospace, accent colour) - Centre: title 'Hexagon API Gateway · Muse VCS' + metadata row (6 acts · 6 commits · 4 branches · 25 symbols, module tags, conflict/agent/language badges) - Right: v0.1.2 version badge + MIDI Demo / Code Demo links Nav height bumped 52px → 60px and body padding-top updated to match. * feat: add all 29 code semantic porcelain commands to code-demo Expands the CLI reference section at the bottom of code-demo.html from 14 commands to all 29, grouped into 7 categories matching the MIDI demo pattern (group header with icon, label, description, and count badge): 1. Snapshot & Inventory — symbols, languages, grep, codemap 2. Symbol Identity & Hist — blame, symbol-log, lineage, detect-refactor 3. Query & Search — query, query-history, find-symbol, code-query 4. Architecture & Quality — hotspots, stable, coupling, compare, clones, coverage, api-surface 5. Impact & Dependencies — deps, impact, dead, breakage 6. Write & Transform — patch, checkout-symbol, semantic-cherry-pick 7. Infrastructure & Gates — index, invariants, check Renderer updated to use GROUPS array + HTML-escape (esc()) for all text fields; example blocks with intentional span tags are exempt. * feat: scale code-demo up 25% via zoom:1.25 on html element Removes the need to manually browser-zoom to 125% on every load. A single CSS zoom on the root scales all layout, fonts, borders, fixed nav, and SVG panels proportionally without touching individual px values. * fix: prevent cli-card grid overflow with min-width:0 Grid items default to min-width:auto, allowing content wider than the grid column to burst out of the track. Setting min-width:0 + overflow:hidden constrains each card to its column width regardless of content length. * feat(code-demo): unify visual theme with MIDI demo - Match :root variables (--bg, --card, --card2, --border, --muted, --gold) to MIDI demo values so both pages share the same dark base - Add gradient text to .nav-title (white→accent), matching MIDI's treatment - Change nav-tag and nav-badge border-radius to 20px (pill shape) - Add tinted background fill to .nav-badge (consistent with MIDI badge) - Upgrade nav-tag.green/.gold backgrounds to match MIDI pill pattern - Move demo links from nav-right to the left (logo → separator → links → center → badge), matching MIDI's three-zone nav layout exactly - Switch nav from position:fixed to position:sticky; drop body padding-top - Match nav backdrop: rgba(13,17,24,0.92) instead of rgba(9,13,22,.92) * fix(code-demo): flip DAG to oldest-top newest-bottom (timeline order) * fix(code-demo): graph mode buttons now actually filter the symbol graph - Replace svgEl.className= with svgEl.setAttribute('class',...): SVG elements expose className as a read-only SVGAnimatedString, so direct assignment silently fails and the graph-dead / graph-impact CSS classes never applied - Impact mode: auto-select Router.dispatch on entry so the impact subgraph is visible immediately rather than dimming everything with no feedback * fix(code-demo): impact mode updates per commit when navigating DAG - Replace hardcoded COMMIT_EDGES[5] with COMMIT_EDGES[state.cur] in selectSymbol so impact highlighting and callee/caller info always reflect the currently selected commit's call graph - Re-apply selectSymbol in selectCommit when graphMode==='impact' so navigating commits in impact mode re-computes the impact subgraph against the new commit's edges --------- Co-authored-by: Gabriel Cardona <gabriel@tellurstori.com>
9fb2d692·Gabriel Cardona <cgcardona@gmail.com>
·
feat: muse auth + hub + config — paradigm-level identity architecture with security hardening * feat: introduce muse auth, muse hub, muse config — paradigm-level identity architecture Replaces the flat [auth] token-in-repo-config pattern with a three-command identity paradigm designed around Muse's two primary users: humans and agents. Core architectural shift ------------------------ - Credentials move out of .muse/config.toml and into ~/.muse/identity.toml (mode 0o600, never read by the snapshot engine, never accidentally committed). - The repository knows *where* the hub is ([hub] url in config.toml). The machine knows *who you are* (~/.muse/identity.toml). These two concerns are explicitly separated. - Agents and humans are first-class typed identities, not just bearer tokens. New: muse/core/identity.py -------------------------- Global identity store. IdentityEntry carries type ("human" | "agent"), name, hub-assigned id, token, and capabilities (agent capability strings). All operations are synchronous. Token is never logged. New: muse auth -------------- login [--token TOKEN] [--hub HUB] [--agent] — human or agent auth flow whoami [--json] — structured identity display logout [--hub HUB] — remove credentials New: muse hub ------------- connect <url> — anchor repo to MuseHub fabric, write [hub] url status [--json] — show connection + identity (agent-safe JSON mode) disconnect — remove hub association from this repo ping — HTTP health-check to hub endpoint New: muse config ---------------- show [--json] — display full config (credentials never included) get <key> — dotted key lookup (user.name, hub.url, domain.*) set <key> <val> — typed setter with namespace guards edit — open .muse/config.toml in $EDITOR Updated: muse/cli/config.py ---------------------------- - Added UserConfig, HubConfig TypedDicts; removed AuthEntry. - get_auth_token() now resolves via identity store (hub URL → identity.toml). - Added get_hub_url / set_hub_url / clear_hub_url helpers. - Added get_config_value / set_config_value for dotted-key access. - _dump_toml order: [user], [hub], [remotes.*], [domain]. Updated: muse init config template ------------------------------------ - Removed [auth] token stub — replaced with [hub] commented example. - Added [user] type = "human" field. - Inline guidance pointing to `muse hub connect` and `muse auth login`. All: mypy zero errors, typing_audit zero violations, 89 core tests green. * security: harden identity store against TOCTOU, symlink, and cleartext attacks Seven vulnerabilities found in the initial auth implementation and patched. TOCTOU race (identity.py) write_text() created the file world-readable (default umask 0o644), then chmod() restricted it — any process watching ~/.muse/ could read the token in that window. Fix: os.open() + os.fchmod(fd, 0o600) before the first byte is written, followed by os.fdopen() to write, followed by os.replace() for atomic swap. Non-atomic write (identity.py) A kill signal during write_text() left a partial TOML file — next read silently returns an empty identity store. Fix: write to a mkstemp() temp file in the same directory, then os.replace() atomically renames it over the target. Old file stays intact on crash. Symlink attack (identity.py) An attacker pre-placing a symlink at ~/.muse/identity.toml causes write_text() to follow it and overwrite whatever file the user has write access to. Fix: path.is_symlink() guard before any write; raises OSError on detection. Directory permissions (identity.py) mkdir(parents=True, exist_ok=True) without a mode argument created ~/.muse/ world-traversable (0o755). Local users could list the directory. Fix: os.chmod(dir_path, stat.S_IRWXU) after mkdir — restricts to 0o700. http:// cleartext token transmission (hub.py) _normalise_url() only added https:// when no scheme was present. An explicit http:// URL was stored and used, causing bearer tokens to be sent in cleartext on every push/pull. Fix: ValueError raised for any http:// URL; user shown the https:// equivalent. TOML injection in capabilities (identity.py) Capability strings were written raw into TOML array values — a capability containing " or \ would produce malformed TOML and corrupt the identity file. Fix: _toml_escape() applied to every capability string in _dump_identity(). Misleading warning on hub switch (hub.py) "To keep the old credentials, run: muse auth logout" was inverted — logout removes credentials, not keeps them. Fix: message now reads "Your credentials remain … To remove them: muse auth logout". Also: OSError from _save_all is now caught in auth login and surfaced as a clean ❌ message instead of a Typer traceback dump. All fixes verified: mypy zero errors, typing_audit zero violations, 89 tests green. * security: second-pass hardening — redirects, cleartext, locking, hostname normalisation Nine additional vulnerabilities patched. All verified with live tests. Redirect-with-credentials (transport.py) urllib.request.urlopen follows HTTP redirects by default, including HTTPS→HTTP downgrades and cross-host redirects, both of which would forward the Authorization: Bearer header to an unintended recipient. Fix: _NoRedirectHandler raises HTTPError on any redirect; _STRICT_OPENER is used for every request in HttpTransport._execute. Token over cleartext HTTP in transport (transport.py) A remote URL stored as http:// would cause the bearer token to be sent in plaintext on every push/pull — existing muse remote add had no HTTPS guard. Fix: _build_request raises TransportError before adding the Authorization header when the URL scheme is not https://. HTTPS enforcement in set_hub_url + config set (config.py) muse config set hub.url http://... bypassed the _normalise_url check in hub connect. Fix: set_hub_url now raises ValueError on non-HTTPS URLs; set_config_value routes hub.url writes through set_hub_url. Redirect refusal in ping (hub.py) _ping_hub used urllib.request.urlopen (default redirect-following). Fix: dedicated _NoRedirectHandler + _PING_OPENER; any redirect surfaces as an error with the attempted redirect target shown. Userinfo in hub URL key (identity.py) https://user:pass@musehub.ai stored "user:pass@musehub.ai" as the hostname identity key, embedding credentials in config.toml. Fix: _hostname_from_url strips userinfo (user:pass@) before the hostname is extracted. Case-insensitive hostname normalisation (identity.py) DNS is case-insensitive; musehub.ai and MUSEHUB.AI are the same host but would create two separate identity entries, causing lookup misses. Fix: _hostname_from_url normalises to lowercase unconditionally. Concurrent write race (identity.py) Two simultaneous muse auth login calls (e.g. parallel agents) both read the identity file, both modify in-memory, last writer wins and silently erases the other's entry. Fix: _identity_write_lock (fcntl.LOCK_EX on ~/.muse/.identity.lock) wraps the read-modify-write cycle in save_identity and clear_identity. TOML parse-error log leaks token fragment (identity.py) tomllib error messages include the offending line — a corrupted identity file could expose a token fragment in the log output. Fix: _load_all logs only type(exc).__name__, never exc itself. Shell history warning for --token CLI flag (auth.py) Tokens passed via --token appear in ~/.zsh_history and ps aux. Fix: login detects whether the token came from the CLI flag (token is not None and MUSE_TOKEN env var is absent) and emits a ⚠️ warning to stderr recommending the env-var pattern instead. All: mypy zero errors, typing_audit zero violations, 89 tests green. --------- Co-authored-by: Gabriel Cardona <gabriel@tellurstori.com>
80353726·Gabriel Cardona <cgcardona@gmail.com>
·
fix(security): full surface hardening — validation, path containment, parser guards, CLI bounds Introduces muse/core/validation.py as the central trust-boundary module (validate_object_id/ref_id/branch_name/domain_name, contain_path, sanitize_display, MAX_FILE_BYTES/RESPONSE_BYTES), then applies it across the entire codebase: Group 1 — ID validation - object_store: SHA-256 integrity check, atomic write via os.replace, per-read size cap, validate_object_id at object_path/restore_object. - store: glob-safe prefix scan (sanitize_glob_prefix), runtime type checks in CommitRecord.from_dict, branch/repo_id validation, trust-boundary validation in resolve_commit_ref + store_pulled_commit. - merge_engine: validate IDs in read_merge_state/apply_resolution, 50 000-ancestor cap in find_merge_base/_all_ancestors. Group 2 — Path containment - All 6 restore commands (checkout, merge, reset, revert, cherry_pick, stash): contain_path wraps every object restore. - branch/init/commit: validate_branch_name + validate_domain_name at entry points; HEAD-poisoning guard in commit._read_branch. - 7 semantic write commands + midi_shard: contain_path on all output paths; shard rebased_tick clamped to >= 0. Group 3 — Snapshot integrity - compute_snapshot_id / compute_commit_id: null-byte separators replace | and : to eliminate separator-injection attacks. - walk_workdir: symlinks skipped; hidden files/directories excluded. Group 4 — Parser / transport hardening - midi_parser: defusedxml via muse/core/xml_safe.py (SafeET), file-size cap, tempo=0 guard, Inf/NaN BPM guard, negative-divisions guard, root-is-None check. - midi_merge: sysex payload truncated to 64 KiB (MAX_SYSEX_BYTES). - transport: urlparse scheme check, redirect refusal via _STRICT_OPENER + _open_url helper (_HttpResponse Protocol for clean typing), Content-Length cap, streaming read cap, _assert_json_content guard on all three parse helpers. Group 5 — CLI bounds + display sanitization - log: --max-count enforced >= 1; history walking bounded by limit. - find_phrase / agent_map: --depth capped 1–10 000; --min-score 0–1. - humanize: --timing <= 1.0 beat, --velocity <= 127. - invert: --pivot validated 0–127. - All typer.echo paths: sanitize_display strips ANSI/control chars across log, tag, branch, checkout, merge, reset, revert, cherry_pick, commit, find_phrase, agent_map. Tests updated to use real SHA-256 hashes for object IDs, 64-char hex strings for commit/snapshot refs, and correct expectations for the new security behaviors (TransportError instead of JSONDecodeError, ValueError for content-integrity failures, etc.). All checks pass: mypy (0 errors), typing_audit (0 violations), pytest (1967/1967 green). Co-authored-by: Gabriel Cardona <gabriel@tellurstori.com>
8d5137ed·Gabriel Cardona <cgcardona@gmail.com>
·
chore: bump version to 0.1.3 Co-authored-by: Gabriel Cardona <gabriel@tellurstori.com>
cfb03701·Gabriel Cardona <cgcardona@gmail.com>
·
Add security test coverage and reference documentation - tests/test_core_validation.py: 108 unit tests for all 10 validation primitives in muse/core/validation.py (validate_object_id, validate_ref_id, validate_branch_name, validate_repo_id, validate_domain_name, contain_path, sanitize_glob_prefix, sanitize_display, clamp_int, finite_float), including a stress test that verifies contain_path rejects a corpus of traversal attempts. - tests/test_core_xml_safe.py: 14 tests verifying SafeET.parse() correctly parses valid MusicXML, blocks Billion Laughs entity expansion, and blocks XXE file-read attacks. - tests/test_cli_hub.py: 40 tests for muse hub connect/status/disconnect/ping — helper unit tests plus full CLI invocations with mocked network calls. Covers HTTPS enforcement, redirect refusal, JSON output structure, and identity display. - tests/test_cli_auth.py: 31 tests for muse auth login/whoami/logout — token resolution order (env var vs flag vs getpass prompt), identity storage and retrieval, JSON output, token masking, multi-hub support. - docs/reference/security.md: New — security architecture reference covering the trust boundary design, every validation guard, XML safety, HTTP transport hardening, snapshot integrity, identity store security, and size caps. - docs/reference/auth.md: New — complete muse auth reference with identity file format, all three subcommands, human and agent flows, env vars, and token security best practices. - docs/reference/hub.md: New — complete muse hub reference with hub vs remote distinction, all four subcommands, HTTPS enforcement rationale, redirect refusal design, and typical setup workflows. - docs/reference/remotes.md: Replace stale Token Lifecycle section (pointed to config.toml) with current pointer to auth.md. - docs/README.md: Add quick-nav links to auth.md, hub.md, security.md; update directory map; bump displayed version to v0.1.3. All gates: mypy 0 errors, typing_audit 0 violations, 2160 tests green.
368bcde6·Gabriel Cardona <gabriel@tellurstori.com>
·
feat: add tools/git2muse.py — replay Git commit graph into Muse Walks main and dev branches oldest-first, extracts each commit's file tree into muse-work/ via git archive, builds the Muse snapshot manifest, and writes CommitRecord objects directly to the Python API preserving original Git author names, emails, and timestamps. Result: 146 Muse commits on main+dev (143 main, 3 dev-only) mirroring the entire Git history. muse-work/ now holds the current codebase state and 'muse status' correctly diffs against the last replayed commit. Merge commits are skipped — they carry no unique tree delta; the parent chain on each branch reconstructs the DAG faithfully.
4d55bc0f·Gabriel Cardona <gabriel@tellurstori.com>
·
refactor: rename muse-work/ → state/ The working directory is where versioned *state* lives — aligning the name directly with Muse's core vocabulary ("domain-agnostic VCS for multidimensional state"). The old name read as a temp/build artefact directory; the new name is short, domain-agnostic, and self-documenting. Changes: - All source references updated: muse/, tests/, tools/, docs/ - Physical directory renamed: muse-work/ → state/ - .gitignore: add state/ entry (mirrors the old implicit exclusion) - .museignore comment updated (reference to state/ path) All gates green: mypy 0 errors, typing_audit 0 violations, 2160 tests.
dfaf1b77·Gabriel Cardona <gabriel@tellurstori.com>
·
feat: muse reflog, gc, archive, bisect, blame, worktree, workspace * feat: implement muse reflog, gc, archive, bisect, blame, worktree, workspace Seven production-quality VCS commands that complete Muse's core feature set: reflog — append-only per-ref journal of every HEAD movement; hooked into commit, checkout, reset, and merge so every operation is captured. Safety net for undoing accidental resets. gc — reachability walk from all live refs; prunes unreachable blobs from the object store. --dry-run shows impact before any deletion. Handles 200 orphans in a single pass. archive — exports any historical snapshot as tar.gz or zip. No .muse/ metadata included — distribution-only format. Supports --prefix, --ref, --output. bisect — binary search engine (muse/core/bisect.py) with start/bad/good/skip/ run/reset/log CLI subcommands. Converges in log₂(N) steps. Agent-safe: muse bisect run <cmd> automates the hunt using exit-code protocol (0=good, 125=skip, else=bad). blame — line-level attribution for any text file (muse/core/blame.py). Walks the commit graph, uses difflib SequenceMatcher to push each unchanged line back to the oldest commit that introduced it. --porcelain emits JSON per line for pipeline use. worktree — multiple simultaneous branch checkouts sharing one .muse/ store. Each worktree is a sibling directory (<repo>-<name>/state/). Metadata, HEAD files, and state/ population all handled atomically. Includes add/list/remove/prune subcommands. workspace — multi-repository composition manifest (.muse/workspace.toml). add/remove/list/status/sync subcommands. sync clones missing members and pulls existing ones. Designed for coordinator + worker agent swarms. Testing: 133 new tests (90 unit + 43 CLI integration) across 7 new test files. All 2293 tests pass. mypy: 0 errors. typing_audit: 0 violations. Documentation: 7 new reference pages + docs/README.md navigation updated. * chore: remove stray test archive from repo root * chore: ignore *.tar.gz and *.zip artifacts from muse archive --------- Co-authored-by: Gabriel Cardona <gabriel@tellurstori.com>
e0353dfe·Gabriel Cardona <cgcardona@gmail.com>
·
fix: resolve commit refs by short SHA prefix in muse diff muse diff <sha> was silently showing everything as deleted because get_commit_snapshot_manifest called read_commit directly (full ID only), bypassing the resolve_commit_ref prefix scanner.
26cae2ce·Gabriel Cardona <gabriel@tellurstori.com>
·
refactor: repo root is the working tree — remove state/ subdirectory The repository root is now the working tree, identical to how Git works: .muse/ is the internal store, everything else at the root is tracked. The state/ subdirectory was a design mistake that forced an awkward rsync dance every time the codebase was edited. This removes it entirely. Changes: - All CLI commands: workdir = root (was root / "state") - apply_manifest() in muse/core/workdir.py replaces the wipe-and-restore pattern (shutil.rmtree + mkdir) used in merge, reset, revert, cherry-pick, stash, pull, clone — the repo root is never wiped - All three domain plugins: repo_root = workdir (was workdir.parent) + added hidden-file/dir filtering so .muse/ is never snapshotted - muse init: no longer creates state/ subdirectory - muse/core/merge_engine.py apply_resolution: uses root directly - muse/core/worktree.py: worktree root is the working tree - muse/plugins/midi/_query.py: removed state/ fallback path - All 18 test files updated; 2293 tests pass - Deleted state/ directory and tools/sync_state.py (no longer needed) - .museignore: updated comment, added artifacts/ to global patterns
59a915a4·Gabriel Cardona <gabriel@tellurstori.com>
·
fix(git2muse): use temp dir for extraction — repo root is now the working tree
b105da0f·Gabriel Cardona <gabriel@tellurstori.com>
·
fix: directory ignore patterns and git2muse lstrip stripping dots (#152) Two bugs that caused ghost files in `muse status` after a git2muse replay: 1. tools/git2muse.py: lstrip('./') treated its argument as a character set, stripping the leading dot from hidden paths (.cursorignore → cursorignore, .github/ → github/). _should_exclude never matched them, so they landed in every Muse snapshot without dots. Fixed with removeprefix('./') which only removes the literal tar prefix. 2. muse/core/ignore.py: directory patterns (trailing /) were silently skipped instead of matching all files inside that directory. Fixed to match gitignore semantics: 'artifacts/' now ignores every file under artifacts/. Updated two tests that documented the old broken behaviour. Co-authored-by: Gabriel Cardona <gabriel@tellurstori.com>
e05a2ecf·Gabriel Cardona <cgcardona@gmail.com>
·
feat: stat cache — 86x faster muse status Implements a persistent stat-based file hash cache that eliminates redundant SHA-256 hashing on every snapshot() call. Architecture ------------ muse/core/stat_cache.py (new): - FileCacheEntry TypedDict: {mtime, size, object_hash, dimensions} The `dimensions` slot is populated by domain plugins with semantic hashes (AST symbol hash, note-event hash, etc.) so that future muse diff / merge can skip re-parsing unchanged files entirely. - StatCache class: load (versioned JSON), save (atomic temp+rename), get_cached (pre-stat fast path), get_object_hash (pathlib wrapper), set_dimension / get_dimension, prune (removes deleted-file entries). - load_cache() convenience helper; StatCache.empty() for test contexts. - Single canonical _hash_bytes / _hash_str — deletes three duplicate _hash_file() copies (code plugin, MIDI plugin, snapshot.py). Performance ----------- Profile showed pathlib.relative_to() was the hot path (500 ms of 770 ms for 824 files), not SHA-256. Two-pronged fix: 1. Replace pathlib.rglob() + sorted() with os.walk() in walk_workdir and all three plugin snapshot() methods. Hidden directory subtrees are pruned at the walk level (dirnames[:] = ...) so the walker never descends into .muse/, .git/, etc. String slicing computes rel paths in O(1) with no object allocation. 2. StatCache.get_cached() accepts pre-computed (mtime, size) from the walk loop, avoiding a redundant stat() syscall per file. Result on this repo (824 tracked files): Cold (first run, no cache) : ~200 ms Warm (cache fully hit) : 3.3 ms (86x vs pre-patch 284 ms) Forward compatibility --------------------- The dimensions: {} field is always written even when empty. Future domain plugins fill it with semantic hashes after parsing; the cache version field allows safe format evolution without corrupting old caches.
b7e24576·Gabriel Cardona <gabriel@tellurstori.com>
·
feat(bitcoin): add Bitcoin domain plugin — multidimensional VCS for on-chain and Lightning state Introduces `muse/plugins/bitcoin/` as the second production-grade MUSE domain plugin, implementing all three protocol levels: Core (MuseDomainPlugin): - snapshot() — walks workdir, hashes all non-hidden JSON files via StatCache - diff() — semantic PatchOp per recognized file (UTXO-level, channel-level, strategy field-level, fee/price time-series); falls back to ReplaceOp when the object store is unavailable - merge() — three-way merge with Bitcoin-aware double-spend detection: concurrent spends of the same UTXO produce a structured ConflictRecord with conflict_type="double_spend" before any tx reaches the mempool - drift() / apply() / schema() — standard contract implementations OT merge (StructuredMergePlugin): - merge_ops() — operation-level merge; flags concurrent DeleteOps on the same UTXO address pattern ("utxos.json::txid:vout") as double-spend conflicts CRDT (CRDTPlugin): - join() — convergent join across 7 dimensions: files_manifest/utxos/channels/strategy → AWMap (add-wins) labels/transactions/mempool → ORSet (add-wins) - to_crdt_state() / from_crdt_state() — lift/materialise - Satisfies all three CRDT lattice laws (commutativity, associativity, idempotency) — verified by test suite Domain schema: 10 named dimensions (utxos, transactions, labels, descriptors, channels, routing, strategy, execution, oracle_prices, oracle_fees, network, mempool). merge_mode="crdt". Watch-only by design: private keys are never stored. All descriptors are xpubs. The plugin versions your relationship with Bitcoin, not your secrets. Files: - muse/plugins/bitcoin/__init__.py - muse/plugins/bitcoin/_types.py — 15 TypedDicts covering all dimensions - muse/plugins/bitcoin/_query.py — pure-functional analytics layer - muse/plugins/bitcoin/plugin.py — full MuseDomainPlugin + OT + CRDT - tests/test_bitcoin_plugin.py — 84 tests, all green - muse/plugins/registry.py — registers "bitcoin" domain Verification: mypy muse/ → 0 errors (193 files) typing_audit --max-any 0 → 0 violations pytest tests/test_bitcoin_plugin.py → 84/84 passed
73edf876·Gabriel Cardona <gabriel@tellurstori.com>
·
feat(bitcoin): add semantic porcelain layer — 19 Bitcoin-idiomatic CLI commands Adds the full semantic porcelain tier for the Bitcoin domain plugin: Commands: balance, utxos, hodl, whale, moon, halving, fee, mempool, select-coins, consolidate, pnl, compare, stack, dust, strategy, oracle, check, provenance, privacy Supporting modules: - muse/plugins/bitcoin/_analytics.py — wallet summary, PnL, coin selection, fee window, consolidation planning, UTXO lifecycle analytics - muse/plugins/bitcoin/_loader.py — typed data loaders from object store and workdir for all Bitcoin dimensions - muse/core/snapshot.py — integrates StatCache into walk_workdir for faster incremental hashing Fixes: - wallet_summary: confirmed_balance_sat double-counted immature coinbase; spendable now computed as confirmed-minus-immature with correct semantics - test_bitcoin_analytics: RoutingPolicyRecord key fee_base_msat → base_fee_msat - typing: zero mypy errors, zero typing_audit violations (--max-any 0) - 2504 tests green
15cf97e9·Gabriel Cardona <gabriel@tellurstori.com>
·
fix: drop `remote list` subcommand — bare `muse remote` implies list `muse remote` now runs list by default (invoke_without_command callback), matching the git remote UX. The explicit `list` subcommand is removed. `-v` / `--verbose` moves to the callback so `muse remote -v` still works. Tests and docs updated accordingly.
6e6816e8·Gabriel Cardona <gabriel@tellurstori.com>
·
fix: wire walk_workdir into StatCache — was hashing every file on every call (#157) walk_workdir was calling hash_file() unconditionally, completely bypassing the StatCache that was written alongside it. Now it loads the cache via load_cache(), uses get_cached() in the inner loop, prunes stale entries, and saves atomically — making every subsequent muse status / commit / diff call skip re-hashing unmodified files. Fixes two pre-existing integration test failures in TestWalkWorkdirCacheIntegration. Co-authored-by: Gabriel Cardona <gabriel@tellurstori.com>
0df0b434·Gabriel Cardona <cgcardona@gmail.com>
·
fix: add types-defusedxml to dev deps — was installed locally but missing from pyproject.toml (#159) types-defusedxml was installed in the local venv (mypy passed locally) but not declared in [dependency-groups.dev], so CI never installed it. This caused three mypy errors on xml_safe.py: import-untyped (x2) and no-any-return. Adding the stubs package closes the gap between local and CI. Co-authored-by: Gabriel Cardona <gabriel@tellurstori.com>
81937acd·Gabriel Cardona <cgcardona@gmail.com>
·
feat: add Oh My ZSH plugin for Muse (all six phases) Domain-aware, agent-native ZSH integration spanning six implementation phases: §0 Core detection — zero-subprocess file reads for HEAD, domain, merge state; single batched python3 call for JSON/TOML meta; $MUSE_* env var cache with chpwd/preexec/precmd hooks. §1 Prompt — muse_prompt_info() with domain icons (♪ ⌥ ₿ ⬡), SemVer right- prompt, merge state (⚡ ← branch n-conflicts), agent session badge, and MUSE_AGENT_MODE=1 machine-parseable mode [domain|branch|dirty:N|merge]. §2 Completions — _muse companion file: ~50 top-level commands with descriptions; branch/tag/remote/SHA/file/config-key argument completion; all midi (25), code (28), coord (6), plumbing (12) subcommands with domain-specific descriptions; per-command flag completion. §3 Aliases — 30+ m-prefixed shortcuts (mst, mcm, mco, mlg, mdf, mbr…) plus domain shortcuts (mmidi, mcode, mcoord, mplumb). §4 Workflow functions — muse-new-feat/fix/refactor, muse-wip, muse-sync, muse-safe-merge (conflict list + editor launch), muse-quick-commit (domain-aware metadata prompts), muse-health, muse-who-last, muse-agent-blame. §5 Agent-native — muse-context (--json/--toml/--oneline), $MUSE_CONTEXT_JSON always exported, muse-agent-session/end/commit, JSONL session logging (pure ZSH printf, no subprocess), muse-sessions list/replay. §6 Visual tools — muse-graph (domain-colored ANSI, SemVer badges, agent markers), muse-timeline (vertical Unicode timeline), muse-diff-preview (bat/delta), muse-commit-browser/branch-picker/stash-browser (fzf). §7 Powerlevel10k — prompt_muse_vcs + instant_prompt_muse_vcs segments. §8 Keybindings — Ctrl+B branch picker, ESC-M commit browser, ESC-H health. §9 Hook system — MUSE_POST_COMMIT/CHECKOUT/MERGE_CMD user callbacks. Also: fix four pre-existing typing violations in tests/test_bitcoin_plugin.py (ScriptType/CoinCategory/CoinSelectAlgo as Literal params; _AnyBitcoinRecord union alias instead of object in _json_bytes).
9ef121d1·Gabriel Cardona <gabriel@tellurstori.com>
·
feat(omzsh-plugin): strip to minimal, secure shell integration Replaces the 1343-line plugin and 604-line completion file with a focused, auditable implementation: muse.plugin.zsh (~175 lines): - Detects Muse repo by walking up from $PWD (pure ZSH, zero forks) - Reads branch from .muse/HEAD with regex validation; rejects unsafe names and %-escapes the result before prompt interpolation - Reads domain from .muse/repo.json via a single python3 call, path passed via MUSE_REPO_JSON env var (never interpolated into -c string) - Dirty check runs only after a muse command (timeout-guarded) - Zero subprocesses on every prompt render - 15 core aliases (mst, mcm, mco, mlg, mlgo, mlgg, mdf, mbr, mtg, …) - No eval, no ls word-splitting, no session logging, no agent system _muse (~150 lines): - Completes all top-level commands with descriptions - Branch/tag/remote lookup via ZSH glob (no ls, no subprocess) - Subcommand dispatch for stash, remote, plumbing, commit flags Security fixes applied (all were present in the prior version): - Branch name prompt injection (% escaping + regex gate) - Python -c string injection (env var path passing) - eval of post-hook user commands (removed entirely) - ls word-splitting in completion (replaced with ZSH globbing) - Unvalidated commit_id used in file paths (removed code paths) - Hand-rolled JSONL escaping (removed session logging entirely) - muse-safe-merge opening paths from MERGE_STATE.json (removed)
838d4a3e·Gabriel Cardona <gabriel@tellurstori.com>
·
fix(omzsh-plugin): match Muse HEAD format (refs/heads/<branch>, no ref: prefix)
3ed08abf·Gabriel Cardona <gabriel@tellurstori.com>
·
feat(store): self-describing HEAD format with typed read/write API (#163) Muse HEAD file format --------------------- Before: bare path, no type tag refs/heads/main After: self-describing, explicit type prefix ref: refs/heads/<branch> — symbolic ref (on a branch) commit: <sha256> — detached HEAD (direct commit) The `ref:` prefix is adopted from Git because a file that can hold two semantically different things should declare which one it holds. The `commit:` prefix for detached HEAD is a Muse extension — Git stores a bare SHA, which is ambiguous (SHA-1? SHA-256?). Muse makes the type explicit and leaves room for future algorithm prefixes without changing the parsing rule. Changes ------- muse/core/store.py: - HeadState = SymbolicHead | DetachedHead (TypedDict union) - read_head(repo_root) → HeadState (typed parser, raises on malformed) - read_current_branch(repo_root) → str (raises ValueError in detached state) - write_head_branch(repo_root, branch) (writes "ref: refs/heads/<branch>") - write_head_commit(repo_root, commit_id) (writes "commit: <sha256>") Write sites updated (4): muse/cli/commands/init.py, checkout.py, clone.py, tools/git2muse.py Read sites updated (80+): Every muse/cli/commands/*.py that had the inline `(root / ".muse" / "HEAD").read_text().strip().removeprefix("refs/heads/")` pattern is replaced with `read_current_branch(root)`. muse/plugins/bitcoin/_loader.py: dead local `read_current_branch` deleted. btc_*.py commands: import moved from _loader to muse.core.store. Shell plugin (tools/omzsh-plugin/muse.plugin.zsh): _muse_parse_head updated — now matches "ref: refs/heads/" and "commit: " prefixes instead of heuristic shape checks. Tests (20 fixture writes + all assertions): Every `write_text("refs/heads/…")` in tests/ updated to the new format. Verification: mypy 0 errors · typing_audit 0 violations · 2504 tests green Co-authored-by: Gabriel Cardona <gabriel@tellurstori.com>
f7645c07·Gabriel Cardona <cgcardona@gmail.com>
·
fix(omzsh-plugin): restore ref: prefix check regressed by #163
b48b8742·Gabriel Cardona <gabriel@tellurstori.com>
·
feat(omzsh-plugin): show muse:(domain:branch) mirroring git:(branch)
3f48b6eb·Gabriel Cardona <gabriel@tellurstori.com>
·
feat(omzsh-plugin): drop icon from default prompt — domain name is explicit
1b4a7d8a·Gabriel Cardona <gabriel@tellurstori.com>
·
docs(omzsh-plugin): update for muse:(domain:branch) format and icon-off default
bb2f58b3·Gabriel Cardona <gabriel@tellurstori.com>
·
feat(status): color-code output by change type Yellow for modified, green for new files, red for deleted — applied only when stdout is a TTY so piped output stays clean without requiring --porcelain. Porcelain mode is explicitly never colored; short mode colors the letter prefix; default mode colors the label text.
5f930fbb·Gabriel Cardona <gabriel@tellurstori.com>
·
fix(omzsh-plugin): require repo.json for repo detection, pre-clear state on chpwd _muse_find_root previously accepted any .muse/ directory as a valid repo, causing false positives from stray or partial .muse/ directories in ancestor paths. Now requires .muse/repo.json to exist — the canonical marker of a properly initialised Muse repo. _muse_hook_chpwd now pre-clears MUSE_REPO_ROOT and MUSE_BRANCH before calling _muse_refresh, so any silent failure in refresh leaves the prompt blank rather than showing stale state from the previous directory. Adds muse_debug() for in-shell diagnosis when the prompt looks wrong.
5e504f28·Gabriel Cardona <gabriel@tellurstori.com>
·
feat(init): change default domain from midi to code
40401a76·Gabriel Cardona <gabriel@tellurstori.com>
·
feat(init): update default config.toml comments to code domain
5885c6a6·Gabriel Cardona <gabriel@tellurstori.com>
·
fix(init): reset repo.json schema_version to 1
e9145b5f·Gabriel Cardona <gabriel@tellurstori.com>
·
refactor: consolidate schema_version to single source of truth All schema_version fields across the codebase now read from muse/_version.py, which uses importlib.metadata to pull the version from pyproject.toml. No more hardcoded 1/2 integers scattered across plugins, CLI commands, TypedDicts, and tests. - Add muse/_version.py as the single version source - Change schema_version: Literal[1] -> str in all TypedDicts - Replace _SCHEMA_VERSION = 1/"1" constants with the imported __version__ - Update all inline {"schema_version": N} dicts across plugins and CLI - Update all test assertions to compare against __version__
8aa515d5·Gabriel Cardona <gabriel@tellurstori.com>
·
feat(zsh-plugin): yellow branch name is the dirty signal, no extra symbol
674a6394·Gabriel Cardona <gabriel@tellurstori.com>
·
fix(zsh-plugin): run dirty check on cd and shell load, not only after muse commands
72738443·Gabriel Cardona <gabriel@tellurstori.com>
·
fix(zsh-plugin): raise dirty timeout to 5s, add rc tracking to muse_debug
544644fd·Gabriel Cardona <gabriel@tellurstori.com>
·
debug(zsh-plugin): add MUSE_DEBUG=1 timestamped trace to find hang
6d13826f·Gabriel Cardona <gabriel@tellurstori.com>
·
fix(zsh-plugin): skip dirty check on repos with no commits
6c80c4a2·Gabriel Cardona <gabriel@tellurstori.com>
·
fix(zsh-plugin): remove no-commit guard that kept branch purple after muse init
2bf18039·Gabriel Cardona <gabriel@tellurstori.com>
·
debug(zsh-plugin): unconditional logs in dirty check, precmd, and prompt render
018f67e8·Gabriel Cardona <gabriel@tellurstori.com>
·
fix(zsh-plugin): fix bad arithmetic in debug log line
d06f9d07·Gabriel Cardona <gabriel@tellurstori.com>
·
fix(zsh-plugin): only branch turns yellow when dirty, domain stays magenta
33e13111·Gabriel Cardona <gabriel@tellurstori.com>
·
feat(zsh-plugin): branch green when clean, yellow when dirty
da797974·Gabriel Cardona <gabriel@tellurstori.com>
·
feat(zsh-plugin): revert branch to magenta when clean, yellow when dirty
4164158d·Gabriel Cardona <gabriel@tellurstori.com>
·
chore(zsh-plugin): remove debug logs
26b42fb8·Gabriel Cardona <gabriel@tellurstori.com>
·
fix(zsh-plugin): add unconditional verbose logs at every dirty-check step Logs every step of the init, refresh, dirty-check, precmd, chpwd, and prompt-render paths so we can see exactly what MUSE_DIRTY is at every decision point and why the branch colour is chosen.
13c730d5·Gabriel Cardona <gabriel@tellurstori.com>
·
fix(zsh-plugin): replace cd with env -C to stop chpwd recursion loop ZSH fires chpwd_functions inside subshells ($(...)). The dirty check was doing $(cd "$MUSE_REPO_ROOT" && muse status --porcelain), which triggered _muse_hook_chpwd inside the subshell, which called _muse_refresh → _muse_check_dirty → $(cd ...) → infinite recursion until FUNCNEST limit. Replace cd with env -C which changes the working directory at the OS level without invoking ZSH's cd builtin, so chpwd_functions never fires inside the subshell.
4f885bbe·Gabriel Cardona <gabriel@tellurstori.com>
·
fix(zsh-plugin): re-entry guard on chpwd to stop recursion, revert env -C env -C broke muse discovery (rc=127) because it spawns a plain process that does not inherit ZSH's venv/PATH activation. Revert to the ZSH subshell cd. The real recursion fix: add _MUSE_REFRESHING guard at the top of _muse_hook_chpwd. ZSH subshells inherit parent variables, so when the cd inside _muse_check_dirty fires chpwd inside the subshell, the guard is already 1 and the nested hook returns immediately — no recursion.
efce0034·Gabriel Cardona <gabriel@tellurstori.com>
·
fix(zsh-plugin): remove debug logs, fix chpwd recursion, require coreutils - Strip all debug print statements; clean single-prompt output - Re-entry guard (_MUSE_REFRESHING) on _muse_hook_chpwd prevents the infinite recursion that fired when cd inside the dirty-check subshell triggered chpwd_functions (subshells inherit parent variables) - Revert env -C back to cd — env -C broke muse discovery (rc=127) because it spawns a plain process outside ZSH's venv/PATH activation - timeout (GNU coreutils) is now an explicit dependency; install via brew
e6076746·Gabriel Cardona <gabriel@tellurstori.com>
·
fix(status): clean error when HEAD file is missing instead of raw traceback read_head() now catches FileNotFoundError and raises ValueError with a clear message. status catches ValueError from read_current_branch and prints a fatal: line, exiting cleanly instead of dumping a traceback.
d4014b3e·Gabriel Cardona <gabriel@tellurstori.com>
·
fix(zsh-plugin): refresh dirty state on every prompt, not just after muse commands Previously the dirty check only ran when _MUSE_CMD_RAN=1 (i.e. after a muse command). This meant touch, vim, cp etc. never updated the branch colour. Now precmd always calls _muse_check_dirty when inside a repo, and does a full refresh (head + domain + dirty) after a muse command. Same model as the git plugin.
9290bfc7·Gabriel Cardona <gabriel@tellurstori.com>
·
feat(code): full parser coverage for 15 languages + Markdown, CSS, HTML (#180) Expand the code domain plugin to achieve maximum symbol extraction across all supported file types, closing every previously identified gap. Language gap fills (existing tree-sitter adapters): - JS/JSX: arrow functions and function expressions bound to const/let captured as `function` symbols; async keyword detection via `async_node_child` field promotes to `async_function`/`async_method` - TS/TSX: same arrow-function patterns; `async_node_child` wired up - Go: package-level `const_spec` and `var_spec` captured as `variable` - Rust: `static_item`, `const_item`, `type_item` → `variable`; `mod_item` → `class` - C: `struct_specifier` and `enum_specifier` → `class` - C++: `enum_specifier` → `class`; out-of-class method definitions via `qualified_identifier`; `namespace_definition` → `class` - C#: `property_declaration` → `variable`; `record_declaration` → `class` - Java: `annotation_type_declaration` and `record_declaration` → `class` - Kotlin: `object_declaration` → `class`; top-level `property_declaration` → `variable` (uses `identifier` throughout — grammar has no `type_identifier`/`simple_identifier` at this version) New adapters: - MarkdownAdapter: extracts ATX headings (h1–h6) as `section` symbols via tree-sitter-markdown; degrades to file-level tracking without it - HtmlAdapter: extracts semantic elements (section, article, nav, h1–h6, …) and any element bearing an `id` attribute as `section` symbols via tree-sitter-html; degrades gracefully without the package - _CSS_SPEC / _SCSS_SPEC: `rule_set` → `rule`, `keyframes_statement` → `rule`, `media_statement` → `rule` via tree-sitter-css - _SWIFT_SPEC: full Swift symbol extraction (functions, classes, structs, enums, protocols, type aliases, computed properties, initialisers) via py-tree-sitter-swift; degrades to file-level tracking without it Infrastructure: - `SymbolKind` extended with `"section"` and `"rule"` literals - `LangSpec` gains `async_node_child: str` — empty string disables async promotion; non-empty names the direct child token to check - `TreeSitterAdapter.parse_symbols` honours `async_node_child` to promote `function`→`async_function` and `method`→`async_method` - pyproject.toml: adds tree-sitter-markdown, tree-sitter-html, tree-sitter-css as hard dependencies; documents Swift as optional - mypy overrides: adds stubs ignore for all four new grammar modules Tests: 141 tests in test_code_plugin.py (was 103); all 2545 suite green. Co-authored-by: Gabriel Cardona <gabriel@tellurstori.com>
8068d03a·Gabriel Cardona <cgcardona@gmail.com>
·
chore: bump version to 0.1.4 (#182) Co-authored-by: Gabriel Cardona <gabriel@tellurstori.com>
66dc5269·Gabriel Cardona <cgcardona@gmail.com>
·
fix(diff): semantic symbols in working-tree diff + correct A/D/M prefix (#184) Before this fix, `muse diff` on the working tree always fell back to a plain file-level `A README.md` for new/modified files because their blobs had never been written to the object store (that only happens on `muse commit`). Semantic symbol diffing worked fine between two *committed* snapshots, but not for the common case of comparing HEAD to the live tree. Fix 1 — plugin.py: _read_blob + _semantic_ops workdir fallback Added `_read_blob(repo_root, content_id, disk_fallback)` which first tries the object store, then reads the file from disk and verifies its SHA-256 matches the expected content_id before accepting it. `_semantic_ops` now accepts an optional `workdir` parameter; when set, target blobs not in the object store are fetched via `_read_blob` with the on-disk path as fallback. `CodePlugin.diff` passes `workdir=repo_root` unconditionally — the hash check ensures commit-to-commit diffs are never contaminated by stale on-disk content. Fix 2 — diff.py: correct prefix + inline child ops `_print_structured_delta` now correctly labels PatchOps: - All child ops are inserts → `A file` (newly added file) - All child ops are deletes → `D file` (fully removed file) - Mixed → `M file` (modification) Added `_print_child_ops` which renders up to 12 symbol changes inline with tree connectors (├─ / └─), summarising any overflow on one line. Previously, only the file path and a one-line summary were shown. Before: A README.md 1 added file After: A README.md ├─ added section h1: Overview ├─ added section h2: Installation └─ added section h2: Usage 1 added file Tests: 4 new regression tests in TestDiffWorkingTreeSymbols. 2549 tests total — all green. Co-authored-by: Gabriel Cardona <gabriel@tellurstori.com>
82d83229·Gabriel Cardona <cgcardona@gmail.com>
·
feat(cli): color-coded diff output + -h short flag for --help (#185) diff.py — color-coded status lines A (added) → green D (deleted) → red M (modified) → yellow R (moved) → cyan Child symbol ops inherit the same colour scheme: green for added symbols, red for removed, yellow for changed. app.py — context_settings with help_option_names All five Typer instances (cli, plumbing_cli, midi_cli, code_cli, coord_cli, bitcoin_cli) now set context_settings with help_option_names=['-h', '--help'] so -h works at every level of the command tree. Co-authored-by: Gabriel Cardona <gabriel@tellurstori.com>
589ae017·Gabriel Cardona <cgcardona@gmail.com>
·
fix: detect file-level move+edit as a single PatchOp When a file is renamed *and* its content changes simultaneously, the diff engine previously emitted a disconnected all-delete PatchOp for the old path and an all-insert PatchOp for the new path — losing the relationship between them entirely. The fix adds `_detect_file_move_edits` in `symbol_diff.py` which scores (removed_path, added_path) pairs by body_hash overlap and greedily collapses those with ≥50% overlap into a single `PatchOp` carrying `from_address` (old path) and `address` (new path), with `child_ops` being the actual symbol diff between the two trees. `PatchOp` in `domain.py` gains `from_address: NotRequired[DomainAddress]` to carry this metadata cleanly without a new op type. `diff.py` detects `from_address` and renders the file header in cyan as `R old_path → new_path`, matching the existing colour semantics (cyan = same content, different location — now extended to same-origin, different content+location). Three regression tests in `TestFileMoveAndEdit` cover: collapse to single PatchOp, correct child op types, and no false positives for unrelated files.
1afd410c·Gabriel Cardona <gabriel@tellurstori.com>
·
feat: add line numbers to diff child ops and fix move+edit symbols Line numbers - Every child op summary now includes the source range as `L{start}–{end}` appended inline (e.g. `added function add L4–8`). Data was already present on SymbolRecord; this threads it through to the display layer. Move+edit address normalisation - When diffing a renamed+edited file pair, strip the file-path prefix off both trees before calling diff_symbol_trees so that `math_utils.py::add` and `core_math.py::add` normalise to `add` on both sides. Previously every symbol looked deleted-and-added, producing spurious "moved to <name>" entries for functions that were in fact unchanged across the rename.
0637707c·Gabriel Cardona <gabriel@tellurstori.com>
·
feat: add muse cat and muse diff --text muse cat <file>::<symbol> [--at <ref>] Reads the blob from the object store, parses the symbol tree, matches by qualified_name then name (with ambiguity detection), and prints the exact source lines with a dim header showing address, line range, and ref. Available as both top-level `muse cat` and `muse code cat`. muse diff --text Drops to a line-level unified diff (difflib.unified_diff) for every changed file, with coloured +/- hunks and @@ headers. Works with the same commit arguments as `muse diff`. For working-tree diffs, falls back to reading from disk when a blob is not yet in the object store.
65b33a65·Gabriel Cardona <gabriel@tellurstori.com>
·
perf: lazy-load tree-sitter grammars to eliminate startup lag (#189) Every muse command (including muse init) was paying the full cost of loading tree_sitter and all 13+ language grammar packages at import time, because app.py eagerly imports every command module, and many of those transitively pulled in ast_parser.py, which ran the adapter registry build at module level. Changes: - ast_parser.py: move `from tree_sitter import ...` behind a TYPE_CHECKING guard; replace the module-level ADAPTERS/SEMANTIC_EXTENSIONS assignments with _adapters()/_semantic_extensions() lazy initializers. First access triggers grammar loading; subsequent accesses are O(1) dict lookups on promoted module attributes. TreeSitterAdapter.__init__ now takes a pre-built Query (removes the dead _language field). - _query.py: move SEMANTIC_EXTENSIONS import inside is_semantic() so importing _query never pulls in the grammar registry. - dead.py / deps.py: same SEMANTIC_EXTENSIONS deferral inside the helper functions that actually need it. - init.py: remove unused find_repo_root import (dead import). Co-authored-by: Gabriel Cardona <gabriel@tellurstori.com>
2cf7273b·Gabriel Cardona <cgcardona@gmail.com>
·
docs: require CI green before any merge (AGENTS.md + .cursorrules) Adds an explicit gate: no PR may be merged while GitHub Actions checks are yellow (pending) or red (failing). Updated in three places in each file — the task lifecycle steps, the verification checklist, and the anti-patterns list — so the rule appears wherever an agent or developer looks when deciding whether to merge.
c32279ba·Gabriel Cardona <gabriel@tellurstori.com>
·
fix: correct .muse init layout — bare repos, encoding, type safety, constants Bugs fixed: - Bare repos no longer receive .museattributes or .museignore; those files belong to the working tree and are meaningless in a bare repository. - Every write_text / read_text call in init.py and the HEAD/ref/JSON paths of store.py now specifies encoding="utf-8", closing a latent corruption risk on non-UTF-8 locales (Windows, some Linux configs). Commit messages and author names are Unicode and must be written with an explicit codec. - --force repo-id recovery now guards the .get() result with isinstance(str) before assigning to existing_repo_id: str | None, removing an unguarded Any → str assignment. Quality: - Five domain-block strings in _museignore_template were re-allocated on every muse init call and four were always discarded. Promoted to module-level constants (_MUSEIGNORE_DOMAIN_BLOCKS, _MUSEIGNORE_HEADER, _MUSEIGNORE_GLOBAL) so each string is interned once at import time. - Docstring updated: objects/ is created eagerly at init time, not lazily on first commit as the old text claimed.
afdf7861·Gabriel Cardona <gabriel@tellurstori.com>
·
perf+fix: muse status — eliminate rglob descent, stat cache, single repo.json read Bugs fixed: - status --short --branch: branch_only was never checked in short mode, causing the full file list to be printed instead of returning early. Fixed by moving the branch_only guard after both the porcelain and short/default header output. - _read_repo_id: no encoding, no error handling (KeyError / FileNotFoundError / JSONDecodeError all unhandled). Replaced with _read_repo_meta() which reads repo.json once with encoding="utf-8" and degrades gracefully on any failure. - registry._read_domain: missing encoding="utf-8" on repo.json read. Fixed. - repo.json read 3x per muse status: _read_repo_id, resolve_plugin, read_domain each triggered an independent read. Now status reads repo.json exactly once via _read_repo_meta(), passes domain to the new resolve_plugin_by_domain(), and reuses the domain string for SnapshotManifest. Total reads: 1. - sys.stdout.isatty() called once per output line (N syscalls). Now computed once before the output loop and captured in a _color() closure. - any([added, modified, deleted]) created an unnecessary list. Changed to the idiomatic `added or modified or deleted`. Performance: - walk_workdir (snapshot.py): replaced sorted(workdir.rglob("*")) with os.walk + in-place dirnames pruning. Hidden directories (.venv/, .muse/, .git/, node_modules/) are now pruned before os.walk descends into them. Previously rglob visited every file inside those directories — on a Python project with a virtualenv this was tens of thousands of wasted syscalls. os.lstat replaces the separate is_symlink() + is_file() pair (one syscall instead of two per file). - code/plugin.py snapshot(): same rglob → os.walk fix. Also integrates StatCache: files unchanged since the last walk are not re-hashed. Previously every muse status re-computed SHA-256 for every tracked file from scratch. The code plugin also prunes _ALWAYS_IGNORE_DIRS at the dirnames level so __pycache__, node_modules, dist, build etc. are never entered. - midi/plugin.py snapshot(): same os.walk + StatCache integration. - scaffold/plugin.py snapshot(): same os.walk + StatCache integration; also replaces p.read_bytes() + inline sha256 (loads entire file into memory) with the StatCache's chunked 64 KiB reader. Registry: - Added resolve_plugin_by_domain(domain) — looks up the plugin by domain string without reading the filesystem, for callers that have already read repo.json. Tests: - Two patches in test_cli_plugin_dispatch.py updated from resolve_plugin to resolve_plugin_by_domain to match the renamed import in status.py.
f288fedc·Gabriel Cardona <gabriel@tellurstori.com>
·
feat: add -f / -d short flags to muse init (--force / --domain)
04050213·Gabriel Cardona <gabriel@tellurstori.com>
·
audit and harden all 13 plumbing commands for production Each command received a consistent pass: - Short flags: -f/--format on hash-object, cat-object, rev-parse, ls-files, commit-graph; -j/--json on ls-remote; -n/--max on commit-graph - Input validation: validate_object_id() guard on cat-object, read-commit, read-snapshot, update-ref before touching the store - Memory safety: hash-object now uses write_object_from_path() (streaming shutil.copy2) instead of path.read_bytes() — eliminates heap spike for large blobs; cat-object streams raw bytes at 64 KiB chunks, no 256 MB cap - Dead code: removed _read_repo_id (double json import, no encoding) and trivial _current_branch wrappers from rev-parse, commit-tree, ls-files, pack-objects, commit-graph; pathlib imports cleaned up accordingly - Encoding: update-ref write_text(..., encoding='utf-8'); commit-tree _read_repo_id now uses encoding='utf-8' and fails fast on missing/malformed repo_id (prevents writing a commit with empty repo_id) - Format validation: all --format options now reject unknown values with a clear error instead of silently falling through - Type precision: commit-graph nodes typed as _CommitNode TypedDict (not dict[str, str | None]); pack-objects drops needless 'have or None' idiom - Error contract: read-commit and read-snapshot emit validation errors as JSON to stdout (not stderr) so scripts can parse them uniformly - unpack-objects: already clean, no changes
1c3feb01·Gabriel Cardona <gabriel@tellurstori.com>
·
feat: implement muse rerere — reuse recorded conflict resolutions Adds domain-agnostic rerere (Reuse Recorded Resolution) to Muse VCS. Fingerprints conflicts by a SHA-256 of the sorted ours/theirs object ID pair (commutative, content-addressed) and replays cached resolutions automatically on future identical conflicts. New modules: - muse/core/rerere.py — fingerprinting, atomic preimage/resolution I/O, auto_apply(), record_resolutions(), list/forget/clear/gc operations - muse/cli/commands/rerere.py — muse rerere [record|status|forget|clear|gc] with --dry-run, --format (text|json), --yes flags; full short+long forms Protocol extension (non-breaking): - domain.py: RererePlugin optional sub-protocol with conflict_fingerprint() method; falls back to the default content fingerprint when absent Integration: - merge.py: auto-applies cached rerere resolutions before writing MERGE_STATE.json; records preimages for remaining conflicts; adds --rerere-autoupdate/--no-rerere-autoupdate flag - commit.py: records user resolutions after a merge commit and clears MERGE_STATE.json (previously never cleared) Storage: .muse/rr-cache/<fingerprint>/{meta.json, resolution} All I/O uses UTF-8 encoding and atomic writes (temp + os.replace). Tests: 57 new tests covering fingerprinting, preimage lifecycle, apply_cached, auto_apply, record_resolutions, bulk ops, CLI commands, and the RererePlugin protocol — all passing; 2609 total tests green. mypy strict: 0 errors. typing_audit --max-any 0: 0 violations.
95367f8d·Gabriel Cardona <gabriel@tellurstori.com>
·
docs: fix plumbing docstring gaps + add plumbing reference doc pack-objects: add missing Exit 3 (I/O error) to plumbing contract. ls-remote: add default_branch field and * marker to output examples in the module docstring, matching actual command behaviour. Add docs/reference/plumbing.md — a shareable, high-level plumbing reference covering all 12 commands with flags tables, JSON output examples, exit code contracts, composability patterns, and an object ID quick reference. Aimed at scripters, operators, and agent authors rather than developers reading source.
7e374cd5·Gabriel Cardona <gabriel@tellurstori.com>
·
feat(plumbing): add 7 new interrogation commands to complete plumbing layer Implements merge-base, snapshot-diff, domain-info, show-ref, check-ignore, check-attr, and verify-object — closing every gap identified in the plumbing audit against Git's full command surface and adding Muse-specific primitives. Each command: - Emits JSON by default with a stable, versioned schema - Accepts --format text for human-readable output - Has short (-x) and long (--xxx) flag forms on every option - Uses strict exit codes (0 success / 1 user error / 3 internal error) - Is covered by a dedicated test file (7 new files, 72 new tests) - Is documented in docs/reference/plumbing.md Zero mypy errors, zero typing_audit violations, 2681 tests green.
e88283c9·Gabriel Cardona <gabriel@tellurstori.com>
·
feat(plumbing): add symbolic-ref, for-each-ref, name-rev, check-ref-format, verify-pack; enhance commit-graph Completes the remaining identified plumbing gaps from the Git comparison table, implementing every applicable absent command. New commands: - symbolic-ref: read or write HEAD's symbolic branch reference. --set to update HEAD; --short to emit bare branch name; JSON and text output formats; validates target branch exists before writing. - for-each-ref: iterate all refs with full commit metadata. Supports --sort by any commit field, --desc for descending order, --count to limit output, and --pattern for fnmatch glob filtering. Designed for agent pipelines that need to slice the ref list. - name-rev: multi-source BFS from all branch tips simultaneously (O(total- commits)). Maps commit IDs to <branch>~N names; --name-only, --undefined to customise output; reports unreachable commits as undefined. - check-ref-format: validates branch/ref names against the full Muse naming ruleset (same as muse branch and update-ref). --quiet for shell conditionals; exits 0 only when ALL names are valid. - verify-pack: three-tier integrity check for PackBundle JSON. (1) re-hashes every object payload; (2) validates snapshot manifest references; (3) validates commit→snapshot references. Reads from stdin or --file; --no-local verifies bundle in isolation; --quiet for pipelines. Enhanced command: - commit-graph: --count (-c) emits only the integer count, suppressing the full node list. --first-parent (-1) follows only first-parent links for linear history. --ancestry-path (-a) restricts output to commits on the direct path between tip and --stop-at (requires --stop-at). Tests: 6 new test files (62 tests), one per command / enhancement group. All tests use direct store primitives for setup, consistent with the established plumbing test pattern. Docs: docs/reference/plumbing.md extended with full reference entries for all 6 additions, including flag tables, JSON/text output examples, exit code tables, and pipeline examples. Verification: mypy 0 errors (230 files), typing_audit 0 violations, pytest 2743/2743 passed.
96dd15b1·Gabriel Cardona <gabriel@tellurstori.com>
·
feat(tests+docs): supercharge plumbing test suite and update reference doc Tests — 11 new test files covering the 11 previously untested plumbing commands, plus cross-command integration tests and a stress suite: test_plumbing_hash_object.py — SHA-256 correctness, --write, 2 MiB file test_plumbing_cat_object.py — raw streaming, info format, large blobs test_plumbing_rev_parse.py — HEAD/branch/prefix resolution, errors test_plumbing_update_ref.py — update, create, delete, --no-verify test_plumbing_ls_files.py — HEAD/--commit, text format, 500-file manifest test_plumbing_commit_tree.py — create, parents, merge commit, metadata test_plumbing_read_commit.py — full ID, prefix, schema validation, 100-batch test_plumbing_read_snapshot.py — retrieval, manifest, 1000-file stress test_plumbing_commit_graph.py — BFS, --stop-at, --max, text format, 200-chain test_plumbing_pack_unpack.py — schema, --have pruning, round-trip, 50-obj stress test_plumbing_integration.py — 8 cross-command pipelines (hash→cat→verify, commit-tree→update-ref→rev-parse, pack→unpack transport, snapshot-diff↔ls-files, show-ref↔for-each-ref, symbolic-ref→rev-parse→ read-commit, merge-base→snapshot-diff, commit-graph→name-rev) test_plumbing_stress.py — 500-commit BFS, 300-deep merge-base, 200-commit name-rev, 2000-file snapshot-diff, 200-object verify, 100-branch for-each-ref/show-ref, 100-commit/100-object pack round-trip, 200-commit sequential read-commit Total: 2900 tests (was 2743) — 157 new tests, all green. Docs — docs/reference/plumbing.md: - Add Quick Index table (all 24 commands, one-line description, anchor link) - Standardise headings: every command now uses ### `name` — slug style - Fix bug: pack-objects output showed data_b64 (wrong); corrected to content_b64 - Merge duplicate commit-graph sections into one complete entry with all 7 flags - Add --count / --first-parent / --ancestry-path to commit-graph flag table - Fix name-rev docs: distance=0 emits bare branch name, not <branch>~0 - Add "Composability Patterns — Advanced" section with 6 new pipeline recipes for symbolic-ref, for-each-ref, check-ref-format, verify-pack, name-rev, snapshot-diff, and the stale-branch audit pattern Verification: mypy 0 errors, typing_audit 0 violations, pytest 2900/2900.
99746394·Gabriel Cardona <gabriel@tellurstori.com>
·
feat(porcelain): implement 9 gap-fill porcelain commands with full test coverage and docs Adds the following commands, each comprehensively implemented with short+long flags, JSON output, security-hardened inputs, and dedicated test files: Core VCS gap-fill (Git-equivalent): - muse rebase — content-addressed commit replay (--squash, --abort, --continue, --onto) - muse clean — working-tree cleanup with --dry-run safety guard - muse describe — nearest-tag labelling (BFS walk, --long format) - muse shortlog — commit summary grouped by author/agent (--numbered, --email) - muse bundle — offline commit transfer (create/unbundle/verify/list-heads) - muse content-grep — full-text search across tracked file content (regex, binary skip) Muse-specific additions: - muse verify — whole-repository integrity (ref/commit/snapshot/object DAG walk + re-hash) - muse snapshot — explicit snapshot management (create/list/show/export as tar.gz/zip) - muse whoami — identity shortcut (thin wrapper over muse auth whoami) New core modules: - muse/core/describe.py — BFS tag walk, DescribeResult TypedDict - muse/core/verify.py — VerifyFailure/VerifyResult TypedDicts, run_verify() - muse/core/rebase.py — RebaseState TypedDict, state I/O, collect_commits_to_replay(), replay_one() Tests: 99 new tests across 9 files — unit, integration, end-to-end, stress. Docs: docs/reference/porcelain.md — comprehensive reference with output schemas, flag tables, and composability patterns. All checks green: mypy (242 files, 0 errors) · typing_audit (0 violations) · pytest (2999 passed).
1ba7f7b1·Gabriel Cardona <gabriel@tellurstori.com>
·
feat: add --format json to all porcelain commands for agent-first output Every porcelain command that produces meaningful structured output now accepts --format json (or --format text, the default). Agents can pass this flag to receive machine-readable JSON instead of human-readable text, enabling clean pipelines without text parsing. Commands updated: - commit → {commit_id, branch, snapshot_id, message, parent_commit_id, committed_at, sem_ver_bump} - checkout → {action, branch, commit_id} - status → {branch, clean, added, modified, deleted} - diff → {summary, added, deleted, modified, total_changes} - merge → {status, commit_id, branch, current_branch, conflicts} - reset → {branch, old_commit_id, new_commit_id, mode} - revert → {commit_id, branch, reverted_commit_id, message} - cherry-pick → {commit_id, branch, source_commit_id, conflicts} - rebase → {status, branch, new_head, onto, squash, replayed, conflicts} - log → JSON array of commit objects - branch → JSON array for list; action object for create/delete - tag add/list/remove → structured tag objects - stash push/pop/list/drop → structured stash objects - reflog → JSON array of ref entries - worktree add/list/remove → structured worktree objects - gc → {collected_count, collected_bytes, reachable_count, elapsed_seconds, dry_run, collected_ids} - whoami → --format json aliases existing --json flag show already had --json; no change needed. All format validation follows the same pattern: unknown values echo an error and exit USER_ERROR. Tests updated throughout. mypy: 0 errors | typing_audit: 0 violations | pytest: 3198 passed
95b86799·Gabriel Cardona <gabriel@tellurstori.com>
·
feat(hardening): add config/bisect CLI tests and fix bisect convergence bug - Add test_cmd_config.py: 35 tests covering show/get/set subcommands, blocked namespaces (auth.*, remotes.*), HTTPS enforcement for hub.url, TOML injection safety, --format json / --json parity, and a 50-key domain stress test. - Add --format json / -f alias to `muse config show` for consistency with the rest of the porcelain JSON output convention. - Add test_cmd_bisect.py: 21 tests covering CLI subcommands (start, bad, good, skip, log, reset), session guard (double-start rejected), invalid ref rejection, ANSI-injection sanitization, and a 50-commit stress test. - Fix bisect engine infinite-loop bug: _build_remaining() previously included bad_id in the remaining list, causing the midpoint to repeatedly point at bad_id and never converge when remaining == 2. Fix: exclude bad_id from remaining (it is already the known-bad boundary). Update termination condition from <= 1 to == 0 accordingly.
faec8c4d·Gabriel Cardona <gabriel@tellurstori.com>
·
feat(hardening): final sweep — security, performance, API consistency, docs Security fixes -------------- - update_ref: add validate_branch_name() before constructing the ref path — prevents path traversal via crafted branch names (e.g. ../../etc/cron.d) - gc + config_cmd: sanitize_display() on the --format error path so ANSI escape sequences in bad --format values cannot reach the terminal - ls_files + commit_tree: validate_object_id() before passing user-supplied commit/snapshot IDs to read_commit()/read_snapshot() — closes path-traversal vector through the store's filename construction - verify_pack: fix unclosed file handle (open() without with statement) Performance fixes ----------------- - show --json: read snapshot directly via commit.snapshot_id instead of re-reading the commit file we already have in memory; saves one disk read per JSON show invocation - commit_graph --ancestry-path: cap _ancestors_of() BFS at 100 000 commits (was completely unbounded); prevents O(N) pre-scan from stalling on large repositories API consistency --------------- - ls_remote: replace --json bool flag with --format json|text, matching all 20+ peer plumbing commands; update test to use new flag - read_commit, read_snapshot, commit_tree, update_ref, unpack_objects: add --format text option so these always-JSON commands have a shell-pipeline- friendly human-readable output path; text modes print compact one-liners or are silent on success (update_ref) - commit_tree + ls_files: fix tests that used invalid hex IDs ('s'*64, 'snap2') so validate_object_id passes Documentation fixes ------------------- - diff.py: BREAKING — docstring claimed JSON payload contains "ops" field; actual field is "total_changes". Agents consuming "ops" received nothing. - commit.py: add missing "author" and "sem_ver_bump" to JSON payload doc - log.py: add missing "sem_ver_bump" to JSON array element doc - reflog.py: add missing "index" and "author" to JSON entry doc - checkout.py: add missing "already_on" action value to JSON payload doc - rebase.py: document both JSON payload shapes (squash vs normal) - show.py: document all JSON fields including files_added/removed/modified - whoami.py: document full JSON payload fields - config_cmd show: document JSON payload top-level keys
e8c4265e·Gabriel Cardona <gabriel@tellurstori.com>
·
docs: comprehensive plumbing and porcelain reference update Plumbing doc: - read-commit, read-snapshot: document --format text one-liner output - commit-tree: document --format text (bare commit ID), add security note about snapshot/parent ID validation - update-ref: document --format text (silent on success), add security note about branch-name validation preventing path traversal - unpack-objects: document --format text human summary - ls-remote: update from --json/-j to --format json|text (API change) - commit-graph: note 100 000-commit cap on --ancestry-path BFS - Fix composability example to use ls-remote --format text Porcelain doc: - Add complete --format json flag table and JSON payload schema to every command: commit, status, log, diff, show, branch, checkout, merge, rebase, reset, revert, cherry-pick, stash, tag, blame, reflog, gc, archive, bisect, worktree, describe, shortlog, verify, snapshot, bundle, content-grep, whoami, config - Fix whoami JSON: token_set is boolean; add capabilities field - Fix diff JSON: field is total_changes, not ops - Add rebase JSON: two distinct shapes (squash_rebase vs rebase) - Add config show JSON: full schema with user/hub/remotes/domain sections, blocked namespaces documented - Add flag tables with short flags for all commands - Expand composability patterns section with 3 new examples
51e65168·Gabriel Cardona <gabriel@tellurstori.com>
·
docs: revamp README — punchy, developer-first, marketing funnel
ba28f1ce·Gabriel Cardona <gabriel@tellurstori.com>
·
fix(tests): guard help assertions against Rich ANSI codes between '--' dashes Rich's terminal renderer injects color codes between the two dashes of a long flag (e.g. '--force' becomes '-\x1b[...]-force'), so asserting '--force' in result.output fails in CI even though the flag is present. Fix both tests to fall back to the short flag: test_clean_help: '--force' in output or '-f' in output test_verify_cli_help: '--no-objects' in output or '-O' in output Consistent with the existing pattern in test_cmd_rebase.py and test_cmd_describe.py.
229172a6·Gabriel Cardona <gabriel@tellurstori.com>
·
feat: code porcelain hardening — security, perf, JSON, docs Security (critical — write commands): - patch.py, checkout_symbol.py, semantic_cherry_pick.py: add contain_path() guard on ADDRESS file component; path-traversal addresses rejected (exit 1) Security (medium): - code_check.py: validate --rules FILE via contain_path() before reading - grep.py: cap regex patterns at 512 chars to prevent ReDoS; surface re.error gracefully instead of crashing Performance: - codemap.py, invariants.py: replace recursive _find_cycles() DFS with an iterative explicit-stack implementation; eliminates RecursionError risk on deeply-nested import graphs (depth > 1000 frames) API / JSON consistency: - patch.py, checkout_symbol.py: add --json flag with structured output - index_rebuild.py: add --json flag to both 'status' and 'rebuild' subcommands - code_check.py, code_query.py: rename output_json → as_json for consistency Documentation: - docs/reference/code-domain.md: JSON schemas for 4 newly-json commands, security notes for 3 write commands and grep, new muse grep and muse code-check reference sections Tests (test_code_commands.py): - Path traversal rejection tests (patch, checkout-symbol, semantic-cherry-pick) - ReDoS guard tests (grep: >512-char pattern, invalid regex) - JSON output tests (index status --json, index rebuild --json, patch --json) - Iterative DFS regression (depth-600 chain, self-loop — no RecursionError) Verification: mypy clean, typing_audit 0 violations, 3273/3273 tests green
8912a997·Gabriel Cardona <gabriel@tellurstori.com>
·
fix: inject explicit HTML anchors in plumbing and porcelain Quick Index GitHub generates anchors from the full header text (e.g. `#init--initialise-a-repository`), not from the command name alone. The Quick Index tables in both docs referenced short anchors (`#init`, `#hash-object`, etc.) that never resolved, breaking all in-page navigation. Fix: prepend `<a id="command-name"></a>` before every section header covered by the Quick Index table — 31 anchors in porcelain.md and 24 in plumbing.md. Also simplify the plumbing.md table links from the broken long-form (`#hash-object----compute-a-content-id`) to the matching short form (`#hash-object`).
48376849·Gabriel Cardona <gabriel@tellurstori.com>
·
docs: add WHITE_PAPER.md — flat-projection impossibility theorem Argues formally why multidimensional state (music, genomics, 3D, simulation) cannot be adequately version-controlled by projecting to flat byte sequences. Five formal arguments grounded in the Muse implementation, with appendices covering the 21 MIDI dimensions, the OT commutativity table, and formal definitions including the impossibility theorem and proof sketch.
fe21d86c·Gabriel Cardona <gabriel@tellurstori.com>
·
fix: correct WHITE_PAPER.md TOC deep links to match GitHub slug generation GitHub slugger lowercases the full heading text and strips non-alphanumeric characters, producing anchors like '#3-argument-i-byte-level-equality-loses- semantic-identity'. The TOC had truncated aliases (#3-argument-i) that resolved to nothing. Updated all 14 broken entries to full slugs. Co-authored-by: Gabriel Cardona <gabriel@tellurstori.com>
0a797960·Gabriel Cardona <cgcardona@gmail.com>
·
Revamp white paper — plain language, drop the false formalism Replace the 1,066-line theorem-heavy paper with a 228-line version written for engineers and AI practitioners. Cuts all LaTeX proofs, the circular existence-proof structure, and the Bitcoin tangent. Keeps the concrete MIDI examples, the 21-dimension model, the plugin protocol, and the AI-agents argument — the parts that actually land. New tagline: "Git versions files. Muse versions meaning."
964c2330·Gabriel Cardona <gabriel@tellurstori.com>
·
feat: harden, test, and document all quality-dial changes Security - LocalFileTransport._repo_root() calls .resolve() to canonicalise symlinks before the .muse/ check — prevents symlink-based path escape - push_pack() validates branch names with validate_branch_name() then contain_path() as defence-in-depth, blocking pre-placed symlink attacks on .muse/refs/heads/ - Fast-forward check upgraded to full BFS (_is_ancestor) through bundle + remote commit graph, correctly handling build_pack(have=...) bundles Performance - LocalFileTransport uses lazy imports per method — zero circular-import risk - _is_ancestor BFS stops at first hit — O(divergence depth), not O(history) Tests (72 new, 3167 total passing) - test_local_file_transport.py: unit, integration, security, stress - test_lineage_algorithm.py: created/copied/renamed/moved/modified/deleted, incremental registry correctness across 10+ intermediate commits - test_store_branch_heads.py: empty dir, missing dir, whitespace, subdirs Docs - transport.py module docstring rewritten — covers both transports, make_transport factory, auth model, security model - docs/reference/security.md — new "Local File Transport Hardening" section with per-guard threat table - Bitcoin plugin removed, predict-conflicts alias added, lineage O(total ops) rewrite retained from previous session
7855ccd0·Gabriel Cardona <gabriel@tellurstori.com>
·
feat: add `muse domains publish` — publish domain plugins to MuseHub New subcommand `muse domains publish` registers a Muse domain plugin in the MuseHub marketplace from the command line. Usage: muse domains publish \ --author cgcardona --slug genomics \ --name "Genomics" \ --description "Version DNA sequences as multidimensional state" \ --viewer-type genome When run from inside a repo using the domain being published, capabilities are derived automatically from the active plugin's schema(). Use --capabilities '<json>' to provide them explicitly for plugins not yet registered locally. Flags: --author, --slug, --name, --description, --viewer-type (required), --version (default 0.1.0), --capabilities (JSON override), --hub (URL override), --json (machine-readable output) Returns the scoped_id, manifest_hash, and marketplace URL on success. Handles 409 Conflict and 401 Unauthorized with actionable error messages. All types are strict TypedDicts — zero Any, zero object, passes mypy strict and typing_audit --max-any 0.
1cc8f8fb·Gabriel Cardona <gabriel@tellurstori.com>
·
feat: complete 100% coverage — elicitation bypass, publish tests, callback guard - domains callback: add ctx.invoked_subcommand guard so publish subcommand does not trigger the dashboard side-effect when invoked - 16 new tests for muse domains publish (unit + CLI integration via CliRunner) covering success, --json, 409/401/5xx, URLError, bad JSON, schema derivation from active plugin, custom version, and missing required args - All type: ignore removed; uses http.client.HTTPMessage() for HTTPError hdrs - mypy strict + typing_audit --max-any 0 + 3183 pytest tests all green
c9c5fd13·Gabriel Cardona <gabriel@tellurstori.com>
·
docs + stress tests: domains publish reference, docstrings, 13 stress/E2E tests - docs/reference/domains.md: new reference page covering muse domains dashboard, --new scaffold, publish command, capabilities manifest, auth, HTTP semantics, agent usage, plugin architecture, and end-to-end examples - muse/cli/commands/domains.py: expanded docstrings on _DimensionDef, _Capabilities, _PublishPayload, _PublishResponse, and _post_json (full Args/Returns/Raises) - tests/test_stress_domains_publish.py: 13 new stress and E2E tests covering 500-call throughput baseline, large capabilities (100 dims), unicode description, repeated 409 does not leak state, large --description (4000 chars), slug with hyphens, hub URL propagation, --json round-trip, --version semver, thread-safety (10 threads), full payload structure, capabilities auto-derived from MIDI plugin, 400-call CLI stress mypy + typing_audit --max-any 0 + 3196 pytest tests all green
154a59f1·Gabriel Cardona <gabriel@tellurstori.com>
·
fix(ci): raise 400-call stress test deadline 30s → 120s for CI runners GitHub Actions shared runners completed the 400 sequential CliRunner invocations in 44.7s (3× slower than a developer laptop), causing the 30s assertion to fail. The deadline is a guard against catastrophic regressions, not a throughput benchmark, so 120s gives generous CI headroom while still catching infinite-loop or exponential-backoff bugs.
19bb6d76·Gabriel Cardona <gabriel@tellurstori.com>
·
chore: bump version 0.1.4 → 0.1.5 Co-authored-by: Gabriel Cardona <gabriel@tellurstori.com>
2d955ff7·Gabriel Cardona <cgcardona@gmail.com>
·
fix: allow localhost HTTP in transport credential guard The HTTPS-only credential check correctly blocks token leakage over cleartext HTTP to remote servers. However, loopback addresses (localhost, 127.0.0.1, ::1) never leave the machine, so TLS would require a self-signed cert and add friction for local development. Add an explicit loopback exemption so `muse push` works against a local MuseHub instance (e.g. Docker on port 10003) without needing a TLS terminator.
c6d2e14b·Gabriel Cardona <gabriel@tellurstori.com>
·
fix: resolve auth token from remote URL — muse push local/origin just works
4e2f1256·Gabriel Cardona <gabriel@tellurstori.com>
·
fix(diff): extract symbols from new working-tree files New files (uncommitted) produce a plain InsertOp with no symbol detail because the object store doesn't have their content yet. Read bytes directly from disk via repo_root / path when the object store misses, then extract symbols for .md (ATX headings) and .py (def / class). Return a PatchOp with InsertOp child ops so _print_structured_delta calls _print_child_ops and displays section/function names inline under the 'A filename' line — matching what the TestDiffWorkingTreeSymbols tests assert.
8d31cbb2·Gabriel Cardona <gabriel@tellurstori.com>
·
perf(log): stop commit walk early when no filters are active get_commits_for_branch accepted no limit, so every muse log invocation read every commit JSON file off disk before log.py sliced the result to --max-count. On a repo with 200 commits, muse log -n 10 was opening 200 files to show 10. Add max_count parameter (0 = unbounded, preserves all existing callers). log.py passes limit as walk_limit when no filters (--since/--until/ --author/--section/--track/--emotion) are active. With filters the walk stays unbounded because commits may be skipped and the filter loop enforces the limit itself. The 'brief moment' on cold repos is Python startup (~100ms). The walk optimisation eliminates the per-commit file-open cost that compounds on repos with many commits.
06fd0886·Gabriel Cardona <gabriel@tellurstori.com>
·
test(log): cover max_count early-stop at both unit and CLI level Unit (TestStoreGaps): - test_get_commits_for_branch_max_count_stops_early: max_count=2 on a 5-commit chain returns exactly the 2 newest commits - test_get_commits_for_branch_max_count_zero_returns_all: max_count=0 (default) returns all 5 commits unchanged - test_get_commits_for_branch_max_count_larger_than_chain: max_count greater than chain length returns every commit without error CLI integration (TestLog): - test_max_count_limits_output: muse log -n 2 on 5 commits returns exactly 2 lines showing the two most recent - test_max_count_one_returns_single_commit: muse log -n 1 returns only the HEAD commit - test_max_count_larger_than_history_returns_all: muse log -n 100 on 3 commits returns all 3
a6a637a2·Gabriel Cardona <gabriel@tellurstori.com>
·
feat: migrate CLI from typer to argparse (POSIX-compliant, order-independent) Replaces typer/click with Python stdlib argparse across all 134 command files (24 plumbing + 110 porcelain) and rewrites app.py with a register()/run() dispatch contract. Key improvements: - Arguments and options now accepted in any order (muse push local --branch dev --set-upstream now works, as does muse push --branch dev local --set-upstream) - No more typer/click import overhead at startup - Full register(subparsers)/run(args) contract — every command is independently testable without invoking the full CLI tree - pyproject.toml entry point updated: muse.cli.app:cli → muse.cli.app:main - All module, function, and private-helper docstrings preserved verbatim
00373ad0·Gabriel Cardona <gabriel@tellurstori.com>
·
fix: replace typer CliRunner with argparse-compatible test helper - Add tests/cli_test_helper.py: drop-in CliRunner that wraps main(), supports env=, input=, catch_exceptions=, stdout.buffer, and stdin.buffer - Strip ANSI codes from output to match typer CliRunner behavior - Rename root parser --version dest to show_version to avoid namespace collision with domains publish --version SEMVER (fixed muse 0.1.3 false positive in 7 test files) - Add missing short flag aliases: archive (-f/-o), describe (-l), reflog (-n/-b), shortlog (-n/-f), snapshot list (-n) - Update all 75 test files: replace typer imports with cli_test_helper All 3202 tests pass.
86000da9·Gabriel Cardona <gabriel@tellurstori.com>
·
fix: remove last typer references from muse/core/ - repo.py: replace typer.echo + typer.Exit with print(stderr) + SystemExit - query_engine.py, invariants.py: update stale typer.echo() docstring refs - test_core_coverage_gaps.py: expect SystemExit instead of click.exceptions.Exit muse/ is now completely free of typer/click.
d78c6f12·Gabriel Cardona <gabriel@tellurstori.com>
·
fix: resolve typing_audit violations in cli_test_helper - Inherit _StdinWithBuffer and _StdoutCapture from io.StringIO so they are proper TextIO types — eliminates type: ignore[assignment] and type: ignore[arg-type] on redirect_stdout / sys.stdin assignment - Type _cli parameter as None (all tests pass None post-migration) - Remove **_kwargs catch-all; drop stale obj= kwarg from one test call - Drop `from typing import Any` — file is now Any-free typing_audit: 0 violations mypy: 0 errors pytest: 3202 passed
5175b6a3·Gabriel Cardona <gabriel@tellurstori.com>
·
feat: add muse code add — selective staging with hunk-level patch mode Implements a full Git-style staging index for the code domain. When `.muse/code/stage.json` exists, `muse commit` commits only what was explicitly staged; all other tracked files carry their committed state forward unchanged. New commands: muse code add <file|dir|.> [-A|-u|-p|-n|-v] muse code reset [HEAD] [<file>] Core changes: - muse/plugins/code/stage.py — StageIndex read/write/clear utilities - muse/domain.py — StagePlugin optional protocol + StagedEntry / StageStatus TypedDicts - muse/plugins/code/plugin.py — CodePlugin implements StagePlugin; snapshot() is now stage-aware - muse/cli/commands/code_stage.py — register_add / register_reset; interactive hunk staging via difflib - muse/cli/commands/commit.py — clears stage after successful commit - muse/cli/commands/status.py — renders three-bucket staged/unstaged/ untracked view when StagePlugin active - muse/cli/app.py — registers add + reset under code subparser - docs/reference/code-domain.md — full staging reference (section 1) Tests: 53 new tests covering unit (pure functions), integration (CLI round-trips), stage-aware commit, three-bucket status, reset variants, resilience (corrupt index), and stress (100-file staging). All checks: mypy zero errors · typing_audit 0 violations · 3255 passed
95e97820·Gabriel Cardona <gabriel@tellurstori.com>
·
fix: muse code add skipped unchanged-vs-HEAD check, staging all files When the stage index was empty (first run of muse code add), the only skip guard compared the newly-computed hash against the existing stage entry. That check was vacuously false for every file, so every file in the working tree was staged — even ones whose content was byte-for-byte identical to the last committed snapshot. Fix: after hashing a file, compare its object_id against head_manifest. If they match the file has not changed; skip without staging it. This makes muse code add . agree with muse status: only genuinely-changed files appear in the stage. Regression test: test_add_dot_does_not_stage_unchanged_files verifies that only the modified file is staged when two files exist and only one is edited between commits.
5b27712b·Gabriel Cardona <gabriel@tellurstori.com>
·
fix: three staging bugs found in post-launch sweep Bug 1 — .museignore not respected by muse code add _walk_tree never loaded ignore patterns, so muse code add . would stage files matched by .museignore. Those files then appeared in the committed snapshot because snapshot() in staged mode skips the ignore walk entirely. Fix: filter collected paths through resolve_patterns / is_ignored in run_add before staging. Staged deletions of previously- tracked files are always permitted regardless of ignore rules. Bug 2 — muse code add -A did not stage deletions -A is documented as staging all changes including deletions, but the code path was identical to no-args: _walk_tree returns only files that exist on disk. Tracked files deleted from the working tree were silently omitted, making -A behave like . for this case. Fix: when all_files=True, also walk head_manifest for paths absent from disk and append them so they receive mode "D" in run_add. No-args path is now its own separate branch (new + modified only, no deletions — matches git add . semantics). Bug 3 — write_stage wrote stage.json non-atomically path.write_text() is not crash-safe. A kill mid-write left a truncated or empty stage.json; read_stage returns {} silently in that case, losing all staged entries with no error. Fix: use mkstemp + replace (temp file then rename) matching the pattern already used by stat_cache.py and the object store. Regression tests added for all three bugs (56 total, all green).
418c9a69·Gabriel Cardona <gabriel@tellurstori.com>
·
fix: restore structured --help output for all CLI commands argparse's default HelpFormatter collapses description text into a single paragraph, destroying the line breaks and indentation of module docstrings used as command descriptions. Add formatter_class=argparse.RawDescriptionHelpFormatter to every multi-line add_parser() call (81 calls, 79 files) and every single-line add_parser() call that carries a description= argument (31 calls). The root parser already had the formatter set; the subcommand parsers were missing it. Simultaneously applies three performance/quality improvements to muse status: - Remove the dead resolve_plugin_by_domain import - Defer get_head_snapshot_manifest until after the staged-status check so the manifest is not loaded twice when staging is active - Consolidate the three-pass delta["ops"] scan into a single loop
6acddccb·Gabriel Cardona <gabriel@tellurstori.com>
·
feat: CLI polish — diff model, log colours, commit summary, push fix - muse diff: correct semantic model (HEAD vs workdir by default, --staged for what will be committed, --unstaged for unstaged only); add -p/--path filter; add workdir_snapshot() to StagePlugin protocol - muse log: ANSI colour output (yellow hash, cyan HEAD, green branch, dim date, semver colour by severity); coloured help docstring examples - muse commit: print file-change summary after bracket line, matching git - muse push: fix push_branch bug (was using upstream remote name as branch name); remove redundant server echo message; deque BFS in build_pack - muse status: fix upstream tracking (was using snapshot_id instead of commit_id); show remote/branch ref (e.g. origin/feat/...); rename handling; fix stage_status unstaged bucket (staged-then-modified files were silently dropped) - completions/_muse: zsh tab-completion script
45033f17·Gabriel Cardona <gabriel@tellurstori.com>
·
feat: semantic TOML and Markdown adapters for the code domain plugin Add two fully-typed, tree-sitter-backed language adapters to the code domain plugin, extending Muse's semantic version control to documentation and configuration files. ### TOML adapter (tomllib-based) - Tables and array-of-table entries emitted as `section` symbols with dotted qualified names (e.g. `pyproject.toml::tool.mypy`). - Scalar key-value pairs emitted as `variable` symbols. - Semantic `content_id` / `body_hash` via `json.dumps(sort_keys=True)` so comment-only edits and key-reordering produce no diff. - PEP 695 recursive type aliases (`_TomlScalar`, `_TomlValue`) keep the implementation Any-free despite `tomllib.loads()` returning `dict[str, Any]`. - 53-test suite in `tests/test_toml_adapter.py`. ### Markdown adapter (complete rewrite) - **Fixes critical content_id bug**: previously hashed only the heading text; now hashes the full section node (heading + all content beneath it) so body changes are correctly detected. - **Hierarchical qualified names**: `## Installation` under `# API Reference` becomes `API Reference.Installation`, preventing address collisions between identically-named headings in different contexts. - **Fenced code blocks** emitted as `variable` symbols — `code[python]@L15` — scoped to their containing section; language tag in `signature_id`. - **GFM pipe tables** emitted as `section` symbols — `table@L22` — with column-header schema in `signature_id` and data rows in `body_hash` so header renames and row additions are independently visible. - **Inline markup stripping** (`_plain_heading` + `_MD_INLINE_RE`) makes addresses stable across formatting changes (bold/italic/code/link markers stripped; bounded quantifiers prevent backtracking). - Deduplication: identical sibling headings get `@L{lineno}` suffix. - Depth limit (`_MAX_DEPTH = 8`) prevents unbounded recursion. - 95-test suite in `tests/test_markdown_adapter.py`. ### Docs & housekeeping - `docs/reference/code-domain.md`: Language Support table updated to include TOML, Markdown, HTML, and CSS/SCSS. - `pyproject.toml`: `license = {text = "MIT"}` field added. - `LICENSE`: MIT license file added to repository root. - `tests/test_code_plugin.py`: two stale `h1:` / `h2:` address-format assertions updated to match the new hierarchical scheme. Verification: mypy 0 errors · typing_audit --max-any 0 passes · 3406 tests green.
9d49af7a·Gabriel Cardona <gabriel@tellurstori.com>
·
fix: correct branch resolution in fetch and add --prune/--all/--dry-run Bug: muse fetch with no explicit --branch used get_upstream() whose return value is the upstream *remote name* (e.g. "origin"), not the branch name. This caused "Branch 'origin' does not exist on remote 'origin'" whenever the remote's branch tracking was set, because "origin" was passed as the branch to look up in the remote's branch_heads map. Fix: use the current local branch name directly as the default, which is the correct git-equivalent behaviour. New flags that match git fetch: --all fetch all configured remotes --prune / -p remove stale remote-tracking refs (branches deleted on remote) --dry-run / -n show what would be fetched without writing anything --tags / --no-tags reserved for future tag storage (parsed, no-op for now) When a branch is not found on the remote, the error now lists all available branch names so the user knows what to pass with --branch. Adds regression test for the upstream-name-as-branch bug, plus tests for --prune, --dry-run, --all, up-to-date fast-path, and branch hint output.
b5d8116d·Gabriel Cardona <gabriel@tellurstori.com>
·
feat(symbols): polish muse code symbols command - Remove dead-code duplication: replace private _symbols_for_snapshot, _is_semantic, and _language_of with imports from _query.py. The private _language_of rebuilt its lookup dict on every call (perf bug); the shared language_of keeps it as a module-level constant. - Add --language filter (present in every analogous command — grep, stable, hotspots, clones, compare — but missing from symbols). - Validate --kind immediately; invalid values now error with the full list of accepted kinds instead of silently returning zero results. - Make --count and --json mutually exclusive at the argparse level. - Add TTY-aware ANSI color output: bold file paths, blue fn, bold-yellow class, cyan method, dim line numbers and hashes. - Expand language_of() map in _query.py to cover all tracked file types (Markdown, TOML, YAML, JSON, CSS, SCSS, HTML, SQL, Shell, Swift, Protobuf, Terraform) — fixes display in every command that calls it. - Human-friendly summary line: thousand separators, proper pluralization (symbol/symbols, file/files), comma-separated language breakdown. - Fix help text: correct title (muse code symbols), remove third-party references, add live ANSI color to the example, replace RST double- backtick markup with bold ANSI flag names. - Add 15 new tests in TestSymbols covering all flags, invalid inputs, JSON schema, mutual exclusion, and edge cases.
dc734ff5·Gabriel Cardona <gabriel@tellurstori.com>
·
fix: guard all apply_manifest callers against dirty working tree Adds muse/cli/guard.py::require_clean_workdir() — a single pre-flight check that mirrors Git's behaviour: modified/deleted tracked files block the operation; brand-new untracked files are left alone. Guards added to: merge, checkout, reset (--hard only), revert, cherry-pick. Each also gains a --force flag to bypass the guard when intentional. Also restores branch.py (full git parity, colours) and log.py (lane-based --graph DAG renderer, --all flag) which were wiped by the merge that prompted this fix, and updates TestBranch to reflect the new safe-delete semantics (-d blocks unmerged branches, -D force-deletes them).
1f065285·Gabriel Cardona <gabriel@tellurstori.com>
·
fix(git2muse): use walk_workdir, drop hashlib, write reflog entries
5392be66·Gabriel Cardona <gabriel@tellurstori.com>
·
fix(log): --oneline shows subject only, support git-style -<n> shorthand
7d4c82af·Gabriel Cardona <gabriel@tellurstori.com>
·
fix(tests): update branch/merge tests for new output and renamed internals
661e0ed9·Gabriel Cardona <gabriel@tellurstori.com>
·
Show fetch and push lines in muse remote -v, matching git remote -v Each remote now prints two lines in verbose mode — one labelled (fetch) and one labelled (push) — mirroring the output format of git remote -v.
0841b933·Gabriel Cardona <gabriel@tellurstori.com>
·
Fix push 422 on new branches; add tab completion for branch/remote names Push fix: when pushing a branch with no tracking ref yet, use all known remote heads as 'have' anchors instead of sending an empty have list. This avoids re-uploading every object in the repo (fixes HTTP 422 when object count > 1000). Tab completion: wire argcomplete into the CLI entry point and attach branch/remote/ref completers to push, pull, checkout, and branch commands. Activate once with: eval "$(register-python-argcomplete muse)" (or add to ~/.zshrc)
edfb5d16·Gabriel Cardona <gabriel@tellurstori.com>
·
Fix oh-my-zsh tab completion: drop argcomplete, fix _muse helpers - Remove argcomplete integration entirely (app.py, pyproject.toml, _completers.py, push/pull/checkout/branch.py) — the oh-my-zsh plugin already owns completion; argcomplete was redundant and non-functional under oh-my-zsh - _muse_branches: rewrite to read .muse/refs/heads/ directly via find+sed instead of spawning `muse plumbing show-ref | python3` — no subprocess, works even when muse is not on PATH inside the completion subshell - _muse_remotes: rewrite to read .muse/config.toml directly via grep+sed matching [remotes.<name>] section headers — no subprocess, correct TOML schema (was [remote."name"], actual schema is [remotes.name]) - muse.plugin.zsh §7: remove internal `compinit` call — oh-my-zsh calls compinit after all plugins load; calling it again inside the plugin body breaks completion; just add to fpath and let oh-my-zsh handle the rest
9a9dba59·Gabriel Cardona <gabriel@tellurstori.com>
·
Fix push 422 on fresh remotes: query /refs before building pack Before this change, pushing to a remote with no local tracking cache (e.g. first push to 'local') fell back to an empty 'have' list, causing build_pack to bundle every object in the repo (2338 → 422 too_long). The fix: call GET /refs (fetch_remote_info) on the transport before building the pack. This gives authoritative have-anchors directly from the server, regardless of whether local tracking refs exist. Falls back to cached tracking refs, then all known remote heads, then empty (for a truly empty remote).
2d501768·Gabriel Cardona <gabriel@tellurstori.com>
·
Fix push 422: use all cached remote heads as have anchors The previous fix queried GET /refs to get the target remote's heads, but those commit IDs often don't exist in the local object store (e.g. when the remote received commits via GitHub PR merges that were never fetched locally). build_pack's BFS can't stop at commits it can't find, so it walks the entire history. The correct fallback: use ALL cached tracking refs across every configured remote (.muse/remotes/**/*). Any commit the machine has previously pushed to any remote IS in the local object store AND is a shared ancestor with mirrors/forks. This lets build_pack stop at the nearest known common ancestor and send only the delta (3 commits, ~424 objects) rather than the full repo (2338 objects → 422 too_long).
a815e85a·Gabriel Cardona <gabriel@tellurstori.com>
·
Fix push 422: filter have-anchors to locally-known commits GET /refs returns the remote's branch heads, but those commits may not exist in the local object store (e.g. GitHub merge commits never fetched). Without filtering, build_pack's BFS can't stop at them and walks the entire history (2338 objects → 422 too_long). Fix: combine live remote heads with all cached cross-remote tracking refs, then filter the combined list to commits that exist locally. The cached origin/dev and origin/main entries are in the local store and are ancestors of the pushed branch, so build_pack stops at the nearest one (3 commits, ~424 objects) rather than sending everything.
7fd04b9f·Gabriel Cardona <gabriel@tellurstori.com>
·
fix(push): exclude local_head from have — prevents 0-object push when branch already on another remote
049f451c·Gabriel Cardona <gabriel@tellurstori.com>
·
feat(push): implement two-phase chunked push protocol Separates large pushes into two phases to avoid the server's per-request object limit (MAX_OBJECTS_PER_PUSH = 1 000): Phase 1 — object pre-upload POST {url}/push/objects (N calls, ≤ 900 objects) Objects are content-addressed (SHA-256) so each chunk is idempotent — the server skips blobs it already holds. No branch refs are touched. Phase 2 — commit push POST {url}/push (single call) Carries commits + snapshots with an empty objects list. Because blobs were pre-uploaded, this request is small regardless of history size. Branch refs are updated atomically here. For small pushes (≤ 900 objects) the two phases collapse into the existing single POST /push call — no behaviour change. Client-side additions: - ObjectsChunkResponse TypedDict in pack.py (stored / skipped counts) - CHUNK_OBJECTS = 900 constant in transport.py - push_objects() added to MuseTransport Protocol, HttpTransport, and LocalFileTransport with full docstrings and security notes - _parse_objects_response() parser mirrors existing _parse_push_result() - _push_chunked() orchestrator in push.py: chunks objects, prints per-chunk progress, then calls push_pack with a slim (no-objects) bundle Test: updated test_push_already_up_to_date to mock fetch_remote_info correctly (returns RemoteInfo with same HEAD as local → triggers up-to-date fast-path rather than proceeding to JSON serialisation of MagicMock).
7af7a886·Gabriel Cardona <gabriel@tellurstori.com>
·
docs: inject explicit HTML anchors in code-domain.md section headers Adds <a id="..."> before each of the 12 numbered section headers so the Contents table links resolve reliably regardless of GitHub's anchor- generation algorithm. Same treatment as plumbing.md / porcelain.md.
87c1ef22·Gabriel Cardona <gabriel@tellurstori.com>
·
fix(push): lower CHUNK_OBJECTS to 400 for reliable chunked upload 900-object chunks caused intermittent broken-pipe errors on both local and production hosts under real-world load. 400 keeps well within the server's 1 000-object Pydantic limit while halving per-request payload size, which eliminates the connection resets observed during testing.
e03c1b5b·Gabriel Cardona <gabriel@tellurstori.com>
·
fix: use live remote info for push up-to-date check; restore CHUNK_OBJECTS=400 When the remote repo is empty (or was wiped and recreated), the push command was falling back to a stale locally-cached tracking ref and incorrectly reporting "Everything up to date", sending nothing. Fix: when GET /refs succeeds (remote_info is not None), use only the live branch_heads to determine the remote head. Only fall back to the cached tracking ref when the remote was unreachable. Also restores CHUNK_OBJECTS from 100 back to 400 — the correct value for the production Nginx 300s timeout window.
acfd61fa·Gabriel Cardona <gabriel@tellurstori.com>
·
feat(mwp): replace JSON+base64 wire protocol with MWP binary msgpack - ObjectPayload.content: bytes replaces content_b64: str — no base64 overhead - HttpTransport encodes all requests/responses as application/x-msgpack - push.py: MWP push flow with filter-objects deduplication, chunked object upload - pull.py: depth-limited commit negotiation before fetch - bundle, pack-objects, unpack-objects, verify-pack: all switched to msgpack binary - CliRunner updated to support bytes stdin/stdout for plumbing commands - All tests updated for binary msgpack format; zero JSON+base64 fallback paths
dec4604a·Gabriel Cardona <gabriel@tellurstori.com>
·
feat(git2muse): auto-run muse init --domain code when .muse/ is missing Also add test efficiency protocol to AGENTS.md and .cursorrules: run full suite once, fix failures, re-run only the affected file to confirm, then run full suite as the final pre-PR gate.
390ce2eb·Gabriel Cardona <gabriel@tellurstori.com>
M!
chore: ignore .hypothesis, .pytest_cache, .mypy_cache, .ruff_cache; add separation-of-concerns rules - .museignore: add test/tool cache directories that should never be tracked - .cursorrules + AGENTS.md: enforce frontend separation of concerns (structure/behaviour/style)
⚠ AGENTS.md::Muse — Agent Contract.Architecture.code@L38⚠ AGENTS.md::Muse — Agent Contract.Branch Discipline — Absolute Rule⚠ AGENTS.md::Muse — Agent Contract.Branch Discipline — Absolute Rule.Enforcement protocol⚠ AGENTS.md::Muse — Agent Contract.Branch Discipline — Absolute Rule.Enforcement protocol.table@L100⚠ AGENTS.md::Muse — Agent Contract.Branch Discipline — Absolute Rule.Full task lifecycle⚠ AGENTS.md::Muse — Agent Contract.GitHub Interactions — MCP First⚠ AGENTS.md::Muse — Agent Contract.GitHub Interactions — MCP First.table@L113⚠ AGENTS.md::Muse — Agent Contract.Testing Standards.table@L177⚠ AGENTS.md::Muse — Agent Contract.Typing — Zero-Tolerance Rules.Enforcement chain⚠ AGENTS.md::Muse — Agent Contract.Typing — Zero-Tolerance Rules.Enforcement chain.table@L167⚠ AGENTS.md::Muse — Agent Contract.Typing — Zero-Tolerance Rules.table@L149⚠ muse/core/errors.py::ExitCode⚠ muse/core/pack.py::PackBundle⚠ muse/core/pack.py::build_pack⚠ muse/plugins/code/plugin.py::CodePlugin⚠ tests/test_core_snapshot.py::TestBuildSnapshotManifest.test_hidden_files_excluded⚠ tests/test_core_stat_cache.py::TestWalkWorkdirCacheIntegration.test_walk_excludes_hidden_paths_from_cache⚠ tools/omzsh-plugin/_muse⚠ tools/omzsh-plugin/muse.plugin.zsh