gabriel / musehub public
.cursorrules
207 lines 8.2 KB
3b83b8c0 feat(release-detail): server-side semantic analysis, rd2 design system,… gabriel 8h ago
1 # MuseHub — Agent Rules
2
3 ## Identity
4
5 MuseHub — the remote repository server for the Muse version control system. It is the GitHub analogue in the Muse ecosystem: stores pushed commits and snapshots, renders release detail pages, serves the wire protocol, hosts issue tracking and MCP tooling.
6
7 - **Stack:** Python 3.14, FastAPI, SQLAlchemy (async), Alembic, Jinja2, SCSS.
8 - **Database:** SQLite (local) / PostgreSQL (production), accessed only through the service layer.
9 - **Remote name for local dev:** `local` → `http://localhost:10003/gabriel/musehub`
10 - **Version control:** Muse and MuseHub exclusively. Git and GitHub are not used.
11
12 ## No legacy. No deprecated. No exceptions.
13
14 - **Delete on sight.** If you touch a file and find dead code, a deprecated API shape, a backward-compatibility shim, or a legacy fallback — delete it in the same commit.
15 - **No fallback paths for old shapes.** Remove every trace of the old way.
16 - **No "legacy" or "deprecated" comments.** If it's marked deprecated, delete it.
17 - **No dead constants, dead regexes, dead fields.** If it can never be reached, delete it.
18
19 When you remove something, remove it completely: implementation, tests, docs, config.
20
21 ## Version control — Muse only. Git and GitHub are not used.
22
23 All code changes, branching, merging, and releases happen through Muse. Never run `git`, `gh`, or reference GitHub. The Muse remote for this repo is `local`.
24
25 ### Starting work
26
27 ```
28 muse status # where am I, what's dirty
29 muse branch feat/my-thing # create branch
30 muse checkout feat/my-thing # switch to it
31 ```
32
33 ### While working
34
35 ```
36 muse status # constantly — like breathing
37 muse diff # symbol-level diff
38 muse code add . # stage
39 muse commit -m "..." # typed event; Muse proposes MAJOR/MINOR/PATCH
40 ```
41
42 ### Before merging
43
44 ```
45 muse fetch local
46 muse status
47 muse merge --dry-run main # confirm no symbol conflicts
48 ```
49
50 ### Merging
51
52 ```
53 muse checkout main
54 muse merge feat/my-thing
55 ```
56
57 ### Releasing
58
59 ```
60 # Create a local release at HEAD (--title and --body are required by convention)
61 muse release add <tag> --title "<title>" --body "<description>"
62
63 # Optionally set channel (default is inferred from semver pre-release label)
64 muse release add <tag> --title "<title>" --body "<description>" --channel stable
65
66 # Push to a remote
67 muse release push <tag> --remote local
68
69 # Full delete-and-recreate cycle (e.g. after a DB migration or data fix):
70 muse release delete <tag> --remote local --yes
71 muse release add <tag> --title "<title>" --body "<description>"
72 muse release push <tag> --remote local
73 ```
74
75 ### Branch discipline
76
77 **Never work directly on `main`.** Every task lives on a feature branch.
78
79 Full lifecycle:
80 1. `muse status` — clean before branching.
81 2. `muse branch feat/<desc>` then `muse checkout feat/<desc>`.
82 3. Do the work. Commit on the branch.
83 4. **Verify** before merging — in this exact order:
84 ```
85 mypy musehub/ # zero errors
86 pytest tests/ -v # all green
87 ```
88 5. `muse merge --dry-run main` — confirm clean.
89 6. `muse checkout main && muse merge feat/<desc>`.
90 7. `muse release add <tag> --title "<title>" --body "<description>"` then `muse release push <tag> --remote local`.
91
92 ## MuseHub interactions
93
94 The running server is at `http://localhost:10003`. Start/restart it with Docker Compose.
95
96 | Operation | Command |
97 |-----------|---------|
98 | Start server | `docker compose up -d` |
99 | View releases | `http://localhost:10003/gabriel/musehub/releases` |
100 | Push release | `muse release push <tag> --remote local` |
101 | Delete remote release | `muse release delete <tag> --remote local --yes` |
102 | Run migrations | `docker compose exec musehub alembic upgrade head` |
103 | Build SCSS | `docker compose exec musehub python -m tools.build_scss` (or equivalent) |
104
105 ## Architecture (do not weaken)
106
107 ```
108 musehub/
109 api/
110 routes/
111 wire.py → Muse CLI wire protocol (push, pull, releases)
112 musehub/
113 ui.py → Server-side rendered HTML pages
114 releases.py → Release CRUD API
115 db/
116 musehub_models.py → SQLAlchemy ORM models
117 models/
118 musehub.py → Pydantic request/response models (camelCase wire, snake_case Python)
119 services/
120 musehub_releases.py → ONLY module that touches musehub_releases table
121 templates/
122 musehub/
123 pages/ → Full-page Jinja2 templates
124 fragments/ → HTMX partial fragments
125 static/
126 scss/ → SCSS source; compiled to app.css
127 mcp/ → Model Context Protocol tools and resources
128 alembic/
129 versions/ → One migration per schema change; never edit applied migrations
130 tests/ → pytest + anyio async test suite
131 ```
132
133 **Layer rules:**
134 - Route handlers are thin — delegate all DB access to `services/`.
135 - `services/musehub_releases.py` is the ONLY file that touches `musehub_releases`.
136 - No business logic in templates — only presentation.
137 - Alembic migrations are append-only. Never edit an already-applied migration.
138
139 ## Frontend separation of concerns — absolute rule
140
141 Every concern lives in exactly one layer. Never mix them.
142
143 | Layer | Where it lives | What it does |
144 |-------|---------------|--------------|
145 | **Structure** | `templates/musehub/pages/*.html`, `fragments/*.html` | Jinja2 markup only — no `<style>`, no `<script>` tags |
146 | **Behaviour** | `templates/musehub/static/js/*.js` | Vanilla JS / Alpine.js / HTMX configuration |
147 | **Style** | `templates/musehub/static/scss/_*.scss` | All CSS, compiled to `app.css` via `app.scss` |
148
149 **Banned in templates:**
150 - `<style>` or `<style scoped>` blocks — move every rule to the appropriate `_*.scss` partial
151 - Inline `style="..."` attributes for anything beyond a truly dynamic value (e.g. `style="width:{{ pct }}%"`)
152 - `<script>` tags that contain non-trivial logic — extract to a `.js` file
153
154 **Required when adding new UI:**
155 1. New CSS classes → new or existing `scss/_*.scss` partial (use the file that matches the page/component)
156 2. New interactive behaviour → `static/js/` file imported by `app.js`
157 3. Compile SCSS locally after any change — `app.css` is committed and baked into the Docker image:
158 ```
159 sass musehub/templates/musehub/static/scss/app.scss musehub/templates/musehub/static/app.css --style=compressed --no-source-map
160 ```
161 4. Then rebuild the container: `docker compose up --build -d`
162
163 This rule applies retroactively. If you touch a template and find inline styles, pull them into SCSS in the same commit.
164
165 ## Code standards
166
167 - Type hints everywhere — 100% coverage, no untyped functions or parameters.
168 - `list[X]` / `dict[K, V]` style — never `List`, `Dict`, `Optional`.
169 - `X | None` — never `Optional[X]`.
170 - `logging.getLogger(__name__)` — never `print()`.
171 - Docstrings on public modules, classes, and functions.
172
173 ## Typing — zero-tolerance rules
174
175 | Banned | Use instead |
176 |--------|-------------|
177 | `Any` | `TypedDict`, `Protocol`, specific union |
178 | `object` | The actual type or a constrained union |
179 | `list` (bare) | `list[X]` |
180 | `dict` (bare) | `dict[K, V]` |
181 | `cast(T, x)` | Fix the callee to return `T` |
182 | `# type: ignore` | Fix the root cause |
183 | `Optional[X]` | `X \| None` |
184 | `List[X]`, `Dict[K,V]` | `list[X]`, `dict[K, V]` |
185
186 ## Verification checklist
187
188 Run before every merge to `main`:
189
190 - [ ] On a feature branch, not `main`
191 - [ ] `mypy musehub/` — zero errors
192 - [ ] `pytest tests/ -v` — all green
193 - [ ] No `Any`, bare collections, `cast()`, `# type: ignore`, `Optional[X]`, legacy `List`/`Dict`
194 - [ ] No dead code, no music-domain specific UI elements
195 - [ ] New DB columns have an Alembic migration
196 - [ ] Affected docs updated in the same commit
197
198 ## Anti-patterns (never do these)
199
200 - Using `git`, `gh`, or GitHub for anything. Muse and MuseHub are the only VCS tools.
201 - Working directly on `main`.
202 - Adding business logic to route handlers — delegate to services.
203 - Editing an already-applied Alembic migration — always create a new one.
204 - `Any`, bare collections, `cast()`, `# type: ignore` — absolute bans.
205 - `Optional[X]`, `List[X]`, `Dict[K,V]` — use modern syntax.
206 - Music-domain-specific UI elements (audio players, MIDI download buttons) in generic release pages.
207 - `print()` for diagnostics — use `logging`.