Skip to content

Commit d492caa

Browse files
committed
docs: add AGENTS.md, CONTRIBUTING.md, CHANGELOG.md and skill playbooks
Adds the canonical contributor + agent docs and a set of task-specific skill playbooks adapted from a production setup. Lockstep versioning is documented and the dashboard is aligned to the broker's version. Root docs - AGENTS.md: rules-of-engagement for any AI agent (Claude Code, Cursor, Codex, etc.) — conversational style, code quality (Rust + TS split), commands map, skills index, PR workflow, comment hygiene, branch/commit conventions, and the parallel-agent git safety rules. - CONTRIBUTING.md: human-facing contribution guide — local setup, issue/PR quality bar, the verification suite to run before submitting, commit message format, and security disclosure. - CHANGELOG.md: Keep a Changelog format. Documents lockstep versioning policy (broker and dashboard share one version, Cargo.toml is authoritative). - .gitignore: whitelist the three new docs from the existing /*.md ignore rule. Skill playbooks (.claude/skills/) - code-review + references/gotchas.md: catch project pitfalls linters miss (unwrap on Option::None for packet_id, dropped oneshot replies, missing Zustand reset(), hardcoded colors, missing i18n keys in en/ko/uz). - verify: full pre-commit suite — cargo check/clippy/fmt/test + eslint/prettier/yarn build. - debug-coremq: symptom→investigation table for broker, REST, MQTT WebSocket, and dashboard issues. - coremq-guide: directory map, Makefile commands, default ports. - new-backend-feature: the 7-step AdminCommand → Engine → Service → Controller → Route wiring. - web-page: page → view → table/drawer + Zustand store + service scaffold for new dashboard sections. Lockstep version alignment - client/package.json: 3.0.0 → 0.1.0 to match server/coremq-server/Cargo.toml. The 3.0.0 number was template-inherited and did not reflect project history. Flip Cargo.toml to 3.0.0 instead if that ordering is preferred — this commit is the safer cleanup direction. These changes are docs/config only. No source code is modified.
1 parent e468206 commit d492caa

12 files changed

Lines changed: 775 additions & 2 deletions

File tree

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
# Gotchas — Common Mistakes in CoreMQ
2+
3+
## Comment style
4+
5+
### Rust (`server/coremq-server/src/**/*.rs`)
6+
7+
- **Block comments only** — Use `/* */`, never `//` line comments. Project rule.
8+
- Multi-line:
9+
```rust
10+
/*
11+
* Assigns the next packet ID for QoS 1/2 publishes.
12+
* Returns None if the packet ID counter has wrapped.
13+
*/
14+
```
15+
- Do NOT use `// inline` or doc comments (`///`). Convert to `/* */` above the line, or delete if redundant.
16+
17+
### TypeScript (`client/src/**/*.{ts,tsx}`)
18+
19+
- **JSDoc only** — `/** */`, never `//` or `/* */` (non-JSDoc). Project rule.
20+
- Single-line: `/** Disconnect the active session. */` is fine for one-liners.
21+
- Do NOT use `// inline` or `// end-of-line` comments.
22+
23+
## Rust backend
24+
25+
- **`unwrap()` on `Option::None` panics** — especially around `packet_id` for QoS 1/2 publishes. Always use `ok_or(...)` or `if let Some(...)`. The `fix-panic_id-none-server-panic-bug` branch was created for exactly this class of bug.
26+
- **AdminCommand wiring** — every new command needs all 7 steps wired (model → command variant → service method → engine match arm → controller → route → mod.rs). Forgetting the engine match arm causes the controller's `oneshot::Receiver` to hang forever.
27+
- **Always reply on the oneshot** — the engine's `match` arm MUST send a response back through the `reply` sender, even on error paths. A dropped sender causes the controller to receive `RecvError` and return 500.
28+
- **No blocking calls in async code** — `std::fs`, `std::thread::sleep`, `std::sync::Mutex` (long held) all block the Tokio runtime. Use `tokio::fs`, `tokio::time::sleep`, `tokio::sync::Mutex`.
29+
- **Bounded channels for backpressure**Use `mpsc::channel(N)` not `unbounded_channel()` for hot paths (publish, connect). Unbounded channels can OOM under load.
30+
- **DashMap iteration locks shards** — never hold a `DashMap::iter()` guard across an `.await` point. Collect keys first, then process.
31+
- **ReDB transactions** — `WriteTransaction` must be committed or it silently rolls back. Always end with `txn.commit()?`.
32+
33+
## Frontend (React + TypeScript)
34+
35+
- **`type` over `interface`**Always. The only exception is `extend-theme-types.d.ts` (MUI module augmentation requires `interface`).
36+
- **`export default function`**All page and section components.
37+
- **No raw colors**Never hardcode hex/rgb in `sx`. Use theme tokens: `sx={{ bgcolor: 'background.paper' }}` not `sx={{ bgcolor: '#131825' }}`.
38+
- **Responsive padding always** — `sx={{ p: { xs: 2, sm: 3 } }}`, never `sx={{ p: 3 }}`.
39+
- **Pages are thin** — `pages/foo.tsx` should ONLY import and render the section view. Logic belongs in `sections/foo/foo_view.tsx`.
40+
- **i18n is mandatory**Any user-facing string must be `t('key')`. Adding a key requires updates to ALL THREE: `118n/en.json`, `118n/ko.json`, `118n/uz.json`. Missing one is a must-fix.
41+
42+
## Zustand stores
43+
44+
- **`reset()` is required** — every store must have `reset: () => set(initialState)` for logout cleanup. Missing reset = stale data after re-login.
45+
- **Separate State and Actions types**State is data only, Actions is functions only. Don't merge them.
46+
- **Shared `initialState` object** — defined once, used as both default and for `reset()`. Don't duplicate the literals.
47+
- **Selectors over full subscription** — `useFooStore(s => s.items)`, not `const { items } = useFooStore()`. The latter re-renders on every store change.
48+
49+
## API layer
50+
51+
- **Always wrap in `ApiResponse<T>`** — services return `ApiResponse<T>`, not raw `T`. The wrapper carries `success`, `data`, `error_message`.
52+
- **Token refresh is automatic** — don't manually handle 401 in services; the axios interceptor does it. Adding manual refresh logic causes double-refresh races.
53+
- **Bearer token from cookies**never read `localStorage` for auth. The interceptor attaches the bearer from cookie helpers.
54+
55+
## Theme & MUI
56+
57+
- **Drawers use `bgcolor: '#131825'` literally** — this is the one documented exception. Everywhere else: theme tokens.
58+
- **`JetBrains Mono Variable` for monospace data** — client IDs, topic names, ports. Don't use the default font.
59+
60+
## Build / Tooling
61+
62+
- **`Cargo.lock` is committed**for reproducible broker builds. Don't add it to `.gitignore`.
63+
- **Frontend uses `yarn`** (per Makefile) — `yarn dev`, `yarn install`. Don't switch to `npm install` casually; it produces a different lockfile and CI may break.
64+
- **Default ports**MQTT TCP `1883`, MQTT TLS `8883`, WS `8083`, REST `18083`, frontend dev `3039`. Don't hardcode different values in tests.
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
---
2+
name: code-review
3+
description: Review changed code for CoreMQ project conventions — catches gotchas linters miss like wrong comment style, missing i18n keys, hardcoded colors, AdminCommand wiring mistakes, Zustand store leaks. TRIGGER when user asks to review, check, or audit code they wrote.
4+
---
5+
6+
# CoreMQ Code Review
7+
8+
Review changed files against the project rules in `.claude/skills.md` and the codebase-specific pitfalls in `references/gotchas.md`.
9+
10+
## Steps
11+
12+
1. Run `cargo clippy --all-targets -- -D warnings` (server) and `npm run lint` + `npx prettier --check "src/**/*.{ts,tsx}"` (client) to catch automated issues
13+
2. Read each changed file and check against `.claude/skills.md` rules and `references/gotchas.md`
14+
3. Report findings grouped by severity: **must fix**, **should fix**, **nit**
15+
16+
Focus on things linters cannot catch: missing oneshot reply on an `AdminCommand` arm, blocking syscalls in async code, hardcoded MUI colors instead of theme tokens, i18n keys missing from one of `en.json`/`ko.json`/`uz.json`, Zustand store missing `reset()`, panics from `unwrap()` on `Option::None` (especially around `packet_id`).
17+
18+
## Output
19+
20+
```
21+
[must fix|should fix|nit] file:line — description
22+
```
23+
24+
Fix **must fix** and **should fix** automatically. Ask before fixing nits.
25+
26+
If more than 5 files, spawn a subagent for the review and implement fixes based on its findings.
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
---
2+
name: coremq-guide
3+
description: Reference for navigating the CoreMQ project — directory layout, build commands, default ports, where things live across the Rust broker and React dashboard. TRIGGER when user asks about project structure, where to put files, or how the server and client connect.
4+
---
5+
6+
# CoreMQ Project Guide
7+
8+
Cargo workspace + Vite-React app. One Rust crate (the broker), one TS app (the dashboard).
9+
10+
For deep conventions (TypeScript types, Zustand store shape, API routes, theme tokens, AdminCommand pattern), see `.claude/skills.md` — it's the canonical project doc.
11+
12+
## Structure
13+
14+
```
15+
coremq-rust/
16+
├── server/coremq-server/ — Rust MQTT broker (Tokio, Axum, ReDB, Casbin)
17+
│ └── src/
18+
│ ├── api/ — Axum REST API (controllers, router, auth)
19+
│ ├── engine/ — Core event loop + command enums + workers
20+
│ ├── services/ — session, topic, jwt
21+
│ ├── protocol/ — MQTT 3.1.1 / 5 wire protocol
22+
│ ├── transport/ — TCP / TLS / WebSocket listeners
23+
│ ├── storage/ — ReDB persistence
24+
│ ├── models/ — Serde structs (api/, engine/)
25+
│ └── main.rs
26+
├── client/ — React 19 + TS + MUI 7 + Zustand admin dashboard
27+
│ └── src/
28+
│ ├── pages/ — thin route wrappers
29+
│ ├── sections/ — feature views (UI + logic)
30+
│ ├── stores/ — Zustand state
31+
│ ├── services/ — axios API calls
32+
│ ├── types/ — TypeScript types
33+
│ ├── theme/ — MUI dark theme
34+
│ └── 118n/ — en.json, ko.json, uz.json
35+
├── docs/ — ARCHITECTURE.md, COREMQ_AI_NATIVE_PLATFORM.md, drawio diagrams
36+
├── tests/ — integration / stress / qos tests
37+
├── Cargo.toml — workspace root
38+
├── Makefile — `make dev` / `server` / `client` / `fmt` / `lint` / `fix`
39+
└── .claude/skills.md — full project conventions
40+
```
41+
42+
## Common Commands
43+
44+
| What | Command |
45+
| --------------------- | ------------------------------------------------ |
46+
| Run both | `make dev` |
47+
| Run broker only | `make server` (= `cargo run -p coremq-server`) |
48+
| Run frontend only | `make client` (= `cd client && yarn dev`) |
49+
| Install everything | `make setup` |
50+
| Build broker | `cargo build -p coremq-server` |
51+
| Test broker | `cargo test -p coremq-server` |
52+
| Format Rust | `cargo fmt -p coremq-server` |
53+
| Lint Rust | `cargo clippy -p coremq-server --all-targets` |
54+
| Format frontend | `make fmt` |
55+
| Lint frontend | `make lint` |
56+
| Lint + format fix | `make fix` |
57+
58+
## Default Ports
59+
60+
| Service | Port |
61+
| ------------- | ------- |
62+
| MQTT TCP | `1883` |
63+
| MQTT TLS | `8883` |
64+
| MQTT WebSocket| `8083` |
65+
| REST API | `18083` |
66+
| Frontend dev | `3039` |
67+
68+
## Default Credentials
69+
70+
Username: `admin` / Password: `public`
71+
72+
## Cross-cutting Wiring
73+
74+
The broker and dashboard talk over two channels:
75+
76+
- **REST API** (`http://localhost:18083/api/v1/...`) — admin operations: list sessions/topics/listeners/users, publish, login. See `server/coremq-server/src/api/router.rs` for the full route table.
77+
- **MQTT WebSocket** (`ws://localhost:8083`) — the dashboard speaks MQTT directly to subscribe to live topic updates. See `client/src/sections/websocket/`.
78+
79+
## Gotchas
80+
81+
- `Cargo.lock` IS committed (binary crate — needs reproducible builds).
82+
- Frontend uses **`yarn`**, not `npm install` (Makefile uses yarn, lockfile is `yarn.lock`).
83+
- `target/` and `client/node_modules/` are gitignored — don't commit either.
84+
- See `.claude/skills/debug-coremq/skill.md` for runtime issue diagnosis.
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
---
2+
name: debug-coremq
3+
description: Diagnose CoreMQ broker, REST API, MQTT client, or React dashboard issues — common errors, port conflicts, auth failures, QoS panics, WebSocket disconnects. TRIGGER when user reports a bug, error, or unexpected behavior.
4+
---
5+
6+
# Debug CoreMQ
7+
8+
Systematic debugging for the CoreMQ MQTT broker and admin dashboard.
9+
10+
## Symptom → Investigation Map
11+
12+
| Symptom | First check | Then check |
13+
| -------------------------------------- | --------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------- |
14+
| Broker won't start | `lsof -iTCP:1883 -sTCP:LISTEN` — is another process holding the port? | Check `cargo run` output for `bind` errors and ReDB lock messages |
15+
| `cargo run` panics on QoS publish | Look for `unwrap()` on `packet_id` — must be a known bug class | Check the `fix-panic_id-none-server-panic-bug` branch / commit `2f10d8d` for fix |
16+
| MQTT client disconnects immediately | `cargo run` log: auth failure? Casbin policy missing? | Verify default `admin/public` creds and that JWT secret env var is set |
17+
| REST API returns 401 after login | Token cookie not being attached — check axios interceptor + cookie helpers | Verify `/api/v1/public/login` returns the token and the refresh endpoint works |
18+
| REST controller hangs forever | The engine `match` arm is not sending on the `oneshot::Sender` — must reply | grep for the `AdminCommand` variant in `engine/engine.rs` `run()` loop |
19+
| Frontend shows stale data after logout | Some Zustand store is missing `reset()` | Check every store in `client/src/stores/` has `reset: () => set(initialState)` |
20+
| MQTT WebSocket fails to connect | Is `:8083` listening? Browser console: CORS or upgrade failure? | Check `client/src/sections/websocket/` for the connect logic and broker URL |
21+
| Topic publish doesn't match subscriber | Wildcard order — `+` matches one level, `#` matches multi (must be at end) | Check `services/topic.rs` matcher; verify subscriber QoS is supported by broker |
22+
| `cargo build` fails on macOS | Toolchain stale: `rustup update` | `cargo clean` if proc-macro errors persist |
23+
| Frontend dev server port conflict | `lsof -iTCP:3039 -sTCP:LISTEN` — kill the stale process | Check `vite.config.ts` for the configured port |
24+
| i18n key shows as raw `feature.title` | Key missing from one of `118n/en.json`, `118n/ko.json`, `118n/uz.json` | Add to ALL THREE files — partial translations cause this |
25+
26+
## Quick Health Check
27+
28+
```bash
29+
# Backend reachable?
30+
curl -s http://localhost:18083/api/v1/public/login -X POST \
31+
-H 'content-type: application/json' \
32+
-d '{"username":"admin","password":"public"}' | jq .
33+
34+
# MQTT TCP listening?
35+
nc -zv localhost 1883
36+
37+
# Frontend dev server up?
38+
curl -s http://localhost:3039 -o /dev/null -w '%{http_code}\n'
39+
```
40+
41+
## Gotchas
42+
43+
- **`packet_id` must be assigned for QoS 1/2 publishes** — missing assignment caused the server panic fixed in `2f10d8d`. Always set before passing to the engine.
44+
- **ReDB write lock is exclusive** — only one `WriteTransaction` at a time. Long-held write txns block all writers.
45+
- **DashMap iteration across `.await` deadlocks** — collect keys first, drop the iter, then await.
46+
- **Token refresh races** — only the axios interceptor handles 401. Don't add manual refresh in services.
47+
- **Frontend uses `yarn`, not `npm install`** — using `npm install` will rewrite lockfile and may break CI.
48+
- **`Cargo.lock` IS committed** — don't `.gitignore` it; the broker is a binary and needs reproducible deps.
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
---
2+
name: new-backend-feature
3+
description: Scaffold a new admin feature in the Rust broker following the AdminCommand → Engine → Service → Controller → Route pattern. TRIGGER when user asks to add, create, or scaffold a new backend feature, REST endpoint, or admin operation.
4+
---
5+
6+
# Scaffold New Backend Feature (CoreMQ)
7+
8+
Every admin operation in CoreMQ flows through the same 7-step wiring. Skipping any step causes hard-to-find bugs (controller hangs, command never runs, route 404s).
9+
10+
Read `.claude/skills.md` "Engine Command Pattern" before starting. Follow Rust conventions: `/* */` block comments only, snake_case functions, PascalCase types.
11+
12+
## Before Scaffolding
13+
14+
1. Check `server/coremq-server/src/api/router.rs` — does a similar route already exist?
15+
2. Check `server/coremq-server/src/engine/commands.rs` — could you extend an existing variant instead of adding a new one?
16+
3. Decide: does this need a oneshot reply (synchronous) or fire-and-forget (rare)? Default: oneshot.
17+
18+
## The 7 Steps
19+
20+
1. **Model**`server/coremq-server/src/models/api/<feature>.rs`. Request/response structs with `Serialize`/`Deserialize`. Add `pub mod <feature>;` to `models/api/mod.rs`.
21+
2. **Command variant** — Add to `AdminCommand` enum in `server/coremq-server/src/engine/commands.rs`. Include the request payload + a `reply: oneshot::Sender<Result<Resp, ApiError>>`.
22+
3. **Service method** — Add to the relevant service in `server/coremq-server/src/services/`. Pure logic, no channels. Returns `Result<Resp, ApiError>`.
23+
4. **Engine handler** — Add a `match` arm in `engine/engine.rs` `run()` loop. Calls the service, then `let _ = reply.send(result);`. **Always reply, even on error.** A dropped sender = hung controller.
24+
5. **Controller**`server/coremq-server/src/api/controllers/<feature>.rs`. Creates `oneshot::channel()`, sends the `AdminCommand`, awaits the reply, wraps in `ApiResponse<T>`. Add `pub mod <feature>;` to `controllers/mod.rs`.
25+
6. **Route** — Register in `api/router.rs` with method + path + auth layer. Follow the `/api/v1/<resource>` convention.
26+
7. **Auth policy** — If the route is admin-protected, add the Casbin policy entry. Check existing routes for the pattern.
27+
28+
## Verify
29+
30+
1. `cargo check -p coremq-server` — compiles?
31+
2. `cargo clippy -p coremq-server --all-targets -- -D warnings` — clean?
32+
3. Run the broker, hit the new endpoint with `curl`:
33+
```bash
34+
TOKEN=$(curl -s -X POST http://localhost:18083/api/v1/public/login \
35+
-H 'content-type: application/json' \
36+
-d '{"username":"admin","password":"public"}' | jq -r .data.access_token)
37+
38+
curl -s http://localhost:18083/api/v1/<your-route> \
39+
-H "authorization: Bearer $TOKEN" | jq .
40+
```
41+
42+
## Gotchas
43+
44+
- **Never drop the `oneshot::Sender` without sending.** Forgetting `reply.send(...)` in an error path = controller hangs until timeout.
45+
- **The engine `run()` loop is single-threaded.** Don't call long-blocking work from a match arm — spawn a task or call into a service that uses a worker pool.
46+
- **`AdminCommand` variants must be exhaustively matched** — adding a variant without an arm is a compile error, which is the bug you want.
47+
- **Frontend wiring is separate** — after the backend is done, see `.claude/skills/web-page/skill.md` for the React side (service function + Zustand store + section view).

.claude/skills/verify/skill.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
---
2+
name: verify
3+
description: Run the full CoreMQ verification suite — cargo check/clippy/test on the Rust broker, lint and prettier on the React client. TRIGGER when user asks to verify, test, check, or validate changes before committing.
4+
---
5+
6+
# CoreMQ Verification
7+
8+
Run all checks in order. Stop on first failure. Report a one-line PASS/FAIL per step.
9+
10+
## Steps
11+
12+
### Backend (`server/coremq-server`)
13+
14+
1. **Type check**: `cargo check -p coremq-server`
15+
2. **Lint**: `cargo clippy -p coremq-server --all-targets -- -D warnings`
16+
3. **Format check**: `cargo fmt -p coremq-server -- --check`
17+
4. **Tests**: `cargo test -p coremq-server`
18+
19+
### Frontend (`client`)
20+
21+
5. **Lint**: `cd client && npx eslint "src/**/*.{js,jsx,ts,tsx}"`
22+
6. **Format check**: `cd client && npx prettier --check "src/**/*.{ts,tsx}"`
23+
7. **Type + build**: `cd client && yarn build`
24+
25+
### Final
26+
27+
8. **Git status**: ensure no uncommitted untracked artifacts (e.g., `target/`, `dist/`, `node_modules/`)
28+
29+
## Auto-fix
30+
31+
- If `cargo fmt --check` fails, auto-fix with `cargo fmt -p coremq-server`.
32+
- If prettier check fails, auto-fix with `cd client && npx prettier --write "src/**/*.{ts,tsx}"`.
33+
- If eslint fails on auto-fixable rules, run `cd client && npm run lint:fix`.
34+
35+
## Output
36+
37+
Report one line per step: `PASS` or `FAIL` with a short error summary. After all steps, print a final summary.

0 commit comments

Comments
 (0)