Skip to content

feat: harden config error messages in loading paths#117

Merged
QMalcolm merged 2 commits into
mainfrom
qmalcolm--feat-harden-config-error-messages
May 6, 2026
Merged

feat: harden config error messages in loading paths#117
QMalcolm merged 2 commits into
mainfrom
qmalcolm--feat-harden-config-error-messages

Conversation

@QMalcolm

@QMalcolm QMalcolm commented May 6, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Loader (feat(loader)): Adds warn_missing_node_fields/2 called after every node parse. Emits a Logger.warning naming the node {type_id, concept_id, field_name} when required fields are absent:
    • :generated nodes missing "method" — the evaluator cannot find a rolling method
    • :mapping nodes missing "input" or "steps" — the mapping lookup cannot run
    • Rolling methods missing "dice" — the dice parser crashes on first roll
  • RuleModule (feat(rule_module)): from_map/1 now validates concept type and metadata contribution lists before building the struct:
    • [[concept_type]] entry missing id{:error, {:concept_type_missing_id, idx}}
    • [[metadata_contributions]] entry missing any of from_type/from_field/to_type/to_field/value{:error, {:metadata_contribution_missing_field, field, idx}}
    • Restructured from_map/1 from if/else + with to a flat with/else to fix a Credo nesting violation

Previously all of these failed silently (nil values, no effects generated, or runtime evaluator crash with no load-time signal).

Test plan

  • All 66 tests pass
  • Loader: 3 new tests — generated node missing method, mapping node missing input/steps, rolling method missing dice
  • RuleModule: 2 new tests — concept_type missing id, metadata_contribution missing any required field (parameterized over all 5)

Summary by Bito

  • Added validation in loader.ex to warn about missing required fields in rolling methods (e.g., 'dice') and nodes (e.g., 'method' for generated type, 'input' and 'steps' for mapping type).
  • Refactored rule_module.ex to validate concept types and metadata contributions upfront, ensuring required fields like 'id' for concept types and 'from_type', 'from_field', 'to_type', 'to_field', 'value' for metadata contributions are present, returning errors if missing.
  • Added unit tests in loader_test.exs and rule_module_test.exs to verify the new validation logic and warning behaviors.

QMalcolm added 2 commits May 6, 2026 09:46
…re absent

Missing required fields in node definitions silently produce nil values
that the evaluator later dereferences, giving a confusing crash at
runtime instead of a helpful message at load time.

Now `parse_concept_fields` calls `warn_missing_node_fields/2` after
every node parse, emitting a Logger.warning that names the node
({type_id, concept_id, field_name}) and the missing field:

- :generated nodes must have "method"; without it the evaluator cannot
  find a rolling method to use.
- :mapping nodes must have "input" and "steps"; without either, the
  mapping lookup cannot run.
- :accumulator and :formula nodes have no required optional fields
  beyond their structural keys, so they are not validated here.

Similarly, rolling methods missing "dice" now warn with the method id;
without dice the expression parser will crash on the first roll.

Logger.warning rather than error return, consistent with the metadata
key validation added previously. A future breaking release can harden
these to errors.
…nd metadata_contribution

A [[concept_type]] entry without an id, or a [[metadata_contributions]]
entry without from_type/from_field/to_type/to_field/value, previously
produced a struct with nil fields that silently misbehaved: a nil id
would be included in the concept_type_ids MapSet, and a nil from_type
would cause the contribution to match no concepts and generate no
effects, with no indication of the problem.

from_map/1 now validates both lists before building the struct:
- validate_concept_types/1: {:error, {:concept_type_missing_id, idx}}
  if any entry lacks an id field.
- validate_metadata_contributions/1: {:error,
  {:metadata_contribution_missing_field, field, idx}} if any entry is
  missing one of the five required fields.

These return {:error, ...} from from_map/1 (and therefore from
Loader.load/1) rather than Logger.warning, because a metadata_contribution
with a nil from_type or a concept_type with a nil id cannot be recovered
from at runtime — the data is structurally invalid.

Restructured from_map/1 to use a top-level with/else rather than
if/else + nested with, eliminating a Credo nesting violation. Extracted
check_metadata_contribution_fields/1 to flatten the validate function
for the same reason.
@QMalcolm QMalcolm merged commit 502390f into main May 6, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant