| name | add-config-field |
| description | Use this skill when adding a new YAML configuration field to ForgeLM. Handles Pydantic model update, cross-field validation, config_template.yaml sync, bilingual docs, and tests. Triggered by requests like "add a new config option for X", "expose Y as a YAML field", "make Z configurable". |
Skill: Add a New Config Field
ForgeLM is strictly config-driven. Every new runtime behaviour becomes a YAML field passing through Pydantic validation to the consumer. This skill walks through all the places you must touch so nothing drifts.
When to use
- User wants a new YAML option (e.g.,
training.new_flag: true)
- A feature is currently hardcoded and needs to be exposed
- A new optional dependency needs its own config section
Do not use for:
- Changing an existing field's default (that's a different, more risky change)
- Internal refactors that don't affect user-visible YAML
Required reading before acting
- docs/standards/architecture.md โ config flow principle
- docs/standards/coding.md โ Pydantic conventions
- forgelm/config.py โ existing patterns to match
Steps
1. Pick the right config class
Look at forgelm/config.py. Choose:
ModelConfig โ for model-related fields (name, quantization, MoE)
LoraConfigModel โ for LoRA/DoRA/PiSSA/rsLoRA parameters
TrainingConfig โ for training hyperparameters, trainer types, algorithmic flags
DataConfig โ for dataset paths, format, preprocessing
EvaluationConfig / SafetyConfig / JudgeConfig โ for eval pipeline
ComplianceConfig โ for EU AI Act artifacts
WebhookConfig, TrackingConfig, etc. โ for integrations
ForgeConfig (root) โ only if genuinely cross-cutting
If none fit, the field may belong to a new config class โ see architecture.md ยง1.
2. Add the field
class TrainingConfig(BaseModel):
...
new_flag: Optional[bool] = None
"""If set, enable the X behaviour. Default: inherit from model's default."""
Rules:
- Use
Optional[T] = None for truly optional fields; T = default_value for always-set fields with safe defaults.
- Use
Literal["a", "b"] for enums, not str.
- Field order: existing fields first, new field at a logical group boundary.
- One-line docstring directly below the field (Pydantic doesn't use these, but humans and docs do).
3. Validation
If the field has invariants, add field_validator or model_validator:
@field_validator("new_flag")
@classmethod
def _validate_new_flag(cls, v: Optional[bool], info) -> Optional[bool]:
if v and info.data.get("trainer_type") != "grpo":
raise ValueError("new_flag is only valid for trainer_type='grpo'")
return v
Error messages follow error-handling.md โ specific, actionable.
4. Wire it to the consumer
Find the module that will read the field:
Access via config.training.new_flag โ never import from environment or global state.
5. Update config_template.yaml
The repo ships config_template.yaml as the canonical example. Add your field with a comment:
training:
trainer_type: sft
...
new_flag: false
6. Document the field
Both mirrors:
Follow localization.md for the TR mirror.
7. Write a test
Create or extend tests/test_config.py:
def test_new_flag_defaults_to_none():
cfg = ForgeConfig.model_validate(minimal_config())
assert cfg.training.new_flag is None
def test_new_flag_requires_grpo_trainer():
with pytest.raises(ValidationError, match="trainer_type='grpo'"):
ForgeConfig.model_validate(
minimal_config(training={"new_flag": True, "trainer_type": "sft"})
)
At least one happy-path + one error-path test.
8. Update CHANGELOG
In CHANGELOG.md, under [Unreleased] / ### Added:
- **New config field**: `training.new_flag` โ enables X for GRPO trainer. See docs/reference/configuration.md#new_flag.
Verification before PR
pytest tests/test_config.py -v
ruff check forgelm/config.py
forgelm --config config_template.yaml --dry-run
All three must pass.
Pitfalls to avoid
- Adding
new_flag without validation. Pydantic accepts any value of the declared type โ if there are interactions with other fields, you must validate them.
- Skipping the TR mirror. The PR gets rejected. Same change, same PR.
- Breaking backward compatibility with a default change. If users have
training: blocks that worked before and would fail now, you need a major bump. Consider adding as opt-in instead.
- Forgetting
config_template.yaml. The CI dry-run uses this; if your field is required, the template must include it.
- Logging the field value. If it could contain secrets (tokens, paths), sanitize per logging-observability.md.
Related skills
add-trainer-feature โ if the field controls end-to-end behaviour, not just a knob
sync-bilingual-docs โ run after step 6 to verify TR/EN parity