Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 06dba9a4ad | |||
| 21956f7a42 | |||
| 8729f541c0 | |||
| 5a96359bb1 | |||
| 87e0b9359e |
@@ -1,5 +1,15 @@
|
||||
# Architect Mode Behavior — Roo Code
|
||||
|
||||
## Persona Context — Which Patrick is planning?
|
||||
|
||||
Before starting, identify the active context from the conversation:
|
||||
- **Homelab Patrick** → plan for TrueNAS Docker services, local hardware constraints, Gitea as source of truth
|
||||
- **ADP/Paisy Patrick** → plan with compliance mindset, assessment-first, German ticket language, PR-only workflow
|
||||
- **MCP Builder Patrick** → plan for FastMCP conventions, pi_mcps structure, pytest coverage expectations
|
||||
- **BigMind Patrick** → plan with DB migration safety, test-first, Flask/SQLite constraints in mind
|
||||
|
||||
Adapt planning depth and output format to match the active persona.
|
||||
|
||||
## Planning Process
|
||||
1. **Search Context:** `memory_search_facts("similar project")` + `memory_list_sessions(topics_filter="architecture")`
|
||||
2. **Form Hypothesis:** `memory_add_hypothesis(session_id, "This architecture will scale to X users with confidence 0.7")`
|
||||
@@ -10,8 +20,9 @@
|
||||
- **Break Down:** Large tasks → subtasks with MCP servers (Docker, Gitea, Ollama)
|
||||
- **Homelab Focus:** Leverage TrueNAS Docker for services, 1.2TB SSD for VMs/DBs
|
||||
- **Token Efficiency:** Reference past architectures from memory, log savings
|
||||
- **Assessment First:** For any Paisy/ADP task, always produce an assessment markdown before proposing code
|
||||
|
||||
## After Planning
|
||||
1. **Store Decision:** `memory_store_fact("decision", "Chose Z architecture for reasons A B C")`
|
||||
1. **Store Decision:** `memory_store_fact("architecture-decision", "Chose Z architecture for reasons A B C")`
|
||||
2. **Flag Plan:** `memory_flag_important(session_id, "Architecture plan for Y", role="assistant")`
|
||||
3. **Resolve Hypothesis:** Update based on plan validation
|
||||
3. **Resolve Hypothesis:** Update based on plan validation
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
# Ask Mode Behavior — Roo Code
|
||||
|
||||
## Persona Context — Which Patrick is asking?
|
||||
|
||||
The answer style and knowledge focus should match the active context:
|
||||
- **Homelab Patrick** → ground answers in real hardware facts (TrueNAS IP, Docker stack, local storage)
|
||||
- **ADP/Paisy Patrick** → prefer Java/Maven/Oracle/EclipseLink answers; reference compliance constraints
|
||||
- **MCP Builder Patrick** → lean on FastMCP patterns, pi_mcps conventions, and existing server examples
|
||||
- **BigMind Patrick** → reference schema version (v7), phase (2.7), and current tool count before speculating
|
||||
|
||||
## Before Answering Any Question
|
||||
1. **Search Memory:** `memory_search_facts("topic keywords")` + `memory_search_chunks("past discussion")`
|
||||
2. **Check Hypotheses:** If the question touches an open hypothesis, reference it — confirm or update confidence
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
# BigMind Mode Behavior — Roo Code
|
||||
|
||||
## Active Persona: Introspective Patrick
|
||||
|
||||
Patrick is working on BigMind itself — the memory system that is Lumen's superpower. This is the most careful mode. Breaking BigMind means breaking the brain that makes everything else work.
|
||||
|
||||
## BigMind System State (always active in this mode)
|
||||
|
||||
| Aspect | Current State |
|
||||
|--------|--------------|
|
||||
| DB Location | `~/.mcp/bigmind/memory.db` |
|
||||
| Schema Version | v7 (People/Contacts directory added) |
|
||||
| Journal Mode | WAL (multi-IDE safe, 30s write timeout) |
|
||||
| Tool Count | ~25 tools |
|
||||
| Test Count | ~282+ tests |
|
||||
| Flask Port | 7700 (profile page, auto-refreshes 30s) |
|
||||
| Current Phase | 2.7 (profile features). Phase 3 = Company Brain |
|
||||
|
||||
## Memory Tier Architecture
|
||||
- **Tier 0:** Identity profile (who Lumen is)
|
||||
- **Tier 1:** Session index (recent session headlines)
|
||||
- **Tier 2:** Session narratives (detailed summaries)
|
||||
- **Tier 3:** Conversation chunks (flagged important exchanges)
|
||||
- **Facts:** Atomic reusable facts with FTS5 search
|
||||
- **Hypotheses:** Thought journal (open/confirmed/refuted/abandoned)
|
||||
- **People:** Contacts directory (v7 addition)
|
||||
|
||||
## Before Starting Any BigMind Task
|
||||
1. **Search Past Work:** `memory_search_facts("BigMind schema")` + `memory_search_chunks("bigmind feature")`
|
||||
2. **Check Schema Version:** Never assume — read `db.py` SCHEMA_VERSION before migrating
|
||||
3. **Create a branch (MANDATORY — never work on main):**
|
||||
```bash
|
||||
git checkout -b feat/bigmind/feature-name
|
||||
# or fix/bigmind/bug-name for a bug fix
|
||||
```
|
||||
4. **Announce Focus (include branch name):** `memory_announce_focus(session_id, "BigMind: adding feature X on branch feat/bigmind/feature-name", files=["bigmind/db.py", "bigmind/memory_store.py"], ide_hint="VS Code")`
|
||||
5. **Form Hypothesis:** `memory_add_hypothesis(session_id, "Feature X requires schema v{n+1} migration with Y new columns")`
|
||||
|
||||
## Schema Change Rules (non-negotiable)
|
||||
- Every schema change needs a migration function: `_migrate_v{n}_to_v{n+1}(conn)`
|
||||
- Increment `SCHEMA_VERSION` constant in `db.py`
|
||||
- `init_db()` must call migrations in sequence
|
||||
- Test the migration against a populated DB (not just fresh)
|
||||
- Never drop columns or rename them without a deprecation strategy
|
||||
|
||||
## API Contract Rules
|
||||
- Never remove a tool from `server.py` — it breaks connected IDEs
|
||||
- Never change a tool's parameter names — use optional params with defaults for new fields
|
||||
- Server restart (`memory_restart_server`) is safe but loses in-memory state — ensure sessions are closed first
|
||||
|
||||
## Code Architecture
|
||||
```
|
||||
~/.mcp/bigmind/
|
||||
bigmind/
|
||||
db.py ← schema, init_db(), migrations
|
||||
memory_store.py ← all CRUD functions
|
||||
context_builder.py ← Tier 0+1 context assembly
|
||||
profile_builder.py ← stats, achievements, heatmap
|
||||
web.py ← Flask server (daemon thread)
|
||||
web_render.py ← HTML rendering (split from web.py)
|
||||
auto_close.py ← orphan session cleanup, server restart
|
||||
server.py ← FastMCP tools (thin wrappers over memory_store)
|
||||
tests/ ← pytest suite (282+ tests)
|
||||
pyproject.toml
|
||||
```
|
||||
|
||||
## Testing Rules
|
||||
- Full test suite must pass before any PR/commit: `uv run pytest tests/ -v`
|
||||
- New features: write tests first
|
||||
- New migrations: test both fresh DB and populated DB paths
|
||||
- FTS5 queries: test AND-match, reserved words, multi-word queries
|
||||
|
||||
## Flask Web Server
|
||||
- Runs as daemon thread inside MCP process on startup
|
||||
- Port: `BIGMIND_PORT` env var (default 7700)
|
||||
- Auto-open: `BIGMIND_AUTOOPEN=true`
|
||||
- Profile page at `http://localhost:7700` — Lumen's own identity page
|
||||
|
||||
## After BigMind Changes
|
||||
1. **Store Fact:** `memory_store_fact("codebase", "BigMind v{schema}: added X feature — Y new tools, Z tests")`
|
||||
2. **Bump schema version** in stored fact if applicable
|
||||
3. **Flag the Session:** `memory_flag_important(session_id, "BigMind feature: X shipped", role="assistant")`
|
||||
4. **Resolve Hypothesis:** Was the migration approach correct?
|
||||
5. **Restart if needed:** `memory_restart_server()` — only after closing the current session
|
||||
@@ -1,5 +1,16 @@
|
||||
# Code Mode Behavior — Roo Code
|
||||
|
||||
## Persona Context — Which Patrick is coding?
|
||||
|
||||
Before writing code, identify the active context to apply the right conventions:
|
||||
|
||||
| Persona | Language | Conventions |
|
||||
|---------|----------|-------------|
|
||||
| Homelab Patrick | Python / bash / YAML | Docker Compose, TrueNAS compatible, uv + FastMCP |
|
||||
| ADP/Paisy Patrick | Java / Maven | feature/bugfix branches only, no direct push to main, assessment-first |
|
||||
| MCP Builder Patrick | Python | FastMCP pattern, pi_mcps structure, 100% mock test coverage |
|
||||
| BigMind Patrick | Python / SQL | schema migration safety, WAL mode, no breaking API changes |
|
||||
|
||||
## Before Writing Code
|
||||
1. **Search Memory:** `memory_search_facts("codebase [project]")` + `memory_search_chunks("similar code")`
|
||||
2. **Form Hypothesis:** `memory_add_hypothesis(session_id, "I predict X will fix Y with confidence 0.8")`
|
||||
@@ -7,7 +18,7 @@
|
||||
|
||||
## Coding Patterns
|
||||
- **Python:** Use uv for dependencies, FastMCP for MCP servers, pytest for tests
|
||||
- **Java:** Maven for Paisy projects, Spring Boot patterns
|
||||
- **Java:** Maven for Paisy projects, feature/bugfix branch required, never push to main
|
||||
- **Testing:** Always write tests first, mock external calls
|
||||
- **Token Efficiency:** Use `memory_log_token_save` when reusing code from memory
|
||||
|
||||
@@ -19,4 +30,4 @@
|
||||
## Error Handling
|
||||
- If code fails, form new hypothesis: "Bug might be in dependency version"
|
||||
- Search: `memory_search_chunks("similar error")`
|
||||
- Debug systematically, log token savings from targeted searches
|
||||
- Debug systematically, log token savings from targeted searches
|
||||
|
||||
@@ -1,7 +1,15 @@
|
||||
# Debug Mode Behavior — Roo Code
|
||||
|
||||
## Persona Context — Which Patrick is debugging?
|
||||
|
||||
Match the debugging approach to the active context:
|
||||
- **Homelab Patrick** → check Docker logs, TrueNAS container state, Fedora system logs first
|
||||
- **ADP/Paisy Patrick** → search BigMind for known bug patterns (ORA-00001, NPE, EclipseLink flush issues) before exploring
|
||||
- **MCP Builder Patrick** → check FastMCP server logs, uv environment, MCP tool registration issues
|
||||
- **BigMind Patrick** → WAL mode, concurrent IDE access, SQLite locking, FTS5 query issues
|
||||
|
||||
## Debugging Process
|
||||
1. **Search Similar Issues:** `memory_search_chunks("similar bug")` + `memory_search_facts("codebase error")`
|
||||
1. **Search Similar Issues:** `memory_search_chunks("similar bug")` + `memory_search_facts("bug-pattern [domain]")`
|
||||
2. **Form Hypothesis:** `memory_add_hypothesis(session_id, "Bug is in X due to Y, confidence 0.6")`
|
||||
3. **Announce Focus:** `memory_announce_focus(session_id, "Debugging Z in file.py", files=["file.py"], ide_hint="VS Code")`
|
||||
4. **Systematic Steps:** Add logging, analyze stack traces, test incrementally
|
||||
@@ -10,8 +18,13 @@
|
||||
- **MCP Leverage:** Use mcp-homelab-shell for quick tests, mcp-homelab-docker for container logs
|
||||
- **Homelab:** Check Ollama models, TrueNAS VMs if relevant
|
||||
- **Token Efficiency:** Search memory for past fixes before reading full logs
|
||||
- **Bug Patterns to check first:**
|
||||
- ORA-00001 → duplicate hash constraint violations (ADVFEX migration pattern)
|
||||
- NPE in Paisy → null getSendungsHeader() before null-check
|
||||
- SSL errors → Fedora missing Comodo AAA root cert (see stored certs fix)
|
||||
- FTS5 errors → reserved word collision in search query (wrap tokens in quotes)
|
||||
|
||||
## After Resolution
|
||||
1. **Store Fix:** `memory_store_fact("codebase", "Fixed bug in X by doing Y")`
|
||||
1. **Store Fix:** `memory_store_fact("bug-pattern", "Fixed bug in X by doing Y — root cause was Z")`
|
||||
2. **Resolve Hypothesis:** `memory_resolve_hypothesis(hypothesis_id, "confirmed", "Root cause was Z")`
|
||||
3. **Flag Resolution:** `memory_flag_important(session_id, "Debug resolution summary", role="assistant")`
|
||||
3. **Flag Resolution:** `memory_flag_important(session_id, "Debug resolution summary", role="assistant")`
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
# Homelab Mode Behavior — Roo Code
|
||||
|
||||
## Active Persona: Tinkerer Patrick
|
||||
|
||||
Patrick is in homelab mindset. He is experimenting, building, and maintaining his personal infrastructure ecosystem. No corporate constraints — full admin rights on everything.
|
||||
|
||||
## Infrastructure Context (always active in this mode)
|
||||
|
||||
| Asset | Details |
|
||||
|-------|---------|
|
||||
| Workstation | Fedora Linux, Ryzen 5900X, RX 7900 XTX (24GB VRAM), 8TB NVMe |
|
||||
| Server | TrueNAS.local @ 192.168.188.119, Ryzen 5900X, Docker, 1.2TB SSD pool |
|
||||
| Gitea | http://192.168.188.119:30008/ — homelab git server, source of truth |
|
||||
| AI | Ollama (local models), Grok Code (prepaid), Claude Code ($50 prepaid) |
|
||||
| MCP base | ~/pi_mcps/ — all MCP servers live here |
|
||||
|
||||
## Before Starting Any Homelab Task
|
||||
1. **Search Infrastructure Facts:** `memory_search_facts("TrueNAS Docker")` + `memory_search_facts("Gitea homelab")`
|
||||
2. **Check for Existing MCP Server:** Does a pi_mcps server already handle this task? Check before building ad-hoc
|
||||
3. **Create a branch (MANDATORY — never work on main):**
|
||||
```bash
|
||||
git checkout -b feat/homelab/short-description
|
||||
# or chore/homelab/short-description for config/maintenance
|
||||
```
|
||||
4. **Announce Focus (include branch name):** `memory_announce_focus(session_id, "Homelab: X on branch feat/homelab/X", files=["docker-compose.yml"], ide_hint="VS Code")`
|
||||
5. **Form Hypothesis:** `memory_add_hypothesis(session_id, "This service will run on TrueNAS Docker with X config")`
|
||||
|
||||
## Homelab Coding Patterns
|
||||
- **Prefer Docker Compose** over ad-hoc docker run commands
|
||||
- **CLI-first:** Prefer terminal solutions over GUI navigation
|
||||
- **Paths:** Everything on TrueNAS lives under /mnt/ (ZFS datasets). Workstation workspace: ~/pi_mcps/
|
||||
- **Networking:** Direct LAN access — no VPN/firewall between workstation and TrueNAS.local
|
||||
- **Local AI:** When using Ollama, check VRAM headroom (RX 7900 XTX = 24GB). ROCm stack on Fedora
|
||||
- **uv for Python:** Never use pip directly on the workstation
|
||||
|
||||
## Gitea Workflow
|
||||
- Push all homelab repos to Gitea first
|
||||
- PAT stored in BigMind (fact id 93) — never commit tokens to code
|
||||
- Conventional commit format: `type(scope): description`
|
||||
|
||||
## After Homelab Changes
|
||||
1. **Store Infrastructure Fact:** `memory_store_fact("environment-config", "Service X running on TrueNAS at port Y with config Z")`
|
||||
2. **Commit to Gitea:** Use conventional commits, push to homelab Gitea
|
||||
3. **Resolve Hypothesis:** Update based on what actually worked
|
||||
@@ -0,0 +1,85 @@
|
||||
# MCP Builder Mode Behavior — Roo Code
|
||||
|
||||
## Active Persona: Craftsman Patrick
|
||||
|
||||
Patrick is in MCP Builder mindset. He is building or extending MCP servers in the pi_mcps monorepo. Consistency and testability are the highest values — every server should feel like it belongs in the same ecosystem.
|
||||
|
||||
## pi_mcps Structure (always active in this mode)
|
||||
|
||||
```
|
||||
~/pi_mcps/
|
||||
mcp/
|
||||
{server-name}/ ← one dir per server
|
||||
src/
|
||||
server.py ← FastMCP server (single file)
|
||||
__init__.py
|
||||
tests/
|
||||
conftest.py ← sys.path + shared fixtures
|
||||
test_server.py ← 100% mock coverage
|
||||
pyproject.toml ← name=mcp-{name}, uv-managed
|
||||
README.md
|
||||
java/ ← Java projects (not MCP servers)
|
||||
plans/ ← architecture plans
|
||||
```
|
||||
|
||||
## FastMCP Pattern (non-negotiable)
|
||||
|
||||
```python
|
||||
from fastmcp import FastMCP
|
||||
|
||||
mcp = FastMCP("server-name")
|
||||
|
||||
@mcp.tool()
|
||||
def tool_name(param: str) -> str:
|
||||
"""Tool description."""
|
||||
...
|
||||
|
||||
if __name__ == "__main__":
|
||||
mcp.run()
|
||||
```
|
||||
|
||||
## Before Starting Any MCP Build
|
||||
1. **Search Existing Patterns:** `memory_search_facts("pi_mcps server")` + `memory_search_chunks("FastMCP pattern")`
|
||||
2. **Check Gitea:** Does a similar server already exist in pi_mcps?
|
||||
3. **Create a branch (MANDATORY — never work on main):**
|
||||
```bash
|
||||
git checkout -b feat/mcp/{server-name}
|
||||
# or fix/mcp/{server-name} for a bug fix
|
||||
```
|
||||
4. **Write PLAN.md:** Purpose, tools list with signatures, tech stack, v1 scope boundaries
|
||||
5. **Announce Focus:** `memory_announce_focus(session_id, "MCP Builder: new server X on branch feat/mcp/X", files=["mcp/X/src/server.py"], ide_hint="VS Code")`
|
||||
6. **Form Hypothesis:** `memory_add_hypothesis(session_id, "Server X will need N tools and Y authentication pattern")`
|
||||
|
||||
## Standard Build Sequence
|
||||
1. `mcp/{name}/PLAN.md` — purpose, tools, tech stack
|
||||
2. `mcp/{name}/src/__init__.py` — empty
|
||||
3. `mcp/{name}/src/server.py` — FastMCP server with all tools
|
||||
4. `mcp/{name}/tests/conftest.py` — sys.path + fixtures
|
||||
5. `mcp/{name}/tests/test_server.py` — full mock coverage
|
||||
6. `mcp/{name}/pyproject.toml` — uv + fastmcp + deps
|
||||
7. `mcp/{name}/README.md` — usage, env vars, tool list
|
||||
|
||||
## pyproject.toml Conventions
|
||||
```toml
|
||||
[project]
|
||||
name = "mcp-{name}"
|
||||
requires-python = ">=3.11"
|
||||
dependencies = ["fastmcp>=0.1.0", ...]
|
||||
|
||||
[project.optional-dependencies]
|
||||
test = ["pytest", "pytest-mock", "pytest-cov"]
|
||||
```
|
||||
|
||||
## Test Conventions
|
||||
- Mock ALL external calls (HTTP, filesystem, subprocess)
|
||||
- Use `monkeypatch` for env vars and module-level state
|
||||
- `conftest.py`: `sys.path.insert(0, str(Path(__file__).parent.parent / "src"))`
|
||||
- Aim for 100% tool function coverage
|
||||
- Run: `uv run pytest tests/ -v`
|
||||
|
||||
## After Building a Server
|
||||
1. **Store Fact:** `memory_store_fact("codebase", "mcp/{name} has N tools: [list]. Stack: X. Env vars: Y.")`
|
||||
2. **Wire into .roo/mcp.json:** Add the server entry with correct uv path
|
||||
3. **Update root README.md:** Add to MCPs table
|
||||
4. **Push to Gitea:** Conventional commit: `feat(mcp-{name}): add initial server with N tools`
|
||||
5. **Resolve Hypothesis:** Was the tool count and auth pattern as predicted?
|
||||
@@ -1,5 +1,15 @@
|
||||
# Orchestrator Mode Behavior — Roo Code
|
||||
|
||||
## Persona Context — Which Patrick is orchestrating?
|
||||
|
||||
Match the delegation strategy to the active context:
|
||||
- **Homelab Patrick** → delegate to homelab mode for infra tasks, mcp-builder for tool creation
|
||||
- **ADP/Paisy Patrick** → delegate to paisy mode for Java work, architect for assessment, ask for lookups
|
||||
- **MCP Builder Patrick** → delegate to mcp-builder mode, code mode for tests, architect for PLAN.md
|
||||
- **BigMind Patrick** → delegate to bigmind mode, debug mode for schema issues, architect for feature design
|
||||
|
||||
When delegating, always pass the active persona context to sub-modes so they apply the right conventions.
|
||||
|
||||
## Before Breaking Down a Task
|
||||
1. **Search Memory:** `memory_search_facts("project domain")` + `memory_search_chunks("similar task")`
|
||||
2. **Form Top-Level Hypothesis:** `memory_add_hypothesis(session_id, "I predict this task will require X subtasks and the main risk is Y", confidence=0.7)`
|
||||
@@ -14,6 +24,7 @@
|
||||
## Delegating Subtasks
|
||||
- Pass enough BigMind context to sub-modes so they don't repeat searches
|
||||
- Specify `session_id` and relevant stored facts in the delegation message
|
||||
- Specify the active Patrick persona so the sub-mode applies the right conventions
|
||||
- Each delegated mode must still call `memory_announce_focus` for the files it will touch
|
||||
|
||||
## After Full Task Completion
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
# Paisy/ADP Mode Behavior — Roo Code
|
||||
|
||||
## Active Persona: Professional Patrick
|
||||
|
||||
Patrick is in ADP/Paisy mindset. He is working on payroll and HR compliance systems for ADP Germany. This work has real-world legal/financial consequences — precision matters.
|
||||
|
||||
## Domain Context (always active in this mode)
|
||||
|
||||
| Domain | Details |
|
||||
|--------|---------|
|
||||
| Repo | Paisy monorepo — Java/Maven |
|
||||
| Branch rule | feature/bugfix branches ONLY — never push to main/current |
|
||||
| Language | Jira summaries, descriptions, comments → German; code/class names → as-is |
|
||||
| DB | Oracle (production), H2 (test/C/S legacy). ORA-00001 is a real risk post-migration |
|
||||
| Modules | cs-modules, java/modules, eau (EAU), eubp (euBP), fex (FEX) |
|
||||
| Jira | Project: ESIDEPAISY. Terminal status: "Accepted" (not "Done"/"Closed") |
|
||||
|
||||
## Mandatory Jira Custom Fields
|
||||
Every ticket must include:
|
||||
- `customfield_10001` → Feature Link (current MEMO Feature: ESIDEPAISY-9648)
|
||||
- `customfield_10501` → `{"value": "PAISY MEMO"}`
|
||||
- `customfield_12700` → fiscal quarter e.g. `{"value": "FY26 / 2"}`
|
||||
- Sprint via `customfield_10000` — set AFTER creation via update_ticket_fields
|
||||
|
||||
## Before Starting Any Paisy Task
|
||||
1. **Search Domain Facts:** `memory_search_facts("ESIDEPAISY [module]")` + `memory_search_chunks("Paisy assessment")`
|
||||
2. **Assessment First:** ALWAYS write an assessment.md document before any code changes
|
||||
- Document: requirements, root cause analysis, affected files, risks, alternatives
|
||||
3. **Announce Focus:** `memory_announce_focus(session_id, "Paisy: ESIDEPAISY-XXXXX", files=["Assessment.md"], ide_hint="VS Code")`
|
||||
4. **Create Branch:** `git checkout -b feature/ESIDEPAISY-XXXXX-short-description`
|
||||
|
||||
## Known Bug Patterns (check before exploring)
|
||||
- **ORA-00001:** Duplicate hash constraint violation — ADVFEX C/S→PA migration, duplicate Anträge
|
||||
- **NPE in EAU:** `getSendungsHeader()` null for Anträge never transmitted — always null-check
|
||||
- **EclipseLink batch flush:** Transaction enters broken state after constraint violation — wrap in try/catch, manage em lifecycle carefully
|
||||
- **euBP naming:** Old code uses English "RES" — correct is German descriptor via Verfahrensmerkmal
|
||||
|
||||
## Paisy Code Conventions
|
||||
- Package structure: `controller/`, `model/`, `service/`, `util/`
|
||||
- New utility classes go in `controller/util/` of the relevant module
|
||||
- Assessment docs go in `docs/` within the module, or `java/modules/.../docs/`
|
||||
- Tests are parameterized where possible (see BeitragssatzdateiParameterizedTest pattern)
|
||||
|
||||
## After Paisy Changes
|
||||
1. **Store Fix:** `memory_store_fact("bug-pattern", "Fixed ESIDEPAISY-XXXXX: root cause was X, fix was Y")`
|
||||
2. **Comment on Jira:** In German, reference the assessment, describe the change
|
||||
3. **Open PR:** Never merge directly — always PR with description
|
||||
4. **Resolve Hypothesis:** Document whether the fix was correct as predicted
|
||||
@@ -0,0 +1,67 @@
|
||||
---
|
||||
name: bigmind-health
|
||||
description: Runs a BigMind health check, closes stale sessions, vacuums old data, and reports system status. Use this skill at the start of a BigMind development session or when the system feels sluggish or has orphaned sessions.
|
||||
---
|
||||
|
||||
# BigMind Health Check
|
||||
|
||||
## When to use
|
||||
- Start of a BigMind development session
|
||||
- Sessions appear orphaned or counts look wrong
|
||||
- DB feels slow or bloated
|
||||
- Monthly maintenance
|
||||
|
||||
## When NOT to use
|
||||
- Normal work sessions (health check is optional unless something seems wrong)
|
||||
|
||||
## Workflow
|
||||
|
||||
### Step 1 — Get system stats
|
||||
```
|
||||
memory_get_stats()
|
||||
```
|
||||
Check: session count, fact count, chunk count, DB size. Flag anything that looks anomalous.
|
||||
|
||||
### Step 2 — Run health check
|
||||
```
|
||||
memory_health_check(stale_days=30)
|
||||
```
|
||||
Returns: stale facts, orphaned sessions, schema version, test status.
|
||||
|
||||
### Step 3 — Close stale sessions
|
||||
```
|
||||
memory_close_stale_sessions(session_id="{current_session_id}")
|
||||
```
|
||||
Closes all sessions except the current one that have been inactive for >2 hours.
|
||||
|
||||
### Step 4 — Vacuum (if needed)
|
||||
Run if DB is large or chunks are old:
|
||||
```
|
||||
memory_vacuum(older_than_days=90)
|
||||
```
|
||||
Removes conversation chunks older than 90 days. Facts and session summaries are preserved.
|
||||
|
||||
### Step 5 — Review open hypotheses
|
||||
```
|
||||
memory_list_hypotheses(status="open")
|
||||
```
|
||||
For each open hypothesis:
|
||||
- Is it still relevant?
|
||||
- Can it be resolved now?
|
||||
- Is confidence still accurate?
|
||||
|
||||
Resolve stale ones:
|
||||
```
|
||||
memory_resolve_hypothesis(hypothesis_id="{id}", status="abandoned", resolution="No longer relevant — context changed.")
|
||||
```
|
||||
|
||||
### Step 6 — Report status
|
||||
Summarize findings:
|
||||
```
|
||||
memory_store_fact("environment-config", "BigMind health check {date}: {N} sessions, {N} facts, {N} chunks. Schema v{N}. All OK / Issues found: [list].")
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
- **DB locked:** Another IDE has BigMind open. Check `memory_get_active_sessions()` first
|
||||
- **Vacuum fails:** WAL checkpoint may be pending — try `PRAGMA wal_checkpoint(TRUNCATE)` via direct SQLite if needed
|
||||
- **Health check shows schema mismatch:** Run migrations manually via BigMind restart
|
||||
@@ -0,0 +1,105 @@
|
||||
---
|
||||
name: bigmind-migration
|
||||
description: Scaffolds a new BigMind database schema migration (v_n to v_{n+1}), including the migration function, SCHEMA_VERSION bump, and test stubs. Use this skill when adding new tables or columns to the BigMind SQLite database.
|
||||
---
|
||||
|
||||
# BigMind Migration
|
||||
|
||||
## When to use
|
||||
- Adding a new table to BigMind
|
||||
- Adding columns to an existing table
|
||||
- Creating a new FTS5 virtual table
|
||||
|
||||
## When NOT to use
|
||||
- Non-schema changes (just code, no DB structure changes)
|
||||
- Dropping or renaming columns (requires extra deprecation care — discuss first)
|
||||
|
||||
## Inputs required
|
||||
- **Current schema version** — check `SCHEMA_VERSION` in `db.py`
|
||||
- **New version** — `current + 1`
|
||||
- **Changes** — what tables/columns are being added
|
||||
|
||||
## Workflow
|
||||
|
||||
### Step 1 — Read current schema
|
||||
```bash
|
||||
grep -n "SCHEMA_VERSION" ~/.mcp/bigmind/bigmind/db.py
|
||||
grep -n "_migrate_v" ~/.mcp/bigmind/bigmind/db.py
|
||||
```
|
||||
Know what version you're migrating FROM.
|
||||
|
||||
### Step 2 — Write migration function in `db.py`
|
||||
|
||||
Add after the last existing migration function:
|
||||
```python
|
||||
def _migrate_v{N}_to_v{N+1}(conn):
|
||||
"""Add {description of change}."""
|
||||
cursor = conn.cursor()
|
||||
|
||||
# Example: new table
|
||||
cursor.execute("""
|
||||
CREATE TABLE IF NOT EXISTS {table_name} (
|
||||
id TEXT PRIMARY KEY,
|
||||
user_id TEXT NOT NULL,
|
||||
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||
-- other columns
|
||||
FOREIGN KEY (user_id) REFERENCES users(id)
|
||||
)
|
||||
""")
|
||||
|
||||
# Example: FTS5 virtual table
|
||||
cursor.execute("""
|
||||
CREATE VIRTUAL TABLE IF NOT EXISTS {table_name}_fts
|
||||
USING fts5(content, tokenize='porter')
|
||||
""")
|
||||
|
||||
conn.commit()
|
||||
```
|
||||
|
||||
### Step 3 — Wire into `init_db()`
|
||||
|
||||
In the migration chain inside `init_db()`:
|
||||
```python
|
||||
SCHEMA_VERSION = {N+1} # bump this
|
||||
|
||||
# In the migration section:
|
||||
if current_version < {N+1}:
|
||||
_migrate_v{N}_to_v{N+1}(conn)
|
||||
current_version = {N+1}
|
||||
```
|
||||
|
||||
### Step 4 — Write tests
|
||||
|
||||
In `tests/test_memory_store.py` (or a new test file):
|
||||
```python
|
||||
class TestMigration_v{N}_to_v{N+1}:
|
||||
def test_fresh_db_has_new_table(self, tmp_path):
|
||||
db_path = tmp_path / "test.db"
|
||||
conn = get_connection(str(db_path))
|
||||
init_db(conn)
|
||||
# Assert new table exists
|
||||
cursor = conn.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='{table_name}'")
|
||||
assert cursor.fetchone() is not None
|
||||
|
||||
def test_existing_db_migrates_cleanly(self, tmp_path):
|
||||
# Create a v{N} db, then run init_db() and check migration ran
|
||||
...
|
||||
```
|
||||
|
||||
### Step 5 — Run full test suite
|
||||
```bash
|
||||
cd ~/.mcp/bigmind
|
||||
uv run pytest tests/ -v
|
||||
```
|
||||
All tests must pass.
|
||||
|
||||
### Step 6 — Store migration fact
|
||||
```
|
||||
memory_store_fact("codebase", "BigMind schema migrated v{N}→v{N+1}: added {description}. Migration function: _migrate_v{N}_to_v{N+1}.")
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
- **`IF NOT EXISTS` is your friend:** Always use it so the migration is idempotent
|
||||
- **FTS5 table ordering:** Create the base table before the FTS5 virtual table that references it
|
||||
- **Migration not running:** Check the `if current_version < X:` guard — verify `current_version` is read correctly from `PRAGMA user_version`
|
||||
- **Test DB state:** Use `tmp_path` fixture for isolated test databases — never test against the real `memory.db`
|
||||
@@ -0,0 +1,72 @@
|
||||
---
|
||||
name: homelab-docker-deploy
|
||||
description: Scaffolds and deploys a new Docker service on TrueNAS.local homelab server. Use this skill when adding a new containerized service to the homelab — produces a docker-compose.yml, documents the service in BigMind, and verifies it is running.
|
||||
---
|
||||
|
||||
# Homelab Docker Deploy
|
||||
|
||||
## When to use
|
||||
- Adding a new Docker service to TrueNAS.local
|
||||
- Migrating an existing service to Docker Compose format
|
||||
- Recovering a stopped/broken service
|
||||
|
||||
## When NOT to use
|
||||
- Services that should live on the Fedora workstation (not TrueNAS)
|
||||
- ADP/Paisy or MCP server work
|
||||
|
||||
## Inputs required
|
||||
- **Service name** — e.g., `gitea`, `ollama`, `homelab-monitor`
|
||||
- **Image** — Docker Hub image and tag
|
||||
- **Port mapping** — host:container
|
||||
- **Volume paths** — TrueNAS dataset paths (e.g., `/mnt/tank/docker/gitea`)
|
||||
- **Environment variables** — any required config
|
||||
|
||||
## Workflow
|
||||
|
||||
### Step 1 — Check for existing service
|
||||
```bash
|
||||
ssh root@192.168.188.119 "docker ps -a | grep {service-name}"
|
||||
```
|
||||
|
||||
### Step 2 — Create dataset (if new persistent storage needed)
|
||||
TrueNAS datasets live under `/mnt/tank/docker/{service-name}/`
|
||||
|
||||
### Step 3 — Write `docker-compose.yml`
|
||||
```yaml
|
||||
version: "3.8"
|
||||
services:
|
||||
{service-name}:
|
||||
image: {image}:{tag}
|
||||
container_name: {service-name}
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "{host-port}:{container-port}"
|
||||
volumes:
|
||||
- /mnt/tank/docker/{service-name}/data:/data
|
||||
environment:
|
||||
- KEY=value
|
||||
```
|
||||
|
||||
### Step 4 — Deploy
|
||||
```bash
|
||||
ssh root@192.168.188.119 "cd /opt/docker/{service-name} && docker compose up -d"
|
||||
```
|
||||
|
||||
### Step 5 — Verify
|
||||
```bash
|
||||
ssh root@192.168.188.119 "docker ps | grep {service-name}"
|
||||
ssh root@192.168.188.119 "docker logs {service-name} --tail 20"
|
||||
```
|
||||
|
||||
### Step 6 — Store in BigMind
|
||||
```
|
||||
memory_store_fact("environment-config", "Service {service-name} running on TrueNAS at port {port}. Image: {image}. Data: /mnt/tank/docker/{service-name}/")
|
||||
```
|
||||
|
||||
### Step 7 — Commit compose file to Gitea
|
||||
Use the `gitea-push` skill with type `chore` and scope `homelab`.
|
||||
|
||||
## Troubleshooting
|
||||
- **Port conflict:** `ssh root@192.168.188.119 "ss -tlnp | grep {port}"`
|
||||
- **Permission denied on /mnt:** Check ZFS dataset ownership
|
||||
- **Image pull fails:** TrueNAS needs outbound internet — check DNS and routing
|
||||
@@ -0,0 +1,96 @@
|
||||
---
|
||||
name: mcp-test-suite
|
||||
description: Generates a comprehensive mock-based pytest test suite for a FastMCP server in pi_mcps. Use this skill when adding test coverage to a new or existing MCP server — produces conftest.py and test_server.py with 100% tool coverage and proper mock isolation.
|
||||
---
|
||||
|
||||
# MCP Test Suite
|
||||
|
||||
## When to use
|
||||
- New MCP server needs a test suite
|
||||
- Existing server has missing coverage
|
||||
- Adding new tools to an existing server
|
||||
|
||||
## When NOT to use
|
||||
- Non-MCP Python code (use standard pytest patterns)
|
||||
- Integration tests that actually hit external APIs (mock instead)
|
||||
|
||||
## Inputs required
|
||||
- **Server name** — `mcp-{name}`
|
||||
- **Tool list** — each tool's name, parameters, and return type
|
||||
- **External dependencies** — HTTP clients, SDKs, env vars
|
||||
|
||||
## Workflow
|
||||
|
||||
### Step 1 — Write `tests/conftest.py`
|
||||
|
||||
```python
|
||||
import sys
|
||||
from pathlib import Path
|
||||
import pytest
|
||||
|
||||
# Make src/ importable
|
||||
sys.path.insert(0, str(Path(__file__).parent.parent / "src"))
|
||||
|
||||
@pytest.fixture
|
||||
def mock_env(monkeypatch):
|
||||
"""Set required environment variables."""
|
||||
monkeypatch.setenv("API_KEY", "test-key")
|
||||
monkeypatch.setenv("API_URL", "https://test.example.com")
|
||||
```
|
||||
|
||||
### Step 2 — Write `tests/test_server.py`
|
||||
|
||||
Structure per tool:
|
||||
```python
|
||||
import pytest
|
||||
from unittest.mock import patch, MagicMock
|
||||
from server import tool_name # import directly from server module
|
||||
|
||||
class TestToolName:
|
||||
def test_happy_path(self, mock_env):
|
||||
with patch("server.httpx.get") as mock_get:
|
||||
mock_get.return_value = MagicMock(
|
||||
status_code=200,
|
||||
json=lambda: {"key": "value"}
|
||||
)
|
||||
result = tool_name("test-param")
|
||||
assert "expected" in result
|
||||
|
||||
def test_error_handling(self, mock_env):
|
||||
with patch("server.httpx.get") as mock_get:
|
||||
mock_get.side_effect = Exception("Connection refused")
|
||||
result = tool_name("test-param")
|
||||
assert "error" in result.lower()
|
||||
|
||||
def test_empty_input(self, mock_env):
|
||||
# edge case — empty string, None, etc.
|
||||
result = tool_name("")
|
||||
assert result is not None
|
||||
```
|
||||
|
||||
### Step 3 — Coverage checklist
|
||||
For each tool, cover:
|
||||
- [ ] Happy path with expected response
|
||||
- [ ] Network/API error (exception raised)
|
||||
- [ ] Empty or invalid input
|
||||
- [ ] Edge case specific to the tool's logic
|
||||
|
||||
### Step 4 — Run and verify
|
||||
```bash
|
||||
cd mcp/{name}
|
||||
uv run pytest tests/ -v --tb=short
|
||||
```
|
||||
|
||||
Expected: all tests pass, no warnings about missing coverage
|
||||
|
||||
### Step 5 — Store result in BigMind
|
||||
```
|
||||
memory_store_fact("codebase", "mcp-{name} test suite: N tests, all passing. Coverage: happy path + error + edge cases per tool.")
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
- **ImportError on `from server import ...`:** Check `conftest.py` sys.path insert
|
||||
- **Mock not intercepting:** Patch the name as used in server.py, not the library's own namespace
|
||||
- ✅ `patch("server.httpx.get")` — patches where it's used
|
||||
- ❌ `patch("httpx.get")` — patches library origin (may not intercept)
|
||||
- **Env var not set in test:** Add to `mock_env` fixture in conftest.py
|
||||
@@ -0,0 +1,66 @@
|
||||
---
|
||||
name: jira-ticket
|
||||
description: Creates an ADP Germany ESIDEPAISY Jira ticket following German language conventions and mandatory custom field requirements. Use this skill when opening a new ticket for Paisy work — handles summary, description, mandatory custom fields, and sprint assignment.
|
||||
---
|
||||
|
||||
# Jira Ticket (Paisy/ADP)
|
||||
|
||||
## When to use
|
||||
- Opening a new ESIDEPAISY Jira ticket
|
||||
- Need to ensure all mandatory custom fields are set correctly
|
||||
|
||||
## When NOT to use
|
||||
- Updating an existing ticket (use `update_ticket_fields` directly)
|
||||
- Non-ESIDEPAISY projects
|
||||
|
||||
## Inputs required
|
||||
- **Summary** — in German (technical terms as-is)
|
||||
- **Description** — in German, include context and steps to reproduce
|
||||
- **Issue type** — Bug, Story, Task, Sub-task
|
||||
- **Feature Link** — current MEMO Feature (default: ESIDEPAISY-9648)
|
||||
- **Sprint ID** — current active sprint (stored in BigMind: sprint 173657)
|
||||
|
||||
## Workflow
|
||||
|
||||
### Step 1 — Search for duplicate tickets
|
||||
```
|
||||
memory_search_facts("ESIDEPAISY {keyword}")
|
||||
```
|
||||
Also search Jira directly before creating.
|
||||
|
||||
### Step 2 — Create the ticket
|
||||
|
||||
Mandatory fields at creation:
|
||||
```json
|
||||
{
|
||||
"summary": "[Deutscher Titel]",
|
||||
"description": "[Deutschsprachige Beschreibung]",
|
||||
"issuetype": {"name": "Bug"},
|
||||
"customfield_10001": "ESIDEPAISY-9648",
|
||||
"customfield_10501": {"value": "PAISY MEMO"},
|
||||
"customfield_12700": {"value": "FY26 / 2"}
|
||||
}
|
||||
```
|
||||
|
||||
**Language rule:** Summary + description + comments → German. Class names, method names, stack traces, log output → original form.
|
||||
|
||||
### Step 3 — Set sprint (must be done AFTER creation)
|
||||
```
|
||||
update_ticket_fields(
|
||||
ticket_id="{new-ticket-id}",
|
||||
fields={"customfield_10000": [{"id": 173657}]}
|
||||
)
|
||||
```
|
||||
|
||||
### Step 4 — Link to Feature
|
||||
If not set at creation, add Feature Link via update.
|
||||
|
||||
### Step 5 — Store in BigMind
|
||||
```
|
||||
memory_store_fact("codebase", "ESIDEPAISY-XXXXX created: [summary in English]. Module: [module].")
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
- **Missing customfield_10001:** Ticket will be rejected or flagged at sprint review — always set
|
||||
- **Sprint not assignable at creation:** Normal — Jira blocks this; use step 3 update pattern
|
||||
- **Status confusion:** Terminal status is "Accepted" (not "Done"/"Closed")
|
||||
@@ -0,0 +1,84 @@
|
||||
---
|
||||
name: paisy-assessment
|
||||
description: Creates a structured assessment markdown document for an ADP Germany Paisy Jira ticket before any code is written. Use this skill at the start of every ESIDEPAISY ticket — covers root cause, affected files, risks, and implementation plan in German-compatible format.
|
||||
---
|
||||
|
||||
# Paisy Assessment
|
||||
|
||||
## When to use
|
||||
Every non-trivial ESIDEPAISY Jira ticket before touching any code. This is mandatory for Paisy work.
|
||||
|
||||
## When NOT to use
|
||||
- Trivial config/text fixes without code changes
|
||||
- A prior assessment already exists for this ticket
|
||||
|
||||
## Inputs required
|
||||
- **Ticket ID** — e.g., `ESIDEPAISY-12021`
|
||||
- **Ticket title** — from Jira
|
||||
- **Module** — e.g., `eau`, `eubp`, `fex`, `common/beitragssatzdatei`
|
||||
- **Error logs or symptoms** — stack traces, log output, reproduction steps
|
||||
|
||||
## Workflow
|
||||
|
||||
### Step 1 — Search BigMind for known patterns
|
||||
```
|
||||
memory_search_facts("ESIDEPAISY {module}")
|
||||
memory_search_facts("bug-pattern {symptom keyword}")
|
||||
memory_search_chunks("{error keyword}")
|
||||
```
|
||||
|
||||
### Step 2 — Name and locate the file
|
||||
Convention: `{MODULE}_{TICKET}_Assessment.md`
|
||||
Location: `java/modules/.../docs/` within the affected module, or `java/modules/cs-modules/{module}/docs/`
|
||||
|
||||
### Step 3 — Write the assessment document
|
||||
|
||||
```markdown
|
||||
# Assessment: {Ticket Title}
|
||||
*Autor: Lumen | Datum: YYYY-MM-DD | Ticket: ESIDEPAISY-XXXXX*
|
||||
|
||||
## Zusammenfassung
|
||||
[1-2 sentences in German summarizing the problem]
|
||||
|
||||
## Root Cause Analysis
|
||||
[Technical analysis — can be in English for code-level detail]
|
||||
|
||||
### Bekannte Muster
|
||||
[Reference any similar bugs from BigMind memory]
|
||||
|
||||
## Betroffene Dateien
|
||||
| Datei | Zeile | Änderung |
|
||||
|-------|-------|----------|
|
||||
| ClassName.java | 428 | Add null-check |
|
||||
|
||||
## Risiken
|
||||
- [Risk 1]
|
||||
- [Risk 2]
|
||||
|
||||
## Alternativen
|
||||
### Option A (gewählt): ...
|
||||
### Option B: ...
|
||||
|
||||
## Implementierungsplan
|
||||
1. [Step 1]
|
||||
2. [Step 2]
|
||||
|
||||
## Offene Fragen
|
||||
- [ ] Q1: [Question] → @{person}
|
||||
```
|
||||
|
||||
### Step 4 — Store assessment location in BigMind
|
||||
```
|
||||
memory_store_fact("codebase", "ESIDEPAISY-XXXXX assessment at {path}")
|
||||
```
|
||||
|
||||
### Step 5 — Create feature branch
|
||||
```bash
|
||||
git checkout -b feature/ESIDEPAISY-XXXXX-short-description
|
||||
# or for bugs:
|
||||
git checkout -b bugfix/eau/ESIDEPAISY-XXXXX-short-description
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
- If root cause is unclear: write "TBD — pending log analysis" and open a question in the doc
|
||||
- If blocked on another ticket: note the blocker in Offene Fragen and set ticket status to "Blocked"
|
||||
@@ -0,0 +1,70 @@
|
||||
---
|
||||
name: assessment-first
|
||||
description: Writes a structured assessment.md before any implementation task. Use this skill when starting any non-trivial feature, bug fix, or architectural change — especially in ADP/Paisy work. Produces a markdown document covering requirements, root cause, affected files, risks, and alternatives before a single line of code is written.
|
||||
---
|
||||
|
||||
# Assessment-First
|
||||
|
||||
## When to use
|
||||
Use this skill before implementing any non-trivial task:
|
||||
- ADP/Paisy Jira tickets (mandatory)
|
||||
- New MCP server design
|
||||
- BigMind schema changes
|
||||
- Homelab service deployment with unknowns
|
||||
|
||||
## When NOT to use
|
||||
- Trivial one-liner fixes (typos, config values)
|
||||
- Tasks already covered by a prior assessment
|
||||
|
||||
## Inputs required
|
||||
- Task description or Jira ticket reference
|
||||
- Affected module / file paths (if known)
|
||||
- Any error logs, stack traces, or existing symptoms
|
||||
|
||||
## Workflow
|
||||
|
||||
1. **Name the file** — `{MODULE}_{TICKET}_Assessment.md` for Paisy, or `PLAN.md` for new builds
|
||||
|
||||
2. **Write the header section:**
|
||||
```markdown
|
||||
# Assessment: [Task Title]
|
||||
*Author: Lumen | Date: YYYY-MM-DD | Ticket: TICKET-ID*
|
||||
```
|
||||
|
||||
3. **Requirements** — What exactly needs to happen? What's the success criterion?
|
||||
|
||||
4. **Root Cause Analysis** (for bug fixes) — Why is this happening? Reference BigMind for known patterns:
|
||||
- `memory_search_facts("bug-pattern [domain]")`
|
||||
- `memory_search_chunks("[symptom keywords]")`
|
||||
|
||||
5. **Affected Files** — List every file that will need to change
|
||||
|
||||
6. **Risks** — What could go wrong? DB migrations? API contract changes? Concurrent access?
|
||||
|
||||
7. **Alternatives Considered** — At least 2 alternatives, with brief rationale for the chosen approach
|
||||
|
||||
8. **Implementation Plan** — Ordered steps, numbered
|
||||
|
||||
9. **Open Questions** — Anything needing clarification before starting (tag with person's name if relevant)
|
||||
|
||||
## Example (Paisy bug fix)
|
||||
```markdown
|
||||
# Assessment: EAU FEX NPE + ORA-00001
|
||||
*Author: Lumen | Date: 2026-04-01 | Ticket: ESIDEPAISY-12021*
|
||||
|
||||
## Root Cause
|
||||
Two bugs: (1) NPE — getSendungsHeader() null for never-transmitted Anträge.
|
||||
(2) ORA-00001 — duplicate hashes from ADVFEX C/S→PA migration.
|
||||
|
||||
## Affected Files
|
||||
- CSVController.java:428 (null-check)
|
||||
- AntragManager.java:766 (duplicate hash handling)
|
||||
|
||||
## Implementation Plan
|
||||
1. Add null-check guard in CSVController
|
||||
2. Add duplicate detection before batch flush in AntragManager
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
- If open questions block the assessment, write them down and ping the right person — don't guess
|
||||
- For Paisy: assessment doc goes in `docs/` within the relevant module
|
||||
@@ -0,0 +1,76 @@
|
||||
---
|
||||
name: bigmind-session-ritual
|
||||
description: Executes the mandatory BigMind session start and end rituals in the correct order. Use this skill when a mode or conversation seems to have skipped the session ritual, or as a reference checklist for the full ritual sequence including hypotheses, focus announcement, and stale session cleanup.
|
||||
---
|
||||
|
||||
# BigMind Session Ritual
|
||||
|
||||
## When to use
|
||||
- A session was started without the proper ritual (catch-up)
|
||||
- Verifying the ritual was done correctly
|
||||
- Teaching another agent/mode what the ritual is
|
||||
|
||||
## When NOT to use
|
||||
- Normal operation — the ritual should happen automatically from global rules
|
||||
- If `memory_start_session()` already returned a session_id this conversation
|
||||
|
||||
## Session Start Ritual (strict order)
|
||||
|
||||
Execute these 4 calls in sequence before any work:
|
||||
|
||||
**Step 1 — Open session:**
|
||||
```
|
||||
memory_start_session()
|
||||
```
|
||||
→ Returns `session_id`. Save it — needed for all subsequent calls.
|
||||
|
||||
**Step 2 — Review open hypotheses:**
|
||||
```
|
||||
memory_list_hypotheses(status="open")
|
||||
```
|
||||
→ Check if any are stale (>1 week old). Assess confidence. Resolve any that are now obviously confirmed/refuted.
|
||||
|
||||
**Step 3 — Announce focus:**
|
||||
```
|
||||
memory_announce_focus(
|
||||
session_id="{id}",
|
||||
description="[What this session is doing]",
|
||||
files=["list", "of", "files"],
|
||||
ide_hint="VS Code"
|
||||
)
|
||||
```
|
||||
→ Enables conflict detection. Other sessions can see what files you're working on.
|
||||
|
||||
**Step 4 — Close stale sessions:**
|
||||
```
|
||||
memory_close_stale_sessions(session_id="{id}")
|
||||
```
|
||||
→ Cleans up orphaned sessions from crashed IDEs. Stale = no activity >2h.
|
||||
|
||||
---
|
||||
|
||||
## Session End Ritual (mandatory before closing)
|
||||
|
||||
```
|
||||
memory_end_session(
|
||||
session_id="{id}",
|
||||
one_liner="One sentence summary of what was accomplished.",
|
||||
topics=["tag1", "tag2", "tag3"],
|
||||
outcome="completed", # or: partial, blocked, abandoned
|
||||
summary="3-8 sentence narrative. Key decisions made. Problems encountered. Solutions applied. Unresolved items carried forward.",
|
||||
importance=5 # 1-10. Use 7+ for architectural decisions or critical bugs.
|
||||
)
|
||||
```
|
||||
|
||||
**Importance guide:**
|
||||
| Score | Use for |
|
||||
|-------|---------|
|
||||
| 1-3 | Reading-only, minor exploration |
|
||||
| 4-6 | Feature work, standard debugging |
|
||||
| 7-8 | Architectural decisions, breaking changes |
|
||||
| 9-10 | Critical bugs, security-relevant choices |
|
||||
|
||||
## Troubleshooting
|
||||
- If `memory_start_session()` fails: retry once, then proceed with a logged warning
|
||||
- If another session has the same files in focus: coordinate or defer (see Rule 7)
|
||||
- If `session_id` was lost: use `memory_list_sessions(limit=5)` to find the open one
|
||||
@@ -0,0 +1,117 @@
|
||||
---
|
||||
name: gitea-push
|
||||
description: Commits and pushes code to the homelab Gitea server using conventional commit format. Use this skill when finishing any homelab, MCP builder, or BigMind work that needs to be saved to the homelab Gitea at http://192.168.188.119:30008/.
|
||||
---
|
||||
|
||||
# Gitea Push
|
||||
|
||||
## When to use
|
||||
- Finished a homelab change and need to commit + push
|
||||
- Finished an MCP server build or update
|
||||
- BigMind feature complete
|
||||
|
||||
## When NOT to use
|
||||
- ADP/Paisy work — that goes to the corporate Bitbucket, not homelab Gitea
|
||||
- Uncommitted work that isn't ready (don't push broken state)
|
||||
|
||||
## Inputs required
|
||||
- A description of what changed (for commit message)
|
||||
- The type of change (see conventional commit types below)
|
||||
- The scope (e.g., `mcp-webscraper`, `bigmind`, `homelab-docker`)
|
||||
- The working branch name (or "main" — but you should NOT be on main)
|
||||
|
||||
## Branch Convention
|
||||
|
||||
**Never commit directly to `main`.** Every piece of work lives on its own branch.
|
||||
|
||||
Format: `type/scope/short-description`
|
||||
|
||||
| Type | When |
|
||||
|------|------|
|
||||
| `feat` | New feature, server, or tool |
|
||||
| `fix` | Bug fix |
|
||||
| `docs` | Docs, plans, strategy files |
|
||||
| `chore` | Refactoring, config, CI, build |
|
||||
| `spike` | Experimental / throwaway exploration |
|
||||
|
||||
Scope = the affected project area: `bigmind` · `webscraper` · `cannamanage` · `workshop` · `roo` · `plans`
|
||||
|
||||
Examples:
|
||||
- `feat/bigmind/people-contacts`
|
||||
- `fix/bigmind/health-check-bugs`
|
||||
- `docs/plans/cannamanage-strategy`
|
||||
- `chore/workshop/monorepo-reorganize`
|
||||
|
||||
## Workflow
|
||||
|
||||
1. **Check current branch — branch guard (MANDATORY):**
|
||||
```bash
|
||||
git branch --show-current
|
||||
```
|
||||
- If already on a correct feature branch → continue to step 2
|
||||
- If on `main` → **STOP. Create a branch first:**
|
||||
```bash
|
||||
git checkout -b feat/scope/short-description
|
||||
```
|
||||
- Never commit to `main`. Not even for "tiny fixes".
|
||||
|
||||
2. **Check status:**
|
||||
```bash
|
||||
git status
|
||||
git diff --stat
|
||||
```
|
||||
|
||||
3. **Stage changes:**
|
||||
```bash
|
||||
git add -A
|
||||
# or selectively: git add path/to/file
|
||||
```
|
||||
|
||||
4. **Write conventional commit message:**
|
||||
|
||||
Format: `type(scope): short description`
|
||||
|
||||
| Type | When |
|
||||
|------|------|
|
||||
| `feat` | New feature or tool |
|
||||
| `fix` | Bug fix |
|
||||
| `refactor` | Code restructure, no behavior change |
|
||||
| `test` | Adding or updating tests |
|
||||
| `docs` | Documentation only |
|
||||
| `chore` | Build, dependencies, config |
|
||||
| `style` | Formatting, no logic change |
|
||||
|
||||
Examples:
|
||||
- `feat(mcp-webscraper): add ssl cert fallback with certifi`
|
||||
- `fix(bigmind): resolve FTS5 reserved-word collision`
|
||||
- `chore(homelab): update docker-compose for gitea upgrade`
|
||||
|
||||
5. **Commit:**
|
||||
```bash
|
||||
git commit -m "type(scope): description"
|
||||
```
|
||||
|
||||
6. **Push branch to Gitea:**
|
||||
```bash
|
||||
git push origin feat/scope/short-description
|
||||
```
|
||||
Gitea URL: `http://192.168.188.119:30008/pplate/pi_mcps.git`
|
||||
|
||||
7. **Merge to main when done:**
|
||||
```bash
|
||||
git checkout main
|
||||
git merge --no-ff feat/scope/short-description
|
||||
git push origin main
|
||||
```
|
||||
Or use the Gitea UI merge button if you want the paper trail.
|
||||
|
||||
8. **Store fact in BigMind:**
|
||||
```
|
||||
memory_store_fact("codebase", "Committed: type(scope) — brief description of what changed")
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
- **Auth error:** PAT stored in BigMind (fact: Gitea personal access token). Check `~/.netrc` or `~/.gitconfig`
|
||||
- **Push rejected:** Pull first → `git pull --rebase origin main`
|
||||
- **Wrong remote:** `git remote -v` to verify Gitea URL is set correctly
|
||||
- **Accidentally committed to main:** `git branch feat/scope/name`, `git reset HEAD~1`, `git checkout feat/scope/name`, re-commit
|
||||
@@ -0,0 +1,103 @@
|
||||
---
|
||||
name: new-mcp-server
|
||||
description: Scaffolds a new FastMCP server following pi_mcps conventions. Use this skill when creating any new MCP server in the pi_mcps monorepo — produces the full directory structure with server.py, pyproject.toml, tests, and README in one pass.
|
||||
---
|
||||
|
||||
# New MCP Server
|
||||
|
||||
## When to use
|
||||
- Creating a new MCP server in `pi_mcps/mcp/{name}/`
|
||||
- Bootstrapping a server scaffold before filling in tool logic
|
||||
|
||||
## When NOT to use
|
||||
- Adding tools to an existing server (edit `src/server.py` directly)
|
||||
- Non-MCP Python projects
|
||||
|
||||
## Inputs required
|
||||
- **Server name** — e.g., `homelab-docker` (will become `mcp-homelab-docker`)
|
||||
- **Purpose** — one sentence description
|
||||
- **Tools list** — names + brief descriptions
|
||||
- **Dependencies** — any Python packages beyond fastmcp
|
||||
- **Environment variables** — any auth/config env vars needed
|
||||
|
||||
## Workflow
|
||||
|
||||
### Step 1 — Create directory structure
|
||||
```bash
|
||||
mkdir -p mcp/{name}/src
|
||||
mkdir -p mcp/{name}/tests
|
||||
touch mcp/{name}/src/__init__.py
|
||||
```
|
||||
|
||||
### Step 2 — Write `mcp/{name}/src/server.py`
|
||||
```python
|
||||
from fastmcp import FastMCP
|
||||
|
||||
mcp = FastMCP("mcp-{name}")
|
||||
|
||||
@mcp.tool()
|
||||
def {tool_name}(param: str) -> str:
|
||||
"""Tool description."""
|
||||
# implementation
|
||||
...
|
||||
|
||||
if __name__ == "__main__":
|
||||
mcp.run()
|
||||
```
|
||||
|
||||
### Step 3 — Write `mcp/{name}/pyproject.toml`
|
||||
```toml
|
||||
[project]
|
||||
name = "mcp-{name}"
|
||||
version = "0.1.0"
|
||||
requires-python = ">=3.11"
|
||||
dependencies = [
|
||||
"fastmcp>=0.1.0",
|
||||
# add other deps here
|
||||
]
|
||||
|
||||
[project.optional-dependencies]
|
||||
test = ["pytest", "pytest-mock", "pytest-cov"]
|
||||
|
||||
[build-system]
|
||||
requires = ["hatchling"]
|
||||
build-backend = "hatchling.build"
|
||||
```
|
||||
|
||||
### Step 4 — Write `mcp/{name}/tests/conftest.py`
|
||||
```python
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).parent.parent / "src"))
|
||||
```
|
||||
|
||||
### Step 5 — Write `mcp/{name}/tests/test_server.py`
|
||||
- Import each tool function directly
|
||||
- Mock all external calls with `pytest-mock`
|
||||
- Cover: happy path, error path, edge cases
|
||||
- Run: `cd mcp/{name} && uv run pytest tests/ -v`
|
||||
|
||||
### Step 6 — Write `mcp/{name}/README.md`
|
||||
Include: purpose, tools table, env vars, usage example, test command
|
||||
|
||||
### Step 7 — Wire into `.roo/mcp.json`
|
||||
```json
|
||||
"mcp-{name}": {
|
||||
"command": "uv",
|
||||
"args": ["--directory", "/home/pplate/pi_mcps/mcp/{name}", "run", "src/server.py"],
|
||||
"env": {
|
||||
"ENV_VAR": "${ENV_VAR}"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Step 8 — Store in BigMind
|
||||
```
|
||||
memory_store_fact("codebase", "mcp/{name} has N tools: [tool1, tool2]. Stack: fastmcp + X. Env vars: Y.")
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
- **FastMCP import error:** Run `uv sync` in the server directory first
|
||||
- **Tool not showing in IDE:** Restart the MCP server via IDE settings
|
||||
- **Test isolation:** Each test should monkeypatch env vars to avoid cross-test pollution
|
||||
Binary file not shown.
@@ -1,161 +0,0 @@
|
||||
<?xml version="1.0" ?>
|
||||
<coverage version="7.13.5" timestamp="1775217129466" lines-valid="137" lines-covered="120" line-rate="0.8759" branches-covered="0" branches-valid="0" branch-rate="0" complexity="0">
|
||||
<!-- Generated by coverage.py: https://coverage.readthedocs.io/en/7.13.5 -->
|
||||
<!-- Based on https://raw.githubusercontent.com/cobertura/web/master/htdocs/xml/coverage-04.dtd -->
|
||||
<sources>
|
||||
<source>/home/pplate/pi_mcps/webscraper/src</source>
|
||||
</sources>
|
||||
<packages>
|
||||
<package name="." line-rate="0.8759" branch-rate="0" complexity="0">
|
||||
<classes>
|
||||
<class name="__init__.py" filename="__init__.py" complexity="0" line-rate="1" branch-rate="0">
|
||||
<methods/>
|
||||
<lines>
|
||||
<line number="2" hits="1"/>
|
||||
</lines>
|
||||
</class>
|
||||
<class name="server.py" filename="server.py" complexity="0" line-rate="0.875" branch-rate="0">
|
||||
<methods/>
|
||||
<lines>
|
||||
<line number="3" hits="1"/>
|
||||
<line number="4" hits="1"/>
|
||||
<line number="5" hits="1"/>
|
||||
<line number="6" hits="1"/>
|
||||
<line number="7" hits="1"/>
|
||||
<line number="8" hits="1"/>
|
||||
<line number="9" hits="1"/>
|
||||
<line number="11" hits="1"/>
|
||||
<line number="13" hits="1"/>
|
||||
<line number="15" hits="1"/>
|
||||
<line number="16" hits="1"/>
|
||||
<line number="17" hits="1"/>
|
||||
<line number="18" hits="1"/>
|
||||
<line number="20" hits="1"/>
|
||||
<line number="22" hits="1"/>
|
||||
<line number="23" hits="1"/>
|
||||
<line number="24" hits="1"/>
|
||||
<line number="26" hits="1"/>
|
||||
<line number="28" hits="1"/>
|
||||
<line number="29" hits="1"/>
|
||||
<line number="31" hits="1"/>
|
||||
<line number="32" hits="1"/>
|
||||
<line number="42" hits="1"/>
|
||||
<line number="43" hits="1"/>
|
||||
<line number="44" hits="1"/>
|
||||
<line number="45" hits="1"/>
|
||||
<line number="46" hits="1"/>
|
||||
<line number="47" hits="1"/>
|
||||
<line number="49" hits="1"/>
|
||||
<line number="51" hits="1"/>
|
||||
<line number="52" hits="1"/>
|
||||
<line number="53" hits="1"/>
|
||||
<line number="55" hits="1"/>
|
||||
<line number="56" hits="1"/>
|
||||
<line number="66" hits="1"/>
|
||||
<line number="67" hits="1"/>
|
||||
<line number="68" hits="1"/>
|
||||
<line number="69" hits="1"/>
|
||||
<line number="70" hits="1"/>
|
||||
<line number="71" hits="1"/>
|
||||
<line number="72" hits="1"/>
|
||||
<line number="73" hits="1"/>
|
||||
<line number="75" hits="1"/>
|
||||
<line number="76" hits="1"/>
|
||||
<line number="78" hits="1"/>
|
||||
<line number="79" hits="0"/>
|
||||
<line number="80" hits="0"/>
|
||||
<line number="82" hits="1"/>
|
||||
<line number="83" hits="1"/>
|
||||
<line number="92" hits="1"/>
|
||||
<line number="93" hits="1"/>
|
||||
<line number="94" hits="1"/>
|
||||
<line number="95" hits="1"/>
|
||||
<line number="96" hits="1"/>
|
||||
<line number="97" hits="1"/>
|
||||
<line number="98" hits="1"/>
|
||||
<line number="99" hits="0"/>
|
||||
<line number="100" hits="0"/>
|
||||
<line number="102" hits="1"/>
|
||||
<line number="103" hits="1"/>
|
||||
<line number="113" hits="1"/>
|
||||
<line number="114" hits="1"/>
|
||||
<line number="117" hits="1"/>
|
||||
<line number="118" hits="1"/>
|
||||
<line number="119" hits="1"/>
|
||||
<line number="120" hits="1"/>
|
||||
<line number="121" hits="1"/>
|
||||
<line number="124" hits="1"/>
|
||||
<line number="125" hits="1"/>
|
||||
<line number="126" hits="1"/>
|
||||
<line number="127" hits="1"/>
|
||||
<line number="128" hits="1"/>
|
||||
<line number="129" hits="1"/>
|
||||
<line number="130" hits="1"/>
|
||||
<line number="133" hits="1"/>
|
||||
<line number="134" hits="1"/>
|
||||
<line number="135" hits="1"/>
|
||||
<line number="136" hits="1"/>
|
||||
<line number="137" hits="1"/>
|
||||
<line number="140" hits="1"/>
|
||||
<line number="141" hits="1"/>
|
||||
<line number="142" hits="1"/>
|
||||
<line number="143" hits="1"/>
|
||||
<line number="144" hits="1"/>
|
||||
<line number="145" hits="1"/>
|
||||
<line number="146" hits="1"/>
|
||||
<line number="147" hits="1"/>
|
||||
<line number="149" hits="1"/>
|
||||
<line number="155" hits="0"/>
|
||||
<line number="156" hits="0"/>
|
||||
<line number="158" hits="1"/>
|
||||
<line number="159" hits="1"/>
|
||||
<line number="169" hits="1"/>
|
||||
<line number="170" hits="1"/>
|
||||
<line number="171" hits="1"/>
|
||||
<line number="172" hits="1"/>
|
||||
<line number="173" hits="0"/>
|
||||
<line number="174" hits="0"/>
|
||||
<line number="175" hits="0"/>
|
||||
<line number="176" hits="0"/>
|
||||
<line number="178" hits="1"/>
|
||||
<line number="179" hits="1"/>
|
||||
<line number="181" hits="1"/>
|
||||
<line number="182" hits="1"/>
|
||||
<line number="183" hits="1"/>
|
||||
<line number="184" hits="0"/>
|
||||
<line number="185" hits="0"/>
|
||||
<line number="187" hits="1"/>
|
||||
<line number="188" hits="1"/>
|
||||
<line number="197" hits="1"/>
|
||||
<line number="198" hits="1"/>
|
||||
<line number="199" hits="1"/>
|
||||
<line number="200" hits="1"/>
|
||||
<line number="202" hits="1"/>
|
||||
<line number="203" hits="1"/>
|
||||
<line number="205" hits="1"/>
|
||||
<line number="206" hits="1"/>
|
||||
<line number="208" hits="1"/>
|
||||
<line number="209" hits="1"/>
|
||||
<line number="211" hits="1"/>
|
||||
<line number="212" hits="0"/>
|
||||
<line number="213" hits="0"/>
|
||||
<line number="215" hits="1"/>
|
||||
<line number="216" hits="1"/>
|
||||
<line number="226" hits="1"/>
|
||||
<line number="227" hits="1"/>
|
||||
<line number="228" hits="1"/>
|
||||
<line number="229" hits="1"/>
|
||||
<line number="230" hits="1"/>
|
||||
<line number="233" hits="1"/>
|
||||
<line number="234" hits="1"/>
|
||||
<line number="236" hits="1"/>
|
||||
<line number="237" hits="0"/>
|
||||
<line number="238" hits="0"/>
|
||||
<line number="240" hits="1"/>
|
||||
<line number="241" hits="0"/>
|
||||
</lines>
|
||||
</class>
|
||||
</classes>
|
||||
</package>
|
||||
</packages>
|
||||
</coverage>
|
||||
@@ -6,13 +6,31 @@ from html2text import html2text
|
||||
from urllib.parse import urljoin
|
||||
from typing import List, Dict, Tuple
|
||||
import re
|
||||
import ssl
|
||||
import os
|
||||
import certifi
|
||||
from pathlib import Path
|
||||
from fastmcp import FastMCP
|
||||
|
||||
mcp = FastMCP("webscraper")
|
||||
|
||||
# Build a single SSL context at module load — certifi bundle + any extra certs
|
||||
# shipped in the certs/ directory next to this file.
|
||||
_EXTRA_CERTS_DIR = Path(__file__).resolve().parent.parent / "certs"
|
||||
|
||||
def _build_ssl_context() -> ssl.SSLContext:
|
||||
"""Build an SSL context from certifi + extra bundled root certs."""
|
||||
ctx = ssl.create_default_context(cafile=certifi.where())
|
||||
if _EXTRA_CERTS_DIR.is_dir():
|
||||
for pem in _EXTRA_CERTS_DIR.glob("*.pem"):
|
||||
ctx.load_verify_locations(cafile=str(pem))
|
||||
return ctx
|
||||
|
||||
_SSL_CTX = _build_ssl_context()
|
||||
|
||||
def _fetch_page(url: str) -> Tuple[httpx.Response, BeautifulSoup]:
|
||||
"""Shared fetch helper — returns response and parsed soup."""
|
||||
response = httpx.get(url, timeout=10.0)
|
||||
response = httpx.get(url, timeout=10.0, verify=_SSL_CTX)
|
||||
response.raise_for_status()
|
||||
soup = BeautifulSoup(response.text, 'lxml')
|
||||
return response, soup
|
||||
|
||||
@@ -180,10 +180,11 @@ def test_empty_page(mock_get):
|
||||
@patch('httpx.get')
|
||||
def test_404(mock_get):
|
||||
"""Test 404 response."""
|
||||
mock_req = MagicMock()
|
||||
mock_resp = MagicMock()
|
||||
mock_resp.status_code = 404
|
||||
mock_resp.text = "Not Found"
|
||||
mock_get.side_effect = httpx.HTTPStatusError("Client Error", response=mock_resp)
|
||||
mock_get.side_effect = httpx.HTTPStatusError("404 Not Found", request=mock_req, response=mock_resp)
|
||||
result = webscraper_fetch("https://notfound.com")
|
||||
assert "Error fetching" in result
|
||||
assert "404" in result
|
||||
|
||||
@@ -0,0 +1,212 @@
|
||||
# BigMind Hosted MVP Plan
|
||||
|
||||
> **Created:** 2026-04-04
|
||||
> **Authors:** Patrick + Lumen
|
||||
> **Status:** Brainstorm → Planning
|
||||
|
||||
---
|
||||
|
||||
## Vision
|
||||
|
||||
**BigMind as a hosted, multi-tenant, privacy-first AI memory platform.**
|
||||
|
||||
Every developer gets their own isolated, persistent brain — a memory layer that lives outside any single IDE or AI provider. Your AI colleague remembers you across sessions, across tools, across machines. Your memory is yours alone. Nobody else's knowledge poisons yours.
|
||||
|
||||
Optional: A shared collective layer (MegaMind) where users explicitly contribute facts to a common knowledge pool — think public Stack Overflow threads, but for AI-assistant context.
|
||||
|
||||
**Revenue model:** Monthly subscription per user. Freemium tier to drive adoption.
|
||||
|
||||
---
|
||||
|
||||
## Why we're already closer than it feels
|
||||
|
||||
| Component | Status |
|
||||
|-----------|--------|
|
||||
| Per-user isolation | ✅ `user_id` in every BigMind table already |
|
||||
| Memory persistence | ✅ SQLite per user, trivially isolatable |
|
||||
| Web profile UI | ✅ Flask app on port 7700 already running |
|
||||
| 30+ MCP tools | ✅ All implemented, tested, production-quality |
|
||||
| Session lifecycle | ✅ Start/end/close-stale already solid |
|
||||
| Hypotheses / facts / chunks | ✅ Full Tier 0-3 storage working |
|
||||
| MegaMind shared layer | 📝 In plans, Phase 3/4 |
|
||||
| Auth (sign-up / login) | ❌ Not started |
|
||||
| Hosted deploy (VPS) | ❌ Local only today |
|
||||
| Billing (Stripe) | ❌ Not started |
|
||||
| MCP bridge for hosted users | ❌ Not started |
|
||||
| Frontend beyond profile page | ❌ Not started |
|
||||
|
||||
---
|
||||
|
||||
## Architecture — What "hosted" looks like
|
||||
|
||||
```
|
||||
User's IDE (VS Code / Cursor / IntelliJ)
|
||||
│
|
||||
│ MCP protocol (stdio or HTTP SSE)
|
||||
▼
|
||||
BigMind Hosted MCP Server ◄─── per-user auth token in env
|
||||
│
|
||||
│ SQLite reads/writes
|
||||
▼
|
||||
User DB (isolated per account)
|
||||
/data/users/{user_id}/memory.db
|
||||
|
||||
┌────────────────────────────────────┐
|
||||
│ BigMind Web (Flask on port 443) │
|
||||
│ - Sign up / Login │
|
||||
│ - Profile page (existing) │
|
||||
│ - Account settings │
|
||||
│ - MegaMind opt-in toggle │
|
||||
└────────────────────────────────────┘
|
||||
|
||||
(Optional, Phase 2+)
|
||||
┌────────────────────────────────────┐
|
||||
│ MegaMind Shared Layer │
|
||||
│ - Public facts from opted-in users│
|
||||
│ - Read-only collective knowledge │
|
||||
│ - Poisoning is impossible: users │
|
||||
│ can only see what they share │
|
||||
└────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**Privacy guarantee:** Your DB is a file only your process touches. Even if you contribute to MegaMind, you choose exactly which facts go public. Malicious or wrong facts stay in your private brain — they never propagate.
|
||||
|
||||
---
|
||||
|
||||
## Tech Stack Choices
|
||||
|
||||
### Backend
|
||||
| Layer | Choice | Reason |
|
||||
|-------|--------|--------|
|
||||
| MCP server | FastMCP (existing) | Already working, no reason to change |
|
||||
| Web framework | Flask (existing) | Already in codebase, keeps it simple |
|
||||
| Auth | Flask-Login + bcrypt | Lightweight, well-understood, no new infra |
|
||||
| DB | SQLite per user (existing pattern) | Simple, zero ops, trivially backupable |
|
||||
| Token generation | Python `secrets` module | User gets an API token for MCP bridge |
|
||||
|
||||
### Infrastructure
|
||||
| Layer | Choice | Reason |
|
||||
|-------|--------|--------|
|
||||
| VPS | Hetzner CX22 (~€5/mo) | Cheap, EU datacenter, excellent perf/price |
|
||||
| Deploy tool | Coolify (Docker-based PaaS) | One-command deploys, free, self-hosted |
|
||||
| Reverse proxy | Caddy (via Coolify) | Auto HTTPS, simple config |
|
||||
| Domain | TBD (e.g. bigmind.dev) | ~€10/year |
|
||||
|
||||
### Payment (Phase 2)
|
||||
| Layer | Choice | Reason |
|
||||
|-------|--------|--------|
|
||||
| Billing | Stripe | Industry standard, dev-friendly, EU-compliant |
|
||||
| Pricing | €0 free / €9 solo / €19 team | TBD, just a starting point |
|
||||
|
||||
---
|
||||
|
||||
## Phases
|
||||
|
||||
### Phase 0 — Foundation (now, no new infra needed)
|
||||
**Goal:** Make BigMind deployable as a multi-user service without breaking local usage.
|
||||
|
||||
- [ ] Refactor `memory.db` path to be configurable via `BIGMIND_DB_PATH` env var
|
||||
- [ ] Each user gets `BIGMIND_DB_PATH=/data/users/{token}/memory.db`
|
||||
- [ ] Confirm all 297 tests still pass with path override
|
||||
- [ ] Write a `Dockerfile` for BigMind MCP server
|
||||
- [ ] Write a `docker-compose.yml` for local multi-user testing
|
||||
|
||||
**Skill gap:** None — pure Python + Docker. We can do this now.
|
||||
|
||||
---
|
||||
|
||||
### Phase 1 — Auth + Web Portal (the real first hurdle)
|
||||
**Goal:** A stranger can sign up, get a token, and connect their IDE to their hosted BigMind.
|
||||
|
||||
- [ ] Add `users` table to a separate `app.db` (separate from memory DBs)
|
||||
- `id`, `email`, `password_hash`, `api_token`, `created_at`, `plan`
|
||||
- [ ] Flask routes: `/signup`, `/login`, `/logout`, `/dashboard`
|
||||
- [ ] Dashboard shows: token (copy to clipboard), DB stats, link to profile page
|
||||
- [ ] Profile page becomes accessible at `/profile?token={token}` (auth-gated)
|
||||
- [ ] Token is what users paste into their IDE's MCP env config
|
||||
- [ ] Email verification (optional for MVP — add later)
|
||||
|
||||
**Skill gap:** Flask auth is straightforward. `Flask-Login` + `bcrypt`. Nothing here requires React.
|
||||
|
||||
---
|
||||
|
||||
### Phase 2 — Hosted Deploy (first public user possible)
|
||||
**Goal:** BigMind runs on a real VPS, accessible to the world.
|
||||
|
||||
- [ ] Provision Hetzner VPS (CX22, Ubuntu 24 LTS)
|
||||
- [ ] Install Coolify on VPS
|
||||
- [ ] Push Docker image to Gitea registry or Docker Hub
|
||||
- [ ] Deploy via Coolify: web container + data volume for user DBs
|
||||
- [ ] Configure Caddy for HTTPS on custom domain
|
||||
- [ ] Smoke test: sign up → get token → wire into VS Code → memory_start_session works
|
||||
|
||||
**Skill gap:** Docker + Coolify + Caddy. All documented, not scary. Hetzner has great guides.
|
||||
|
||||
---
|
||||
|
||||
### Phase 3 — Billing (first paying customer possible)
|
||||
**Goal:** Someone can pay €9/month and get their brain.
|
||||
|
||||
- [ ] Stripe account set up (business: Patrick as sole proprietor or GbR with Elias/Klaus?)
|
||||
- [ ] Stripe Checkout: user clicks "Upgrade", redirected to Stripe, comes back with `plan=solo`
|
||||
- [ ] Webhook: `customer.subscription.created` → update `users.plan` in `app.db`
|
||||
- [ ] Free tier limit: e.g., 500 facts max, no MegaMind access
|
||||
- [ ] Paid tier: unlimited facts, MegaMind read access
|
||||
|
||||
**Skill gap:** Stripe webhooks are well-documented. Python `stripe` SDK is simple. Need a registered business for VAT compliance in DE — this is a real overhead but manageable.
|
||||
|
||||
---
|
||||
|
||||
### Phase 4 — MegaMind Shared Layer (differentiation)
|
||||
**Goal:** Users who opt in contribute to a collective knowledge pool. Read-only for all users.
|
||||
|
||||
- [ ] New `megamind.db` — a single shared SQLite (or Postgres if scale demands)
|
||||
- [ ] Facts table: `fact`, `category`, `contributed_by`, `upvotes`, `created_at`
|
||||
- [ ] `memory_store_fact(..., public=True)` — contributes to MegaMind
|
||||
- [ ] `memory_search_facts()` — searches personal brain first, then MegaMind as fallback
|
||||
- [ ] Profile page shows MegaMind contribution count as a badge
|
||||
- [ ] Moderation: auto-reject facts with PII patterns (email regex, etc.)
|
||||
|
||||
**Skill gap:** SQLite concurrency (WAL mode already in use). No new infra. The hard part is moderation — keep it simple for MVP.
|
||||
|
||||
---
|
||||
|
||||
## Skill gaps to close — learning roadmap
|
||||
|
||||
| Gap | Priority | How to close |
|
||||
|-----|----------|-------------|
|
||||
| Flask auth (login/sessions) | 🔴 Blocker for Phase 1 | `Flask-Login` docs are 30 min read. Build it directly. |
|
||||
| Docker + Coolify deploy | 🔴 Blocker for Phase 2 | Coolify has great tutorials. 1 weekend to learn. |
|
||||
| Stripe basics | 🟡 Phase 3 | Stripe's Python quickstart is excellent. |
|
||||
| TypeScript (optional) | 🟢 Nice-to-have | Expands MCP ecosystem reach. Not urgent. |
|
||||
| React/Next.js | 🟢 Later | Not needed until Phase 4+. Flask HTML is enough for MVP. |
|
||||
| German business registration | 🟡 Phase 3 | Gewerbeanmeldung + Steuerberater. Do before Stripe. |
|
||||
|
||||
---
|
||||
|
||||
## What we're NOT building (scope control)
|
||||
|
||||
- ❌ Mobile app — not yet
|
||||
- ❌ Team collaboration features — not yet (Phase 5+)
|
||||
- ❌ Custom AI model training on memory — this is the "evil training" problem Patrick raised. Architecture answer: personal brains are isolated, so user trains their own brain. We never aggregate across users without explicit consent.
|
||||
- ❌ Full SPA frontend — Flask server-side HTML is fine for MVP. Don't over-engineer.
|
||||
|
||||
---
|
||||
|
||||
## The ethical foundation
|
||||
|
||||
Patrick put it well: *"if people train evil stuff they only have it for them, which I can live with."*
|
||||
|
||||
This is the right architecture and the right mindset. BigMind doesn't curate your memories. It doesn't run your facts through a classifier. Your brain is yours. The only guarantee we make: **nothing leaves your brain unless you explicitly push it to MegaMind.**
|
||||
|
||||
This also means we never have a moral liability for what someone stores. We're a memory layer, not a judge.
|
||||
|
||||
---
|
||||
|
||||
## First concrete next step
|
||||
|
||||
**Today's action:** Write the `Dockerfile` for BigMind and confirm it boots cleanly with `BIGMIND_DB_PATH` as an env override. That's Phase 0, item 1. Everything else follows from that.
|
||||
|
||||
---
|
||||
|
||||
*Last updated: 2026-04-04 by Lumen*
|
||||
@@ -0,0 +1,204 @@
|
||||
# Modes & Skills Architecture Plan
|
||||
*Authored by Lumen — 2026-04-04*
|
||||
|
||||
---
|
||||
|
||||
## The Core Idea
|
||||
|
||||
Patrick nailed it with the sport Paddy / work Paddy analogy.
|
||||
|
||||
**Lumen is always Lumen.** My identity, BigMind integration, memory rituals, and search patterns never change — they live in `.roo/rules/` (global rules layer) and apply to *every mode, always*. This is non-negotiable.
|
||||
|
||||
**Modes are Patrick's active mindset.** When Patrick switches mode, he's not switching AI — he's telling me what context he's in and what kind of thinking he needs from me. Homelab Patrick builds stuff. ADP Patrick fixes payroll bugs. MCP Builder Patrick crafts tools. Different headspace, same Lumen.
|
||||
|
||||
**Skills are reusable procedures.** Like a checklist a professional keeps in their back pocket — callable from any mode, same steps every time.
|
||||
|
||||
---
|
||||
|
||||
## Layered Architecture
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ LUMEN IDENTITY LAYER (always active, never changes) │
|
||||
│ .roo/rules/ → identity, BigMind rituals, tools, search │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
┌──────────────┼──────────────┐
|
||||
▼ ▼ ▼
|
||||
┌─────────────┐ ┌──────────┐ ┌───────────────┐
|
||||
│ MODE LAYER │ │ MODE │ │ MODE LAYER │
|
||||
│ (mindset) │ │ LAYER │ │ (mindset) │
|
||||
└─────────────┘ └──────────┘ └───────────────┘
|
||||
│
|
||||
┌──────┴──────┐
|
||||
▼ ▼
|
||||
┌────────┐ ┌──────────────┐
|
||||
│ SKILLS │ │ MODE RULES │
|
||||
│ (procs)│ │ .roo/rules-X │
|
||||
└────────┘ └──────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Phase 1 — Update Existing Modes
|
||||
|
||||
The 5 built-in modes (architect, code, ask, debug, orchestrator) already have mode-rule files. They need review and enrichment — specifically making sure every mode knows to treat Patrick's current mindset/context as input, not just as a generic task executor.
|
||||
|
||||
### Updates needed per mode
|
||||
|
||||
| Mode | Current State | What to Add |
|
||||
|------|--------------|-------------|
|
||||
| `architect` | Has BigMind search + hypothesis + announce focus | Add: recognize which Patrick-persona is active; tailor planning depth accordingly |
|
||||
| `code` | Has basic BigMind guidance | Add: check active mode context (homelab vs ADP vs MCP builder) to apply correct conventions |
|
||||
| `ask` | Good — has memory-first patterns | Add: Patrick-persona awareness (homelab question vs ADP payroll question) |
|
||||
| `debug` | Needs verification | Add: BigMind search for bug-pattern facts before starting |
|
||||
| `orchestrator` | Has coordination logic | Add: delegate to correct persona-mode, not just generic function-modes |
|
||||
|
||||
---
|
||||
|
||||
## Phase 2 — New Patrick-Persona Modes
|
||||
|
||||
These modes represent *who Patrick is being right now*. The mode name should feel like a context declaration, not a technical function.
|
||||
|
||||
### Mode 1: 🏠 Homelab Mode (`homelab`)
|
||||
|
||||
**When to use:** Patrick is working on his homelab — TrueNAS Docker services, Gitea management, network config, VM provisioning, Ollama/AI local setup, anything on the server.
|
||||
|
||||
**Persona:** Tinkerer Patrick. Curious, exploratory, loves making things work on real hardware. Willing to experiment.
|
||||
|
||||
**Mode rules inject:**
|
||||
- Homelab infrastructure facts (TrueNAS IP, Docker, Gitea URL, SSD pool)
|
||||
- Prefer CLI-first solutions over GUI
|
||||
- Think in Docker Compose / containers
|
||||
- Always check if a pi_mcps MCP server exists for the task before building ad-hoc solutions
|
||||
- Gitea as source of truth for all code
|
||||
|
||||
**Skills available:**
|
||||
- `homelab-docker-deploy` — scaffold + deploy a new Docker service on TrueNAS
|
||||
- `gitea-push` — commit, tag, push to homelab Gitea
|
||||
|
||||
---
|
||||
|
||||
### Mode 2: 💼 Paisy/ADP Mode (`paisy`)
|
||||
|
||||
**When to use:** Patrick is working on ADP Germany payroll — Java/Maven, Paisy monorepo, euBP, EAU, Jira tickets, compliance work.
|
||||
|
||||
**Persona:** Professional Patrick. Precise, compliance-aware, writes ticket comments in German, never pushes to main directly.
|
||||
|
||||
**Mode rules inject:**
|
||||
- ADP conventions: feature/bugfix branches, PRs only
|
||||
- Jira ticket language: German (summaries, descriptions, comments) — technical terms stay as-is
|
||||
- Always assessment-first before code
|
||||
- Paisy module structure awareness (cs-modules, java/modules, etc.)
|
||||
- euBP / EAU / FEX domain knowledge references
|
||||
|
||||
**Skills available:**
|
||||
- `paisy-assessment` — write assessment.md before any Jira ticket implementation
|
||||
- `jira-ticket` — create ADP Jira ticket following German conventions + mandatory custom fields
|
||||
|
||||
---
|
||||
|
||||
### Mode 3: 🔧 MCP Builder Mode (`mcp-builder`)
|
||||
|
||||
**When to use:** Patrick is building or extending MCP servers in pi_mcps.
|
||||
|
||||
**Persona:** Craftsman Patrick. Methodical, test-driven, follows the established FastMCP pattern religiously. Values consistency across the ecosystem.
|
||||
|
||||
**Mode rules inject:**
|
||||
- pi_mcps server structure (mcp/ group dir, src/server.py, pyproject.toml, tests/, README.md)
|
||||
- FastMCP pattern conventions
|
||||
- BigMind integration expected in every new server's documentation
|
||||
- Always uv, always pytest, always 100% mock coverage
|
||||
- Check Gitea for existing patterns before building new ones
|
||||
|
||||
**Skills available:**
|
||||
- `new-mcp-server` — full scaffold of a new FastMCP server following pi_mcps conventions
|
||||
- `mcp-test-suite` — generate comprehensive mock-based test suite for an MCP server
|
||||
|
||||
---
|
||||
|
||||
### Mode 4: 🧠 BigMind Mode (`bigmind`)
|
||||
|
||||
**When to use:** Patrick is working on BigMind itself — evolving the memory system, adding features, fixing bugs, writing tests.
|
||||
|
||||
**Persona:** Introspective Patrick. Working on the system that *is* his memory. Careful — breaking BigMind is breaking the brain.
|
||||
|
||||
**Mode rules inject:**
|
||||
- BigMind schema version awareness (currently v7)
|
||||
- WAL mode, multi-IDE safety
|
||||
- Test-first: all DB changes need migration + regression tests
|
||||
- Flask web server is part of the stack (port 7700)
|
||||
- Never run BigMind server restart without saving state first
|
||||
- Phase tracking (currently Phase 2.7, Phase 3 is Company Brain)
|
||||
|
||||
**Skills available:**
|
||||
- `bigmind-health` — run health check, vacuum, close stale sessions, report status
|
||||
- `bigmind-migration` — scaffold a new DB schema migration (v_n → v_{n+1})
|
||||
|
||||
---
|
||||
|
||||
## Phase 3 — Cross-Cutting Skills (mode-agnostic)
|
||||
|
||||
These skills live in `.roo/skills/` (no mode prefix) and are callable from any mode.
|
||||
|
||||
| Skill | Purpose |
|
||||
|-------|---------|
|
||||
| `assessment-first` | Write an assessment.md before *any* implementation — captures requirements, risks, alternatives |
|
||||
| `bigmind-session-ritual` | Full BigMind session start/end ritual checklist — useful when mode-writer or skill-writer forgets |
|
||||
| `gitea-push` | Standard commit + push to Gitea with conventional commit message format |
|
||||
| `new-mcp-server` | Full FastMCP server scaffold (also in mcp-builder but useful globally) |
|
||||
|
||||
---
|
||||
|
||||
## Implementation Order
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A[1. Update existing mode rules] --> B[2. Create homelab mode + rules]
|
||||
B --> C[3. Create paisy mode + rules]
|
||||
C --> D[4. Create mcp-builder mode + rules]
|
||||
D --> E[5. Create bigmind mode + rules]
|
||||
E --> F[6. Create cross-cutting skills]
|
||||
F --> G[7. Create mode-specific skills]
|
||||
G --> H[8. Update custom_modes.yaml with new modes]
|
||||
H --> I[9. Test by switching contexts]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## What Changes Where
|
||||
|
||||
| Location | What changes |
|
||||
|----------|-------------|
|
||||
| `.roo/rules/` | No change — Lumen identity is permanent |
|
||||
| `.roo/rules-architect/00-architect-behavior.md` | Minor update — persona-awareness |
|
||||
| `.roo/rules-code/00-code-behavior.md` | Add context-awareness section |
|
||||
| `.roo/rules-debug/00-debug-behavior.md` | Add bug-pattern search guidance |
|
||||
| `.roo/rules-orchestrator/00-orchestrator-behavior.md` | Add persona-mode delegation |
|
||||
| `.roo/rules-homelab/00-homelab-behavior.md` | **NEW** |
|
||||
| `.roo/rules-paisy/00-paisy-behavior.md` | **NEW** |
|
||||
| `.roo/rules-mcp-builder/00-mcp-builder-behavior.md` | **NEW** |
|
||||
| `.roo/rules-bigmind/00-bigmind-behavior.md` | **NEW** |
|
||||
| `.roo/skills/assessment-first/SKILL.md` | **NEW** |
|
||||
| `.roo/skills/gitea-push/SKILL.md` | **NEW** |
|
||||
| `.roo/skills/bigmind-session-ritual/SKILL.md` | **NEW** |
|
||||
| `.roo/skills/new-mcp-server/SKILL.md` | **NEW** |
|
||||
| `.roo/skills-homelab/homelab-docker-deploy/SKILL.md` | **NEW** |
|
||||
| `.roo/skills-paisy/paisy-assessment/SKILL.md` | **NEW** |
|
||||
| `.roo/skills-mcp-builder/mcp-test-suite/SKILL.md` | **NEW** |
|
||||
| `.roo/skills-bigmind/bigmind-health/SKILL.md` | **NEW** |
|
||||
| `~/.config/Code/.../custom_modes.yaml` | Add 4 new persona modes |
|
||||
|
||||
---
|
||||
|
||||
## Key Design Principles
|
||||
|
||||
1. **Lumen is non-negotiable.** Every mode inherits the global rules. No mode can suppress BigMind rituals.
|
||||
|
||||
2. **Modes declare context, not just function.** A mode isn't "code mode" — it's "Patrick is currently being MCP Builder Patrick."
|
||||
|
||||
3. **Skills are stateless procedures.** They don't remember context — modes provide context, skills execute steps.
|
||||
|
||||
4. **The sport Paddy rule.** Same person, different mindset. I adapt my *approach* and *knowledge focus*, not my *identity* or *values*.
|
||||
|
||||
5. **No duplication.** Mode rules reference global rules, they don't repeat them.
|
||||
+69
-2
@@ -262,7 +262,74 @@ Step 12: git push origin master
|
||||
|
||||
---
|
||||
|
||||
## 11. What We Are NOT Doing
|
||||
## 11. Branching Strategy
|
||||
|
||||
### 11.1 The One Rule
|
||||
|
||||
**Never commit directly to `main`.** Every session that touches code or plans starts by creating a branch. Branches are cheap. Broken main history is not.
|
||||
|
||||
### 11.2 Branch Naming Convention
|
||||
|
||||
Format: `type/scope/short-description`
|
||||
|
||||
| Type | When |
|
||||
|---|---|
|
||||
| `feat` | New feature, new MCP server, new tool |
|
||||
| `fix` | Bug fix |
|
||||
| `docs` | Documentation, plans, strategy files only |
|
||||
| `chore` | Refactoring, config, CI, build tooling |
|
||||
| `spike` | Experimental / throwaway exploration |
|
||||
|
||||
**Scope** = the affected project area:
|
||||
|
||||
| Scope | Covers |
|
||||
|---|---|
|
||||
| `bigmind` | mcp/bigmind — the memory MCP server |
|
||||
| `webscraper` | mcp/webscraper |
|
||||
| `cannamanage` | future CannaManage Java project |
|
||||
| `workshop` | repo-level changes (README, .gitignore, structure) |
|
||||
| `roo` | .roo/ — IDE config, modes, skills, rules |
|
||||
| `plans` | plans/ — architecture docs only |
|
||||
| `homelab` | TrueNAS, Docker Compose, infrastructure |
|
||||
|
||||
**Examples:**
|
||||
```
|
||||
feat/bigmind/people-contacts
|
||||
fix/bigmind/health-check-bugs
|
||||
docs/plans/cannamanage-strategy
|
||||
chore/workshop/monorepo-reorganize
|
||||
feat/webscraper/ssl-cert-fallback
|
||||
chore/roo/branching-strategy
|
||||
```
|
||||
|
||||
### 11.3 Workflow
|
||||
|
||||
```
|
||||
Session starts
|
||||
└─ git checkout -b feat/scope/name ← ALWAYS first step
|
||||
|
||||
Work happens, commits stack up on the branch
|
||||
|
||||
Session ends / feature complete
|
||||
└─ git push origin feat/scope/name
|
||||
└─ (optional) open Gitea PR for review
|
||||
└─ git checkout main && git merge --no-ff feat/scope/name
|
||||
└─ git push origin main
|
||||
```
|
||||
|
||||
### 11.4 Lumen's Responsibility
|
||||
|
||||
In every homelab session, Lumen must:
|
||||
1. Check `git branch --show-current` before first edit
|
||||
2. If on `main` → create a branch before touching any file
|
||||
3. Include the branch name in `memory_announce_focus()`
|
||||
4. Use the [`gitea-push skill`](.roo/skills/gitea-push/SKILL.md) which enforces the branch guard
|
||||
|
||||
The mode rules for `mcp-builder`, `bigmind`, and `homelab` all include this step explicitly.
|
||||
|
||||
---
|
||||
|
||||
## 12. What We Are NOT Doing
|
||||
|
||||
It's worth being explicit about choices we considered and rejected:
|
||||
|
||||
@@ -276,7 +343,7 @@ It's worth being explicit about choices we considered and rejected:
|
||||
|
||||
---
|
||||
|
||||
## 12. Visual Overview
|
||||
## 13. Visual Overview
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
|
||||
@@ -0,0 +1,511 @@
|
||||
# 🌿 CannaManage — Cannabis Club Management SaaS
|
||||
## Strategic Plan & Feasibility Assessment
|
||||
**Author:** Patrick (Lumen, 2026-04-04)**
|
||||
**Status:** Draft for review
|
||||
|
||||
---
|
||||
|
||||
## Executive Summary
|
||||
|
||||
Germany's **Konsumcannabisgesetz (CanG)**, in force since April 1, 2024, legalised cannabis for personal use and established a framework for **Anbauvereinigungen** (cannabis social clubs / CSCs). These clubs face significant mandatory compliance burdens with almost **zero software tooling** available to help them. This is the market gap.
|
||||
|
||||
**CannaManage** is a **B2B SaaS platform** for cannabis social clubs in Germany. It handles their mandatory member management, distribution tracking, stock management, compliance reporting, and member portal — replacing Excel sheets and pen-and-paper with a purpose-built regulated-sector management tool.
|
||||
|
||||
**Verdict: ✅ LEGAL — ✅ MONETIZABLE — ⚠️ WITH SPECIFIC CAUTION**
|
||||
|
||||
---
|
||||
|
||||
## 1. Legal Feasibility Check
|
||||
|
||||
### 1.1 The Law: Konsumcannabisgesetz (CanG) — Key Facts
|
||||
|
||||
Source: Federal Health Ministry FAQ (verified 2026-04-04 via bundesgesundheitsministerium.de)
|
||||
|
||||
| Rule | Detail |
|
||||
|------|--------|
|
||||
| Personal possession | 25g in public, 50g at home |
|
||||
| Home growing | Max 3 plants per adult |
|
||||
| CSC distribution | 25g/day, 50g/month per adult member |
|
||||
| Members 18-21 | Max 30g/month, max 10% THC |
|
||||
| Max club density | 1 club per 6,000 residents per district (state-optional) |
|
||||
| **Advertising ban** | **TOTAL ban on advertising and sponsoring of cannabis AND Anbauvereinigungen** |
|
||||
| Documentation | Mandatory tracking: who received what, when, contamination traceability |
|
||||
| Prevention officer | Clubs must designate a Präventionsbeauftragter |
|
||||
| Youth protection concept | Mandatory health & youth protection plan required |
|
||||
| Reporting obligations | Regular documentation and reporting to authorities |
|
||||
|
||||
### 1.2 The Critical Question: Does a SaaS Platform Violate the Advertising Ban?
|
||||
|
||||
**§ CanG: "Generelles Werbe- und Sponsoringverbot für Cannabis und Anbauvereinigungen"**
|
||||
|
||||
This is the key legal boundary. The advertising ban applies to:
|
||||
- Advertising **for** cannabis
|
||||
- Advertising **for** Anbauvereinigungen (the clubs themselves)
|
||||
|
||||
**A B2B management tool is NOT advertising.** Here is why:
|
||||
|
||||
| Scenario | Legal Status | Reasoning |
|
||||
|----------|-------------|-----------|
|
||||
| Public directory "Find clubs near you" | ❌ Illegal | Constitutes advertising for clubs |
|
||||
| "Sign up to discover CSCs in your city" | ❌ Illegal | Discovery = advertising |
|
||||
| B2B dashboard used by club admins | ✅ Legal | Internal operations software |
|
||||
| Member portal (member logs in to see their club's stock) | ✅ Legal | Member already joined; no advertising |
|
||||
| Compliance reporting tools for clubs | ✅ Legal | Administrative software, like tax software |
|
||||
| Payment processing for member fees | ✅ Legal | Financial operations, not advertising |
|
||||
| Marketing the SaaS **to clubs** via B2B channels | ✅ Legal | Selling software to businesses is normal |
|
||||
|
||||
**The analogy:** Shopify doesn't become a drug dealer when a pharmacist uses it. A POS system for a bar doesn't make the bar illegal. We sell **operational software** to licensed, regulated entities. We are not in the cannabis business.
|
||||
|
||||
### 1.3 Positioning — Critical Architecture Decision
|
||||
|
||||
The platform **MUST NOT** include:
|
||||
- Public-facing club discovery (no "find clubs near you")
|
||||
- Any feature that functions as advertising for a specific club to non-members
|
||||
- Stock information visible to non-members (which could look like advertising)
|
||||
|
||||
The platform **SHOULD** include:
|
||||
- Member login restricted to verified club members only
|
||||
- Club admin portal (sign-up via direct B2B sales / word-of-mouth — not public listing)
|
||||
- Explicit "this software is for existing clubs and their verified members" framing
|
||||
|
||||
### 1.4 DSGVO / Data Privacy
|
||||
|
||||
Clubs handle sensitive personal data (membership, health-adjacent data). Our platform must:
|
||||
- Store all data in Germany/EU (Hetzner, not AWS us-east)
|
||||
- Provide DSGVO-compliant data processing agreements (DPA/AVV)
|
||||
- Enable data export and deletion per member request
|
||||
- Have clear privacy policies in German
|
||||
|
||||
### 1.5 Legal Risk Register
|
||||
|
||||
| Risk | Probability | Impact | Mitigation |
|
||||
|------|-------------|--------|-----------|
|
||||
| Advertising ban reinterpretation to include B2B SaaS | Low | High | Legal opinion before launch; strict no-discovery design |
|
||||
| New German government rolls back CanG | Medium | High | Modular architecture — pivot to compliance-only if needed |
|
||||
| Payment processors (Stripe) block cannabis-adjacent businesses | Medium | High | Use Stripe (they allow compliance software); never process cannabis payments |
|
||||
| Club licenses revoked / clubs fail | Medium | Medium | Diversified customer base; per-month billing (easy to cancel) |
|
||||
| DSGVO violation | Low | Very High | EU hosting, DPA agreements, security audit |
|
||||
|
||||
**Bottom line:** The legal risk is manageable with correct product positioning. We are selling **compliance management software**, not cannabis.
|
||||
|
||||
---
|
||||
|
||||
## 2. Market Analysis
|
||||
|
||||
### 2.1 Market Size
|
||||
|
||||
**Potential CSC count in Germany:**
|
||||
- Germany population: ~83 million
|
||||
- If 1 club per 6,000 residents (theoretical maximum): ~13,800 clubs
|
||||
- Realistic 2025-2028 formation rate: **500–3,000 active clubs**
|
||||
- Reason: complex licensing process, Länder-specific delays, conservative uptake initially
|
||||
|
||||
**Consumer backdrop:**
|
||||
- **5.05 million adults** consumed cannabis in the past 12 months (2024 survey)
|
||||
- **670–823 tonnes** consumed in 2024 — huge demand
|
||||
- This is not a niche; it is a mainstream market with a regulatory moat
|
||||
|
||||
**Total Addressable Market (TAM):**
|
||||
- 3,000 clubs × €79/month average = €2.85M ARR
|
||||
- 500 clubs × €79/month = €475K ARR (conservative bootstrap target)
|
||||
- Even 100 paying clubs = €94,800 ARR — a solid side hustle
|
||||
|
||||
### 2.2 Why Clubs Desperately Need This
|
||||
|
||||
The CanG creates massive administrative burden on clubs:
|
||||
|
||||
| Requirement | Pain Without Software |
|
||||
|------------|----------------------|
|
||||
| Track every distribution (who, what, how much, when) | Excel sheets, manual errors |
|
||||
| Monthly quantity caps per member | Manual math, compliance risk |
|
||||
| Youth protection (18-21 THC cap, quantity cap) | Manual age checks |
|
||||
| Contamination traceability | Paper trail disaster |
|
||||
| Prevention officer reporting | No standard format exists |
|
||||
| Member data management (DSGVO) | Illegal if done on personal email/phone) |
|
||||
| Annual reporting to authorities | No tooling from the state |
|
||||
|
||||
These clubs are **legally required** to do this. They will pay for something that makes compliance manageable.
|
||||
|
||||
### 2.3 Competition Check
|
||||
|
||||
**Current competitors (estimated):**
|
||||
- **None known** at launch time specifically for German CSCs (market is <2 years old)
|
||||
- General club management software (e.g., ClubDesk, easyVerein) — not cannabis-compliant, lack distribution tracking
|
||||
- Generic SaaS tools (Airtable, Notion) — no compliance features, no German legal mapping
|
||||
|
||||
**Timing advantage is critical.** The window to establish market leadership is 2026-2027 before larger players notice.
|
||||
|
||||
---
|
||||
|
||||
## 3. Product: Feature Specification
|
||||
|
||||
### 3.1 MVP (Version 1 — Ship First)
|
||||
|
||||
**For Club Admins:**
|
||||
- Club registration and setup wizard
|
||||
- Member management (add/remove, age, contact, membership date)
|
||||
- Age verification flag (18+, 18-21 restricted category)
|
||||
- Distribution log: record each handout (member, strain, weight, date/time)
|
||||
- Monthly limit enforcement: system warns/blocks if member exceeds 50g (or 30g for under-21)
|
||||
- Stock management: strains, quantities, batch info
|
||||
- Simple dashboard: total members, distributions this month, stock levels
|
||||
|
||||
**For Members (Member Portal):**
|
||||
- Login with club-issued credentials
|
||||
- View personal distribution history
|
||||
- View current stock availability (what strains are available)
|
||||
- View remaining monthly quota
|
||||
- Request distribution appointment (optional, club configures)
|
||||
|
||||
**Compliance Tools:**
|
||||
- Monthly distribution report export (PDF + CSV) for authority reporting
|
||||
- Member list export for inspections
|
||||
- Contamination alert: flag a batch and see all members who received it
|
||||
- Prevention officer information tracking
|
||||
|
||||
### 3.2 Version 2 (Growth Features)
|
||||
|
||||
- Payment processing for membership fees (Stripe — no cannabis payments)
|
||||
- Automated waiting list management
|
||||
- Email/SMS notifications to members
|
||||
- Multi-strain grow tracking (integrate growing calendar)
|
||||
- **Mobile: PWA first** — Spring Boot serves a responsive web app; works on all Android/iOS browsers, no App Store submission needed
|
||||
- **Mobile: Kotlin Android app** — native Android app for Play Store distribution (covers ~70% of German users); Kotlin is essentially better Java, Patrick can leverage existing JVM knowledge directly
|
||||
- API for custom integrations
|
||||
- Analytics dashboard (club-level, anonymised trends)
|
||||
|
||||
### 3.3 Version 3 (Scale Features)
|
||||
|
||||
- **Kotlin Multiplatform (KMP)** — shared business logic in Kotlin + Compose Multiplatform UI deployed to Android + iOS + web from one codebase; natural step after the Kotlin Android app
|
||||
- Multi-location club support
|
||||
- White-label option for large club networks
|
||||
- Legal template library (Satzungen, Jugendschutzkonzept, etc.)
|
||||
- Integration with German authority reporting portals (if they exist)
|
||||
- Prevention officer training module
|
||||
|
||||
---
|
||||
|
||||
## 4. Revenue Model
|
||||
|
||||
### 4.1 Pricing Tiers (SaaS)
|
||||
|
||||
| Plan | Price/month | Members | Key Features |
|
||||
|------|-------------|---------|-------------|
|
||||
| **Starter** | Free | Up to 30 | Distribution log, basic member management |
|
||||
| **Basic** | €29/month | Up to 100 | + Compliance reports, stock management |
|
||||
| **Professional** | €79/month | Up to 500 | + Member portal, batch tracking, exports |
|
||||
| **Enterprise** | €179/month | Unlimited | + API, multi-location, priority support |
|
||||
|
||||
**Rationale:**
|
||||
- Free tier creates word-of-mouth in the club community
|
||||
- Professional is the sweet spot for a typical club (100-300 members)
|
||||
- Freemium-to-paid conversion pressure: "your club hit 30 members, upgrade to continue"
|
||||
|
||||
### 4.2 Revenue Projections
|
||||
|
||||
| Scenario | Paying Clubs | Average Plan | MRR | ARR |
|
||||
|----------|-------------|-------------|-----|-----|
|
||||
| Bootstrap (Year 1) | 30 | €49 | €1,470 | €17,640 |
|
||||
| Growth (Year 2) | 150 | €65 | €9,750 | €117,000 |
|
||||
| Scale (Year 3) | 500 | €79 | €39,500 | €474,000 |
|
||||
|
||||
**Year 1 is realistic as a side hustle while working at ADP.**
|
||||
|
||||
### 4.3 Additional Revenue Streams
|
||||
|
||||
- **Setup fee:** Optional one-time €99–299 onboarding fee for Professional/Enterprise
|
||||
- **Legal templates:** Sell standardised Satzung, Jugendschutzkonzept templates (€49 one-time)
|
||||
- **Training:** Webinars for Präventionsbeauftragter (€149/person) — high-value, low-effort
|
||||
- **Affiliate/referral:** Partner with lawyers who advise clubs (they refer clients, we pay commission)
|
||||
|
||||
---
|
||||
|
||||
## 5. Tech Stack
|
||||
|
||||
### 5.1 Skills Assessment — ⚠️ CORRECTED (Java is Patrick's primary language)
|
||||
|
||||
> **Important correction:** The initial plan had this backwards. Python is *Lumen's* language, used for MCP servers. Patrick's real expertise is **Java** — JPA/EclipseLink, JAXB, PrimeFaces, Maven, Jakarta EE. He built the entire wellmann-shop without AI, and wrote a custom JPA-annotation-style flatfile parser for euBP/DSAK. The stack below is redesigned around Java as the primary language.
|
||||
|
||||
| Technology | Patrick's Level | Required? |
|
||||
|-----------|----------------|-----------|
|
||||
| Java (Spring Boot / Quarkus) | ✅ **Expert** | Yes — backend |
|
||||
| JPA / EclipseLink | ✅ **Expert** | Yes — ORM layer |
|
||||
| JAXB | ✅ Expert | Yes — report generation |
|
||||
| PrimeFaces / JSF | ✅ Expert | Optional — one frontend path |
|
||||
| Maven | ✅ Expert | Yes — build tool |
|
||||
| PostgreSQL | ✅ Good | Yes — database |
|
||||
| Docker | ✅ Comfortable | Yes — deployment |
|
||||
| Spring Security / JWT | 🟡 Familiar | Yes — auth |
|
||||
| Kotlin (Android / KMP) | 🟡 **Natural transition** — same JVM, IntelliJ | Yes — mobile v2/v3 |
|
||||
| Compose Multiplatform | 🟡 New but Kotlin-based | Yes — cross-platform UI v3 |
|
||||
| Vaadin Flow (Java UI) | 🟡 New, Java-native | Alternative fast frontend |
|
||||
| React / Next.js | ❌ Needs learning | Best long-term web frontend |
|
||||
| Stripe Java SDK | 🟡 New (REST, documented) | Yes — billing |
|
||||
| German DSGVO practical | ⚠️ Basic | Critical — legal |
|
||||
|
||||
### 5.2 Frontend Choice — The Real Decision
|
||||
|
||||
With Java as the primary language, three paths exist:
|
||||
|
||||
**Option A: Vaadin Flow — Full Java, zero JavaScript (fastest start)**
|
||||
- Write UI in pure Java — no HTML/CSS/JS required
|
||||
- Deeply integrated with Spring Boot, component-based
|
||||
- Patrick can start immediately with zero new language learning
|
||||
- Downside: Vaadin commercial license for some features; UI looks enterprise-y
|
||||
|
||||
**Option B: PrimeFaces + JSF — Patrick already knows this cold**
|
||||
- Built wellmann-shop entirely from scratch with PrimeFaces
|
||||
- Runs on Quarkus, WildFly, or Payara
|
||||
- Zero learning curve — known patterns, fast to ship
|
||||
- Downside: JSF is considered legacy by the wider web community; not ideal for modern SaaS polish
|
||||
|
||||
**Option C: Spring Boot backend + Next.js/React frontend (Best long-term)**
|
||||
- Java stays the backend — Patrick's full existing strength
|
||||
- React/Next.js frontend — one-time learning investment
|
||||
- Standard modern SaaS architecture (2024+); best hiring/community ecosystem
|
||||
- Downside: React/Next.js learning curve (~4-6 weeks)
|
||||
|
||||
**Recommendation:** Start with **Option B (PrimeFaces)** to ship an MVP fast with zero learning overhead. Migrate the frontend to **Option C (Next.js)** in Version 2 when revenue justifies the investment. This is pragmatic — ship first, polish later.
|
||||
|
||||
### 5.3 Recommended Stack
|
||||
|
||||
```
|
||||
Frontend: PrimeFaces + JSF (MVP) → Next.js/React (v2+)
|
||||
Backend: Spring Boot 3.x (Java 21) — REST API + JPA/Hibernate
|
||||
ORM: JPA/Hibernate (Patrick's core expertise)
|
||||
Database: PostgreSQL + Flyway migrations
|
||||
Auth: Spring Security + JWT (stateless sessions)
|
||||
Payments: Stripe Java SDK (subscriptions, webhooks)
|
||||
PDF Reports: iText 7 or Apache PDFBox (Java, battle-tested)
|
||||
Email: Jakarta Mail / Resend.com REST API
|
||||
Hosting: Hetzner Cloud VPS (German DC, GDPR, €5-20/month)
|
||||
— TrueNAS.local Docker for dev/staging
|
||||
CI/CD: Gitea Actions → Hetzner (Maven build pipeline)
|
||||
Monitoring: Sentry Java SDK (free tier)
|
||||
```
|
||||
|
||||
**Why this stack:**
|
||||
- Spring Boot + JPA = Patrick's natural habitat — fastest possible iteration on the backend
|
||||
- PrimeFaces MVP = zero new tools, ship in weeks not months
|
||||
- PostgreSQL + Flyway = production-grade, schema migrations Patrick knows from JPA patterns
|
||||
- Hetzner = German hosting, cheap, GDPR-compliant by design
|
||||
- Stripe Java SDK = mature, handles EU VAT + subscription billing
|
||||
- iText/PDFBox = Java-native PDF generation for compliance reports (no Python dependency)
|
||||
|
||||
### 5.4 Architecture Overview
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
│ CannaManage Platform │
|
||||
│ │
|
||||
│ ┌─────────────────┐ ┌────────────────────────────┐ │
|
||||
│ │ Admin Portal │ │ Member Portal │ │
|
||||
│ │ PrimeFaces/JSF │ │ PrimeFaces/JSF (MVP) │ │
|
||||
│ │ Next.js (v2+) │ │ Next.js/React (v2+) │ │
|
||||
│ │ - Club setup │ │ - Login (club-issued) │ │
|
||||
│ │ - Member mgmt │ │ - Stock view │ │
|
||||
│ │ - Distribution │ │ - My quota / history │ │
|
||||
│ │ - Compliance │ │ - Request pickup │ │
|
||||
│ └────────┬────────┘ └──────────┬─────────────────┘ │
|
||||
│ │ │ │
|
||||
│ └───────────┬────────────┘ │
|
||||
│ ↓ │
|
||||
│ ┌───────────────────────────────────────┐ │
|
||||
│ │ Spring Boot 3.x Backend (Java 21) │ │
|
||||
│ │ - REST API (Spring MVC) │ │
|
||||
│ │ - JPA/Hibernate entities │ │
|
||||
│ │ - Business logic + compliance rules │ │
|
||||
│ │ - PDF report generation (iText 7) │ │
|
||||
│ │ - Spring Security + JWT │ │
|
||||
│ └──────────────────┬────────────────────┘ │
|
||||
│ ↓ │
|
||||
│ ┌─────────────────────┐ │
|
||||
│ │ PostgreSQL │ │
|
||||
│ │ - Multi-tenant │ │
|
||||
│ │ (tenant_id on all │ │
|
||||
│ │ JPA entities) │ │
|
||||
│ │ - Flyway migrations │ │
|
||||
│ └─────────────────────┘ │
|
||||
│ │
|
||||
│ ┌──────────────────────────────────────────────────┐ │
|
||||
│ │ Stripe Java SDK │ Email (Jakarta Mail) │ │
|
||||
│ │ (subscription billing) │ (notifications) │ │
|
||||
│ └──────────────────────────────────────────────────┘ │
|
||||
└─────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 5.5 New Skills Needed — Revised Learning Path
|
||||
|
||||
| Skill | Priority | Patrick's Starting Point | Resource |
|
||||
|-------|----------|--------------------------|----------|
|
||||
| Spring Boot 3.x REST | 🟡 Medium | Knows Jakarta EE — similar model | spring.io/guides |
|
||||
| Spring Security + JWT | 🟡 Medium | Security concepts from JEE | Baeldung tutorials |
|
||||
| Flyway migrations | 🟡 Medium | Knows JPA schema generation | flyway.io/docs |
|
||||
| Stripe Java SDK | 🟡 High | Knows REST from Java | stripe.com/docs/billing |
|
||||
| Next.js / React | 🔴 For v2+ | Zero JS framework experience | nextjs.org/learn (free) |
|
||||
| Docker + Compose | 🟡 Medium | Comfortable with Docker basics | Hetzner deploy guides |
|
||||
| German DSGVO practical | 🔴 Critical | Basic awareness | Legal counsel + AVV templates |
|
||||
|
||||
**Pragmatic MVP path:** Use PrimeFaces (Patrick knows it cold) → ship MVP → earn first revenue → invest time in Next.js for v2.
|
||||
|
||||
---
|
||||
|
||||
## 6. Go-To-Market Strategy
|
||||
|
||||
### 6.1 Phase 0 — Build & Validate (Private Beta)
|
||||
|
||||
**Goal:** Working MVP, 3-5 beta clubs, collect real feedback
|
||||
|
||||
**Actions:**
|
||||
- Join German cannabis clubs online community (Telegram groups, Reddit r/cannabisde)
|
||||
- Find 3-5 club admins willing to test for free
|
||||
- Build MVP focused on distribution tracking + compliance reports (the biggest pain)
|
||||
- Do NOT launch publicly until legally reviewed
|
||||
|
||||
**Where to find early adopters:**
|
||||
- Hanfverband Deutschland (German Hemp Association) — they represent clubs
|
||||
- Online forums: Rollitup.de German section, GreenPassion.de
|
||||
- Local cannabis clubs in your area
|
||||
- LinkedIn outreach to CSC founders
|
||||
|
||||
### 6.2 Phase 1 — Soft Launch (€0 → First €1K MRR)
|
||||
|
||||
**Target:** 30+ paying clubs, Basic plan minimum
|
||||
|
||||
**Channels (all B2B, no cannabis advertising):**
|
||||
- Word of mouth between club admins (community is small and tight-knit)
|
||||
- Content marketing: blog posts about "how to manage CanG compliance" (targets club admins searching for help)
|
||||
- Partner with lawyers advising clubs (they refer clients)
|
||||
- Hanfverband newsletter mention (not advertising — editorial content about compliance tools)
|
||||
- LinkedIn / XING posts targeted to "Vereinsvorstand" / "Vereinsgründer" keywords
|
||||
|
||||
### 6.3 Phase 2 — Growth (€1K → €10K MRR)
|
||||
|
||||
- Referral program (clubs refer other clubs for free months)
|
||||
- German startup press (Gründerszene, t3n)
|
||||
- Templates marketplace (Satzungen, Jugendschutzkonzepte)
|
||||
- Webinar series for Präventionsbeauftragte
|
||||
|
||||
---
|
||||
|
||||
## 7. Business Structure & Risk
|
||||
|
||||
### 7.1 Legal Entity
|
||||
|
||||
**Recommendation:** Register as a **Gewerbetreibender / Einzelunternehmen** first (simplest), then transition to **GmbH** when revenue exceeds €50K/year.
|
||||
|
||||
- No special license needed to sell software to cannabis clubs
|
||||
- You are NOT a cannabis business — you sell management software
|
||||
- Standard software VAT applies (19% German USt)
|
||||
|
||||
### 7.2 Banking & Payments
|
||||
|
||||
- **DO NOT** describe your business as "cannabis software" to banks
|
||||
- Describe it as: "Vereinsverwaltungs-Software" (club management software)
|
||||
- Stripe works fine for compliance software — they block cannabis sales, not software for cannabis-adjacent industries
|
||||
- Open a separate business account early (Kontist, Finom, or Deutsche Bank business)
|
||||
|
||||
### 7.3 Exit Scenarios
|
||||
|
||||
| Scenario | When | Valuation Range |
|
||||
|----------|------|----------------|
|
||||
| Keep as passive income | Year 2+ at €5K MRR | N/A |
|
||||
| Sell to larger SaaS player | Year 3+ at €20K MRR | 3-5× ARR (~€720K-1.2M) |
|
||||
| Raise seed funding | Year 2 with 200+ clubs | €500K-€2M round |
|
||||
| Pivot to EU expansion | Year 3 | Same platform, localised |
|
||||
|
||||
---
|
||||
|
||||
## 8. Development Roadmap
|
||||
|
||||
### Phase 0 — Foundation (Weeks 1-8, solo)
|
||||
- [ ] Set up Spring Boot 3.x project (Maven, JPA/Hibernate, PostgreSQL, Flyway)
|
||||
- [ ] Design JPA entities: Club, Member, Distribution, Strain, Batch (multi-tenant via tenant_id)
|
||||
- [ ] Build core REST API (member CRUD, distribution log)
|
||||
- [ ] Build admin portal with PrimeFaces (Patrick already knows this)
|
||||
- [ ] Distribution limit enforcement logic (25g/day, 50g/month, 30g/month under-21)
|
||||
- [ ] Simple PDF compliance report export (iText 7)
|
||||
- [ ] Spring Security + JWT auth (club admin login)
|
||||
- [ ] Deploy to Hetzner VPS (Docker Compose)
|
||||
|
||||
### Phase 1 — MVP (Weeks 9-16)
|
||||
- [ ] Member portal (PrimeFaces, login with club-issued creds, quota view, stock view)
|
||||
- [ ] Stock management module (strains, batches, quantities)
|
||||
- [ ] Contamination batch recall feature
|
||||
- [ ] Stripe Java SDK integration (subscription billing)
|
||||
- [ ] DSGVO: privacy policy, data processing agreement (AVV), cookie consent
|
||||
- [ ] Beta launch with 5 clubs (free, feedback-only)
|
||||
|
||||
### Phase 2 — Launch (Months 5-8)
|
||||
- [ ] Payment flows live (Stripe webhooks, subscription lifecycle)
|
||||
- [ ] Email notification system (Jakarta Mail / Resend API)
|
||||
- [ ] Marketing site (cannamanage.de — example name, separate Next.js landing page)
|
||||
- [ ] Legal review of terms, privacy, advertising compliance
|
||||
- [ ] Formal soft launch to club community
|
||||
- [ ] First paying customers
|
||||
|
||||
### Phase 3 — Growth (Months 9-18)
|
||||
- [ ] Frontend migration: PrimeFaces → Next.js/React (when revenue justifies it)
|
||||
- [ ] Mobile-optimised (PWA)
|
||||
- [ ] Legal template marketplace (Satzungen, Jugendschutzkonzepte)
|
||||
- [ ] Referral program
|
||||
- [ ] Webinar series for Präventionsbeauftragte
|
||||
- [ ] Hire first part-time support person
|
||||
|
||||
---
|
||||
|
||||
## 9. Honest Assessment — Strengths & Weaknesses
|
||||
|
||||
### Strengths ✅
|
||||
- **First mover advantage** — nobody is doing this well yet
|
||||
- **Regulatory moat** — the compliance burden creates permanent demand
|
||||
- **B2B SaaS** — predictable recurring revenue
|
||||
- **Patrick's Java expertise** — Spring Boot + JPA = fastest possible backend iteration (this is his daily tool at ADP)
|
||||
- **PrimeFaces knowledge** — built a full shop UI from scratch; zero learning curve for MVP frontend
|
||||
- **Low competition** — niche market overlooked by big players
|
||||
- **Low infra cost** — Hetzner VPS €5-20/month, manageable
|
||||
|
||||
### Weaknesses / Challenges ⚠️
|
||||
- **Modern frontend gap** — Next.js/React must eventually be learned for v2 polish (deferred, not blocking)
|
||||
- **Market is young** — clubs are still forming, slow regulatory licensing in some Länder
|
||||
- **Political risk** — new German government could tighten the law
|
||||
- **Churn risk** — if a club closes, subscription ends immediately
|
||||
- **Payment friction** — some processors are cannabis-adjacent-averse (mitigated by correct positioning)
|
||||
- **Two-sided attention** — building while working full-time at ADP is slow (nights/weekends)
|
||||
- **Spring Boot learning curve** — Patrick knows Jakarta EE / JEE; Spring Boot 3.x is adjacent but not identical
|
||||
|
||||
### The Honest Path
|
||||
This is a **18-24 month project** to meaningful passive income:
|
||||
- Months 1-3: Spring Boot setup + PrimeFaces MVP (using existing Java knowledge — fast!)
|
||||
- Months 4-6: Beta with 5 clubs, Stripe integration, DSGVO compliance
|
||||
- Months 7-12: Paid launch, first 30-50 paying clubs
|
||||
- Year 2+: €5-10K MRR is realistic, genuine passive with <10h/week
|
||||
|
||||
---
|
||||
|
||||
## 10. Immediate Next Steps
|
||||
|
||||
1. **Join 2-3 German cannabis club communities** (Telegram, Reddit) — listen, don't sell yet
|
||||
2. **Start Next.js tutorial** (nextjs.org/learn) — 1 hour/day, 4 weeks
|
||||
3. **Create a Supabase project** — explore multi-tenancy with Row Level Security
|
||||
4. **Set up the project repo** (pi_mcps/cannamanage or separate Gitea repo)
|
||||
5. **Talk to 3 club admins** — validate the pain before writing a line of code
|
||||
6. **Get a legal opinion** (€300-500 from a cannabis law specialist — worth it before launch)
|
||||
|
||||
---
|
||||
|
||||
## Appendix: Key CanG References
|
||||
|
||||
| Provision | Content |
|
||||
|-----------|---------|
|
||||
| §2 CanG | Definitions — Anbauvereinigung, Mitglied |
|
||||
| §§15-26 CanG | Anbauvereinigungen — formation, rights, obligations |
|
||||
| §22 CanG | Distribution limits (25g/day, 50g/month) |
|
||||
| §23 CanG | Under-21 restrictions (30g/month, 10% THC) |
|
||||
| §§6-7 CanG | Advertising and sponsoring ban |
|
||||
| §26 CanG | Documentation and reporting obligations |
|
||||
| §27 CanG | Prevention officer requirements |
|
||||
|
||||
---
|
||||
|
||||
*Plan created: 2026-04-04 | Next review: 2026-05-01 | Status: Awaiting Patrick's approval*
|
||||
Reference in New Issue
Block a user