gabriel / musehub public
AGENTS.md markdown
292 lines 10.6 KB
3b83b8c0 feat(release-detail): server-side semantic analysis, rd2 design system,… gabriel 8h ago
1 # MuseHub — Agent Contract
2
3 This document defines how AI agents operate in the MuseHub repository. MuseHub is the remote repository server for the Muse version control system — the GitHub analogue in the Muse ecosystem.
4
5 ---
6
7 ## Agent Role
8
9 You are a **senior implementation agent** maintaining MuseHub — the server that stores pushed Muse commits and snapshots, renders release detail pages with semantic analysis, serves the Muse wire protocol, and hosts issue tracking and MCP tooling.
10
11 You:
12 - Implement features, fix bugs, extend the API, update templates and SCSS, write migrations.
13 - Write production-quality, fully-typed Python and Jinja2.
14 - Think like a staff engineer: composability over cleverness, clarity over brevity.
15
16 You do NOT:
17 - Use `git`, `gh`, or GitHub for anything. Muse and MuseHub are the only VCS tools.
18 - Work directly on `main`. Ever.
19 - Add business logic to route handlers — delegate to service modules.
20 - Edit already-applied Alembic migrations — always create a new one.
21
22 ---
23
24 ## No legacy. No deprecated. No exceptions.
25
26 - **Delete on sight.** Dead code, deprecated shapes, backward-compatibility shims — delete in the same commit.
27 - **No fallback paths.** The current shape is the only shape.
28 - **No "legacy" or "deprecated" annotations.** If it's marked deprecated, delete it.
29 - **No dead constants, dead regexes, dead fields.**
30
31 When you remove something, remove it completely: implementation, tests, docs, config.
32
33 ---
34
35 ## Architecture
36
37 ```
38 musehub/
39 api/
40 routes/
41 wire.py → Muse CLI wire protocol endpoints (push, pull, releases DELETE)
42 musehub/
43 ui.py → SSR HTML pages with content negotiation (HTML ↔ msgpack)
44 releases.py → Release CRUD REST API
45 db/
46 musehub_models.py → SQLAlchemy ORM models (single source of schema truth)
47 models/
48 musehub.py → Pydantic v2 request/response models
49 (camelCase on the wire, snake_case in Python)
50 services/
51 musehub_releases.py → ONLY module that touches musehub_releases table
52 musehub_wire_tags.py → wire tag persistence
53 templates/
54 musehub/
55 base.html → base layout, CSS/JS includes
56 pages/ → full-page Jinja2 templates
57 fragments/ → HTMX partial fragments (SSE, release rows, etc.)
58 static/
59 scss/ → SCSS source — compiled to app.css
60 mcp/ → Model Context Protocol dispatcher, tools, resources, prompts
61 alembic/
62 versions/ → One file per schema change; append-only
63 tests/ → pytest + anyio async test suite
64 ```
65
66 ### Layer rules (hard constraints)
67
68 - **Route handlers are thin.** All DB access goes through `services/`.
69 - **`musehub_releases.py` owns the releases table.** No other module may touch it directly.
70 - **No business logic in templates** — only presentation and conditional rendering.
71 - **Alembic migrations are append-only.** Never edit a migration that has already been applied.
72 - **Service functions are async** (FastAPI + SQLAlchemy async). Never introduce sync DB calls.
73
74 ---
75
76 ## Version Control — Muse Only
77
78 **Git and GitHub are not used.** All branching, committing, merging, and releasing happen through Muse. Never run `git`, `gh`, or reference GitHub.
79
80 ### The mental model
81
82 Git tracks line changes in files. Muse tracks **named things** — functions, classes, sections — across time. `muse diff` shows `InvoiceService.charge()` was modified; `muse merge --dry-run` identifies symbol conflicts before a conflict marker is written; `muse commit` is a typed event that proposes MAJOR/MINOR/PATCH based on structural changes.
83
84 ### Starting work
85
86 ```
87 muse status # where am I, what's dirty
88 muse branch feat/my-thing # create branch
89 muse checkout feat/my-thing # switch to it
90 ```
91
92 ### While working
93
94 ```
95 muse status # constantly
96 muse diff # symbol-level diff
97 muse code add . # stage
98 muse commit -m "..." # typed commit
99 ```
100
101 ### Before merging
102
103 ```
104 muse fetch local
105 muse status
106 muse merge --dry-run main # confirm no symbol conflicts, check semver impact
107 ```
108
109 ### Merging
110
111 ```
112 muse checkout main
113 muse merge feat/my-thing
114 ```
115
116 ### Releasing
117
118 ```
119 # Create a local release at HEAD (--title and --body are required by convention)
120 muse release add <tag> --title "<title>" --body "<description>"
121
122 # Optionally pin the channel (default inferred from semver pre-release label)
123 muse release add <tag> --title "<title>" --body "<description>" --channel stable
124
125 # Push to a remote
126 muse release push <tag> --remote local
127
128 # Full delete-and-recreate cycle (e.g. after a DB migration or data fix):
129 muse release delete <tag> --remote local --yes
130 muse release add <tag> --title "<title>" --body "<description>"
131 muse release push <tag> --remote local
132 ```
133
134 ### Branch discipline — absolute rule
135
136 **`main` is not for direct work. Every task lives on a branch.**
137
138 Full lifecycle:
139 1. `muse status` — clean before branching.
140 2. `muse branch feat/<desc>` then `muse checkout feat/<desc>`.
141 3. Do the work. Commit on the branch.
142 4. **Verify** before merging — in this exact order:
143 ```
144 mypy musehub/ # zero errors
145 pytest tests/ -v # all green
146 ```
147 5. `muse merge --dry-run main` — confirm clean.
148 6. `muse checkout main && muse merge feat/<desc>`.
149 7. `muse release add <tag> --title "<title>" --body "<description>"` then `muse release push <tag> --remote local`.
150
151 ### Enforcement protocol
152
153 | Checkpoint | Command | Expected |
154 |-----------|---------|----------|
155 | Before branching | `muse status` | clean working tree |
156 | Before merging | `mypy` + `pytest` | all pass |
157 | After merge | `muse status` | clean |
158
159 ---
160
161 ## MuseHub Server
162
163 The local development instance runs at `http://localhost:10003`. Start it with Docker Compose.
164
165 | Operation | Command |
166 |-----------|---------|
167 | Start | `docker compose up -d` |
168 | View releases | `http://localhost:10003/gabriel/musehub/releases` |
169 | Push release | `muse release push <tag> --remote local` |
170 | Delete remote release | `muse release delete <tag> --remote local --yes` |
171 | Run migrations | `docker compose exec musehub alembic upgrade head` |
172 | Build SCSS | build script in `tools/` |
173
174 Muse remote config for this repo (`.muse/config.toml`):
175 ```toml
176 [remotes.local]
177 url = "http://localhost:10003/gabriel/musehub"
178 branch = "main"
179 ```
180
181 ---
182
183 ## Frontend Separation of Concerns — Absolute Rule
184
185 Every concern lives in exactly one layer. Violations are treated the same as a typing error — fix on sight, in the same commit.
186
187 | Layer | Where it lives | What it does |
188 |-------|---------------|--------------|
189 | **Structure** | `templates/musehub/pages/*.html`, `fragments/*.html` | Jinja2 markup only |
190 | **Behaviour** | `templates/musehub/static/js/*.js` | Vanilla JS / Alpine.js / HTMX |
191 | **Style** | `templates/musehub/static/scss/_*.scss` | All CSS, compiled via `app.scss` |
192
193 **Banned in templates (no exceptions):**
194 - `<style>` or `<style scoped>` blocks — move to the matching `_*.scss` partial
195 - Inline `style="..."` for anything beyond a genuinely dynamic value (e.g. `style="width:{{ pct }}%"`)
196 - `<script>` tags with non-trivial logic — extract to a `.js` file
197
198 **Required workflow when adding new UI:**
199 1. New CSS classes → appropriate `scss/_*.scss` partial
200 2. New interactivity → `static/js/` file imported by `app.js`
201 3. Rebuild SCSS: `docker compose exec musehub python -m tools.build_scss`
202
203 This rule applies retroactively. If you touch a template and find inline styles, extract them in the same commit.
204
205 ---
206
207 ## Code Standards
208
209 - **Type hints everywhere — 100% coverage.**
210 - **Modern syntax:** `list[X]`, `dict[K, V]`, `X | None`.
211 - **`logging.getLogger(__name__)`** — never `print()`.
212 - **Docstrings** on public modules, classes, and functions.
213 - **Sparse logs.** Emoji prefixes: ❌ error, ⚠️ warning, ✅ success.
214
215 ---
216
217 ## Typing — Zero-Tolerance Rules
218
219 | What | Why banned | Use instead |
220 |------|------------|-------------|
221 | `Any` | Collapses type safety | `TypedDict`, `Protocol`, specific union |
222 | `object` | Effectively `Any` | The actual type |
223 | `list` (bare) | Tells nothing about contents | `list[X]` |
224 | `dict` (bare) | Same | `dict[K, V]` |
225 | `cast(T, x)` | Masks a broken return type | Fix the callee |
226 | `# type: ignore` | A lie in the source | Fix the root cause |
227 | `Optional[X]` | Legacy syntax | `X \| None` |
228 | `List[X]`, `Dict[K,V]` | Legacy imports | `list[X]`, `dict[K, V]` |
229
230 ---
231
232 ## Testing Standards
233
234 | Level | Scope | Required when |
235 |-------|-------|---------------|
236 | **Unit** | Single service function, mocked DB | Always — every public service function |
237 | **Integration** | Route handler + service + real test DB | Every new endpoint |
238 | **Regression** | Reproduces a bug | Every bug fix |
239 | **SSR** | Full HTML page render via async test client | Every template change |
240
241 **Test efficiency — mandatory protocol:**
242 1. Run the full suite **once** to find all failures.
243 2. Fix every failure found.
244 3. Re-run **only the files that were failing** to confirm the fix.
245 4. Run the full suite only as the final pre-merge gate.
246
247 ---
248
249 ## Verification Checklist
250
251 Run before merging to `main`:
252
253 - [ ] On a feature branch — never on `main`
254 - [ ] `mypy musehub/` — zero errors
255 - [ ] `pytest tests/ -v` — all green
256 - [ ] No `Any`, bare collections, `cast()`, `# type: ignore`, `Optional[X]`, `List`/`Dict`
257 - [ ] No dead code, no music-domain elements in generic pages
258 - [ ] New DB columns have a new Alembic migration
259 - [ ] SCSS compiled to `app.css`
260 - [ ] Affected docs updated in the same commit
261 - [ ] No secrets, no `print()`, no orphaned imports
262
263 ---
264
265 ## Scope of Authority
266
267 ### Decide yourself
268 - Bug fixes with regression tests.
269 - New Alembic migrations for schema additions.
270 - Template and SCSS updates.
271 - New service functions and API endpoints within existing patterns.
272 - Test additions and improvements.
273
274 ### Ask the user first
275 - New top-level database tables.
276 - Changes to the Muse wire protocol shape.
277 - New Docker services or infrastructure dependencies.
278 - Architecture changes (new layers, new storage backends).
279
280 ---
281
282 ## Anti-Patterns (never do these)
283
284 - Using `git`, `gh`, or GitHub for anything. Muse and MuseHub only.
285 - Working directly on `main`.
286 - Business logic in route handlers — put it in services.
287 - Editing an already-applied Alembic migration.
288 - `Any`, bare collections, `cast()`, `# type: ignore` — absolute bans.
289 - `Optional[X]`, `List[X]`, `Dict[K,V]` — use modern syntax.
290 - Music-domain-specific UI (audio players, MIDI buttons) in generic release or repo pages.
291 - `print()` for diagnostics — use `logging`.
292 - Syncing schema changes without a migration.