-
Notifications
You must be signed in to change notification settings - Fork 0
Design: Configurable document types (#224) #225
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,184 @@ | ||
| # Design: Configurable Document Types for FIRE (Issue #224) | ||
|
|
||
| ## Context | ||
|
|
||
| FIRE currently hardcodes three document types (`.sysreq.md`, `.swreq.md`, `.regreq.md`) | ||
| with validation rules embedded in Python Pydantic models. Systems engineering teams need | ||
| many more document types (`.handbook.md`, `.hara.md`, `.tara.md`, `.opman.md`, etc.) each with | ||
| different mandatory/optional fields. The issue asks for a consumer-configurable system | ||
| where document formats are defined in YAML, validated by FIRE tooling via Bazel, and | ||
| auto-generated into a FORMAT_SPECIFICATION.md usable as LLM context. | ||
|
|
||
| ## Design: YAML Config Schema | ||
|
|
||
| A `fire_config.yaml` in the consumer's repo defines field types and document types: | ||
|
|
||
| ```yaml | ||
| fire_config_version: 1 | ||
|
|
||
| field_definitions: | ||
| sil: | ||
| display_name: "SIL" | ||
| type: enum | ||
| values: | ||
| [ | ||
| "ASIL-A", | ||
| "ASIL-B", | ||
| "ASIL-C", | ||
| "ASIL-D", | ||
| "SIL-1", | ||
| "SIL-2", | ||
| "SIL-3", | ||
| "SIL-4", | ||
| "DAL-A", | ||
| "DAL-B", | ||
| "DAL-C", | ||
| "DAL-D", | ||
| "DAL-E", | ||
| "PL-a", | ||
| "PL-b", | ||
| "PL-c", | ||
| "PL-d", | ||
| "QM", | ||
| ] | ||
| allow_todo: true | ||
| description: "Safety Integrity Level (ISO 26262, IEC 61508, DO-178C/DO-254, ISO 13849, QM)" | ||
|
|
||
| sec: | ||
| display_name: "Sec" | ||
| type: bool | ||
| allow_todo: true | ||
| description: "Security relevance flag" | ||
|
|
||
| version: | ||
| display_name: "Version" | ||
| type: int | ||
| min_value: 1 | ||
| allow_todo: false | ||
| description: "Requirement version, positive integer >= 1" | ||
|
|
||
| parent: | ||
| display_name: "Parent" | ||
| type: parent_link | ||
| allow_todo: true | ||
| allow_multiple: true | ||
| description: "Markdown link(s) to parent requirement(s)" | ||
|
|
||
| document_types: | ||
| sysreq: | ||
| suffix: ".sysreq.md" | ||
| display_name: "System Requirement" | ||
| description: "High-level system requirements" | ||
| required_fields: [sil, sec, version] | ||
| optional_fields: [parent] | ||
|
|
||
| swreq: | ||
| suffix: ".swreq.md" | ||
| display_name: "Software Requirement" | ||
| description: "Implementation-level software requirements" | ||
| required_fields: [sil, sec, version] | ||
| optional_fields: [parent] | ||
|
|
||
| regreq: | ||
| suffix: ".regreq.md" | ||
| display_name: "Regulatory Requirement" | ||
| description: "Regulatory obligations (SIL/Sec optional)" | ||
| required_fields: [version] | ||
| optional_fields: [sil, sec, parent] | ||
| ``` | ||
|
|
||
| Consumers extend this by adding their own types: | ||
|
|
||
| ```yaml | ||
| handbook: | ||
| suffix: ".handbook.md" | ||
| display_name: "Handbook Entry" | ||
| description: "Product handbook entries" | ||
| required_fields: [version] | ||
| optional_fields: [] | ||
| ``` | ||
|
|
||
| ## Key Architectural Decisions | ||
|
|
||
| - **`field_definitions`** are reusable across document types (sil is identical for sysreq and swreq) | ||
| - **Field types**: `enum`, `bool`, `int`, `parent_link` — matching existing Pydantic validators. Extensible later with `string`, `float`, `regex` | ||
| - **Default config** ships with FIRE and matches current hardcoded behavior — backwards compatible with no consumer config | ||
| - **Dynamic Pydantic models** built at runtime from config, preserving error reporting quality and `extra: "forbid"` behavior | ||
|
|
||
| ## Files to Create | ||
|
|
||
| | File | Purpose | | ||
| | ------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ | | ||
| | `fire/starlark/config_models.py` | Pydantic models for config schema (`FireConfig`, `FieldDefinition`, `DocumentType`) + `load_config()` + embedded default | | ||
| | `fire/starlark/config_models_test.py` | Tests for config parsing and validation | | ||
| | `fire/starlark/dynamic_requirement_model.py` | Factory: config -> Pydantic BaseModel subclass at runtime | | ||
| | `fire/starlark/dynamic_requirement_model_test.py` | Tests proving dynamic models match static ones | | ||
| | `fire/starlark/default_fire_config.yaml` | Default config file shipped with FIRE | | ||
| | `fire/starlark/generate_format_spec.py` | Reads config, produces FORMAT_SPECIFICATION.md | | ||
| | `fire/starlark/generate_format_spec_test.py` | Tests for spec generation | | ||
| | `fire/starlark/format_spec.bzl` | Bazel rule `generate_format_specification()` | | ||
|
|
||
| ## Files to Modify | ||
|
|
||
| | File | Change | | ||
| | -------------------------------------------- | ---------------------------------------------------------------------------------------- | | ||
| | `fire/starlark/validate_cross_references.py` | `_metadata_model_for_file()` -> config-driven suffix lookup; `main()` accepts `--config` | | ||
| | `fire/starlark/requirements.bzl` | `requirement_library()` gains optional `config` attr, passed to Python script | | ||
| | `fire/starlark/release_report.py` | `main()` accepts `--config`; passes to metadata parsing | | ||
| | `fire/starlark/reports.bzl` | `release_report` rule gains optional `config` attr | | ||
| | `fire/starlark/BUILD.bazel` | New build targets for new files | | ||
| | `FORMAT_SPECIFICATION.md` | Replaced by auto-generated output | | ||
|
|
||
| ## Bazel Integration | ||
|
|
||
| ```text | ||
| fire_config.yaml ─┬──> requirement_library(config=":fire_config") | ||
| │ └─> validate_cross_references.py --config fire_config.yaml | ||
| │ | ||
| ├──> release_report(config=":fire_config") | ||
| │ └─> release_report.py --config fire_config.yaml | ||
| │ | ||
| └──> generate_format_specification(config=":fire_config", out="FORMAT_SPEC.md") | ||
| └─> generate_format_spec.py --config fire_config.yaml | ||
| ``` | ||
|
|
||
| Config is `attr.label(allow_single_file=[".yaml",".yml"], default=None)`. When None, Python scripts use the embedded default. | ||
|
|
||
| ## Implementation Phases | ||
|
|
||
| ### Phase 1: Config Models (no behavior change) | ||
|
|
||
| 1. Create `config_models.py` with Pydantic models for config schema | ||
| 2. Create `default_fire_config.yaml` matching current behavior | ||
| 3. Add `load_config()` that loads YAML or returns default | ||
| 4. Tests: config parses correctly, invalid configs rejected | ||
|
|
||
| ### Phase 2: Dynamic Model Factory (no behavior change) | ||
|
|
||
| 1. Create `dynamic_requirement_model.py` — builds Pydantic model from config | ||
| 2. Tests: dynamic models produce identical validation to static `RequirementMetadata` and `RegulatoryRequirementMetadata` for all existing test cases | ||
|
|
||
| ### Phase 3: Wire Config into Validation | ||
|
|
||
| 1. Modify `validate_cross_references.py`: config-driven `_metadata_model_for_file()` and `--config` arg | ||
| 2. Modify `requirements.bzl`: optional `config` attr | ||
| 3. Modify `release_report.py` and `reports.bzl`: optional `config` attr | ||
| 4. Tests: existing tests still pass, new test with custom document type | ||
|
|
||
| ### Phase 4: Format Specification Generator | ||
|
|
||
| 1. Create `generate_format_spec.py` and `format_spec.bzl` | ||
| 2. Replace hand-written `FORMAT_SPECIFICATION.md` with auto-generated output | ||
| 3. Tests: generated output covers all document types and fields | ||
|
|
||
| ### Phase 5: Documentation and Examples | ||
|
|
||
| 1. Update README.md with config section | ||
| 2. Add example with custom document type | ||
|
|
||
| ## Verification | ||
|
|
||
| - All existing tests pass unchanged (backwards compatibility) | ||
| - New unit tests for config parsing, dynamic model generation, format spec generation | ||
| - Integration test: add custom document type to `integration_test/`, validate it works end-to-end | ||
| - Verify generated FORMAT_SPECIFICATION.md is equivalent to current hand-written one | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.