# Contributing to Alfred ## Settings Management System Alfred uses a **declarative, schema-based configuration system** that ensures type safety, validation, and maintainability. ### Architecture Overview ``` settings.toml # Schema definitions (single source of truth) ↓ settings_schema.py # Parser & validation ↓ settings_bootstrap.py # Generation & resolution ↓ .env # Runtime configuration .env.make # Build variables for Makefile ↓ settings.py # Pydantic Settings (runtime validation) ``` ### Key Files - **`settings.toml`** — Declarative schema for all settings - **`alfred/settings_schema.py`** — Schema parser and validation logic - **`alfred/settings_bootstrap.py`** — Bootstrap logic (generates `.env` and `.env.make`) - **`alfred/settings.py`** — Pydantic Settings class (runtime) - **`.env`** — Generated configuration file (gitignored) - **`.env.make`** — Build variables for Makefile (gitignored) ### Setting Sources Settings can come from different sources: | Source | Description | Example | |--------|-------------|---------| | `toml` | From `pyproject.toml` | Version numbers, build config | | `env` | From `.env` file | User-provided values, API keys | | `generated` | Auto-generated secrets | JWT secrets, passwords | | `computed` | Calculated from other settings | Database URIs | ### How to Add a New Setting #### 1. Define in `settings.toml` ```toml [tool.alfred.settings_schema.MY_NEW_SETTING] type = "string" # string, integer, float, boolean, secret, computed source = "env" # env, toml, generated, computed default = "default_value" # Optional: default value description = "Description here" # Required: clear description category = "app" # app, api, database, security, build required = true # Optional: default is true validator = "range:1:100" # Optional: validation rule export_to_env_make = false # Optional: export to .env.make for Makefile ``` #### 2. Regenerate Configuration ```bash make bootstrap ``` This will: - Read the schema from `settings.toml` - Generate/update `.env` with the new setting - Generate/update `.env.make` if `export_to_env_make = true` - Preserve existing secrets #### 3. Validate ```bash make validate ``` This ensures all settings are valid according to the schema. #### 4. Use in Code The setting is automatically available in `settings.py`: ```python from alfred.settings import settings print(settings.my_new_setting) ``` ### Setting Types #### String Setting ```toml [tool.alfred.settings_schema.API_URL] type = "string" source = "env" default = "https://api.example.com" description = "API base URL" category = "api" ``` #### Integer Setting with Validation ```toml [tool.alfred.settings_schema.MAX_RETRIES] type = "integer" source = "env" default = 3 description = "Maximum retry attempts" category = "app" validator = "range:1:10" ``` #### Secret (Auto-generated) ```toml [tool.alfred.settings_schema.API_SECRET] type = "secret" source = "generated" secret_rule = "32:b64" # 32 bytes, base64 encoded description = "API secret key" category = "security" ``` Secret rules: - `"32:b64"` — 32 bytes, URL-safe base64 - `"16:hex"` — 16 bytes, hexadecimal #### Computed Setting ```toml [tool.alfred.settings_schema.DATABASE_URL] type = "computed" source = "computed" compute_from = ["DB_HOST", "DB_PORT", "DB_NAME"] compute_template = "postgresql://{DB_HOST}:{DB_PORT}/{DB_NAME}" description = "Database connection URL" category = "database" ``` #### From TOML (Build Variables) ```toml [tool.alfred.settings_schema.APP_VERSION] type = "string" source = "toml" toml_path = "tool.poetry.version" description = "Application version" category = "build" export_to_env_make = true # Available in Makefile ``` ### Validators Available validators: - **`range:min:max`** — Numeric range validation ```toml validator = "range:0.0:2.0" # For floats validator = "range:1:100" # For integers ``` ### Categories Organize settings by category: - **`app`** — Application settings - **`api`** — API keys and external services - **`database`** — Database configuration - **`security`** — Secrets and security keys - **`build`** — Build-time configuration ### Best Practices 1. **Always add a description** — Make it clear what the setting does 2. **Use appropriate types** — Don't use strings for numbers 3. **Add validation** — Use validators for numeric ranges 4. **Categorize properly** — Helps with organization 5. **Use computed settings** — For values derived from others (e.g., URIs) 6. **Mark secrets as generated** — Let the system handle secret generation 7. **Export build vars** — Set `export_to_env_make = true` for Makefile variables ### Workflow Example ```bash # 1. Edit settings.toml vim settings.toml # 2. Regenerate configuration make bootstrap # 3. Validate make validate # 4. Test python -c "from alfred.settings import settings; print(settings.my_new_setting)" # 5. Commit (settings.toml only, not .env) git add settings.toml git commit -m "Add MY_NEW_SETTING" ``` ### Commands ```bash make bootstrap # Generate .env and .env.make from schema make validate # Validate all settings against schema make help # Show all available commands ``` ### Troubleshooting **Setting not found in schema:** ``` KeyError: Missing [tool.alfred.settings_schema] section ``` → Check that `settings.toml` exists and has the correct structure **Validation error:** ``` ValueError: MY_SETTING must be between 1 and 100, got 150 ``` → Check the validator in `settings.toml` and adjust the value in `.env` **Secret not preserved:** → Secrets are automatically preserved during `make bootstrap`. If lost, they were never in `.env` (check `.env` exists before running bootstrap) ### Testing When adding a new setting, consider adding tests: ```python # tests/test_settings_schema.py def test_my_new_setting(self, create_schema_file): """Test MY_NEW_SETTING definition.""" schema_toml = """ [tool.alfred.settings_schema.MY_NEW_SETTING] type = "string" source = "env" default = "test" """ base_dir = create_schema_file(schema_toml) schema = load_schema(base_dir) definition = schema.get("MY_NEW_SETTING") assert definition.default == "test" ``` ### Migration from Old System If you're migrating from the old system: 1. Settings are now in `settings.toml` instead of scattered across files 2. No more `.env.example` — schema is the source of truth 3. Secrets are auto-generated and preserved 4. Validation happens at bootstrap time, not just runtime --- ## Questions? Open an issue or check the existing settings in `settings.toml` for examples.