Development Guide
May 6, 2026 · View on GitHub
Guide for developers contributing to OpenGlaze.
Table of Contents
- Development Environment
- Project Structure
- Running Tests
- Adding Features
- Database Changes
- Frontend Development
- AI Integration
- Release Process
Development Environment
Prerequisites
- Python 3.11+
- Docker & Docker Compose (for full stack testing)
- Node.js 20+ (optional, for frontend linting)
Setup
# Clone repository
git clone https://github.com/KyaniteLabs/openglaze.git
cd openglaze
# Create virtual environment
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
# Install dependencies
pip install -r requirements.txt
# Install dev dependencies (if separate)
pip install pytest pytest-cov black ruff
# Set up pre-commit hooks (optional, requires .pre-commit-config.yaml)
# pre-commit install
Running Locally
# Quick start (SQLite, no auth)
python seed_data.py
python server.py
# Full stack (PostgreSQL, Kratos)
docker compose up -d
Environment Variables for Development
Create .env:
FLASK_ENV=development
BASE_URL=http://localhost:8767
DATABASE_URL=sqlite:///glaze.db
SECRET_KEY=dev-secret-key-not-for-production
MODE=personal
# For AI testing
OLLAMA_HOST=http://localhost:11434
ANTHROPIC_API_KEY=sk-ant-your-key
Project Structure
openglaze/
├── server.py # Flask app factory and routes
├── auth.py # Authentication wrappers (see core/auth/)
├── seed_data.py # Database seeder
├── core/
│ ├── schema.sql # Database schema (single source of truth)
│ ├── glazes/ # Glaze CRUD logic
│ ├── combinations/ # Layering logic
│ ├── experiments/ # Pipeline stage management
│ ├── chemistry/ # UMF, compatibility, batch calc
│ ├── ai/ # Kama assistant + context retrieval
│ ├── auth/ # JWT / Ory Kratos integration
│ ├── security/ # Rate limiting, CSP, validation
│ ├── studios/ # Studio and member management
│ ├── gamification/ # Points, badges, leaderboards
│ ├── predictions/ # Prediction market logic
│ └── simulation/ # Chemistry simulation
├── frontend/ # Vanilla JS SPA
│ ├── index.html # App shell
│ ├── styles/ # CSS modules
│ ├── scripts/ # JS feature modules
│ └── sw.js # Service worker
├── ceramics-foundation/ # Canonical ceramic data
│ ├── data/ # JSON reference data
│ ├── recipes/ # YAML recipe collections
│ ├── taxonomies/ # Classifications
│ └── studios/ # Studio profile templates
├── tests/ # Test suite
│ ├── conftest.py # Shared fixtures
│ ├── test_chemistry.py
│ ├── test_kama_context.py
│ ├── test_routes.py
│ └── ...
├── docs/ # Documentation
├── templates/ # Shareable glaze collections
├── config/ # Environment and mode detection
├── docker-compose.yml # Full stack orchestration
├── Dockerfile # Application container
└── requirements.txt # Python dependencies
Running Tests
Full Suite
pytest tests/ -v
With Coverage
pytest tests/ --cov=. --cov-report=html --cov-report=term
open htmlcov/index.html # View coverage report
Specific Test Files
pytest tests/test_chemistry.py -v -k "umf"
pytest tests/test_kama_context.py -v
pytest tests/test_routes.py -v
Test Database
Tests use an in-memory SQLite database created from core/schema.sql. The conftest.py fixture dynamically loads the canonical schema, ensuring tests always match production.
Writing Tests
# tests/test_feature.py
import pytest
def test_glaze_creation(client, auth_headers):
"""Test creating a new glaze."""
response = client.post('/api/glazes',
json={
'name': 'Test Glaze',
'recipe': 'Feldspar: 50%, Silica: 50%',
'cone': '10',
'atmosphere': 'reduction'
},
headers=auth_headers
)
assert response.status_code == 201
data = response.get_json()
assert data['name'] == 'Test Glaze'
assert 'umf' in data
Test Fixtures (conftest.py)
Key fixtures:
client— Flask test client with in-memory DBauth_headers— Valid JWT authorization headerssample_glaze— Pre-created glaze for testingsample_user— Authenticated test user
Adding Features
Backend Feature Pattern
- Define the data model — Update
core/schema.sqlif needed - Create the module — Add to
core/<feature>/ - Add routes — Register in
server.py - Write tests — Add to
tests/test_<feature>.py - Document — Update API.md and user-guide.md
Example: Adding a "Favorite Glazes" feature
# core/favorites.py
from flask import g
def add_favorite(glaze_id: int, user_id: int) -> dict:
db = g.db
cursor = db.cursor()
cursor.execute('''
INSERT INTO favorites (user_id, glaze_id, created_at)
VALUES (?, ?, datetime('now'))
ON CONFLICT DO NOTHING
''', (user_id, glaze_id))
db.commit()
return {"status": "added"}
def get_favorites(user_id: int) -> list:
db = g.db
cursor = db.cursor()
cursor.execute('''
SELECT g.* FROM glazes g
JOIN favorites f ON g.id = f.glaze_id
WHERE f.user_id = ?
ORDER BY f.created_at DESC
''', (user_id,))
return [dict(row) for row in cursor.fetchall()]
# In server.py
from core.favorites import add_favorite, get_favorites
@app.route('/api/favorites', methods=['GET'])
@require_auth
def list_favorites():
return jsonify(get_favorites(g.user_id))
@app.route('/api/favorites/<int:glaze_id>', methods=['POST'])
@require_auth
def create_favorite(glaze_id):
return jsonify(add_favorite(glaze_id, g.user_id)), 201
Frontend Feature Pattern
- Add UI components — Update
frontend/index.htmlor create partial - Add styles — Update appropriate
frontend/styles/*.css - Add JavaScript — Create
frontend/scripts/<feature>.js - Register in app — Import in main script bundle
Database Changes
Schema Migrations
OpenGlaze uses a simple migration system in server.py:
def _run_migrations(db):
"""Run any pending schema migrations."""
cursor = db.cursor()
# Check current schema version
cursor.execute("PRAGMA user_version")
version = cursor.fetchone()[0]
if version < 2:
# Migration: add favorites table
cursor.execute('''
CREATE TABLE IF NOT EXISTS favorites (
user_id INTEGER REFERENCES users(id),
glaze_id INTEGER REFERENCES glazes(id),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (user_id, glaze_id)
)
''')
cursor.execute("PRAGMA user_version = 2")
db.commit()
Adding a New Table
- Add to
core/schema.sql(for new installs) - Add migration to
_run_migrations()(for existing installs) - Update test fixtures if needed
- Document in architecture.md
Frontend Development
CSS Architecture
Modular CSS in frontend/styles/:
styles/
├── variables.css # CSS custom properties (colors, spacing, typography)
├── reset.css # Normalize/reset
├── typography.css # Font rules
├── layout.css # Grid, flexbox, sidebar, header
├── components.css # Buttons, cards, inputs, badges
├── gamification.css # Points, badges, leaderboards
├── prediction.css # Prediction market UI
└── tips.css # Tooltips and help text
JavaScript Architecture
Feature-based modules:
// frontend/scripts/glazes.js
export const GlazeManager = {
async loadGlazes(filters = {}) {
const params = new URLSearchParams(filters);
const response = await fetch(`/api/glazes?${params}`);
return response.json();
},
async createGlaze(data) {
const response = await fetch('/api/glazes', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
});
return response.json();
}
};
Theme System
CSS variables support light/dark/system modes:
:root {
--color-bg: #faf8f5;
--color-text: #1a1a1a;
--color-primary: #5a9078;
}
[data-theme="dark"] {
--color-bg: #1a1a1a;
--color-text: #faf8f5;
--color-primary: #7ab89a;
}
Toggle via JavaScript:
document.documentElement.setAttribute('data-theme', 'dark');
AI Integration
Adding a New LLM Provider
- Create adapter in
core/ai/providers/<provider>.py - Implement standard interface:
class BaseProvider:
def chat(self, messages: list, stream: bool = False) -> str | Iterator[str]:
raise NotImplementedError
def is_available(self) -> bool:
raise NotImplementedError
- Register in
core/ai/manager.py - Add config options to
.env.example
Context Injection
The context retriever fetches relevant data before sending to the LLM:
def build_context(glaze_ids: list[int] = None, query: str = "") -> str:
context_parts = []
if glaze_ids:
glazes = fetch_glazes(glaze_ids)
context_parts.append(format_glazes_for_prompt(glazes))
if is_chemistry_question(query):
context_parts.append(fetch_chemistry_rules(query))
return "\n\n".join(context_parts)
Release Process
- Update version — Update
__version__inserver.py - Update CHANGELOG.md — Document all changes
- Run tests —
pytest tests/must pass - Update docs — Ensure docs reflect new features
- Create git tag:
git tag -a v1.1.0 -m "Release v1.1.0" git push origin v1.1.0 - Create GitHub Release — With release notes and binaries if applicable
- Update Docker image — Build and push to registry
Version Numbering
Follows Semantic Versioning:
MAJOR— Breaking changesMINOR— New features, backward compatiblePATCH— Bug fixes
Code Style
Python
- PEP 8 compliant
- Black formatter:
black . - Ruff linter:
ruff check . - Type hints encouraged for new code
- Docstrings for public functions
JavaScript
- ES6+ features
- 2-space indentation
- Semicolons required
- Single quotes for strings
CSS
- CSS custom properties for theming
- BEM-like naming:
.component__element--modifier - Mobile-first responsive design