ADR-008: Jurisdiction-Specific Form Configuration

On this page

Status

Partially resolved (mandated reporter categories shipped via craig-reference enum; remaining jurisdiction-varying form content still Pending).

Resolution Notes (2026-04-20)

The immediate trigger — Georgia’s mandated-reporter categories — was handled in Phase 12 Phase 2c by adding a MandatedReporterCategory enum to craig-reference and a dropdown in the public intake BFF form. That subset of the problem is complete and in production.

The broader question of jurisdiction-specific form field labels, required/optional toggles, guidance text, and conditional field logic is still open. The YAML-per-jurisdiction approach (Option A in the analysis below) has not been implemented. For multi-state deployments beyond Georgia, a future MR will need to either:

  • Pick and implement one of the options below.

  • Accept per-deployment template overrides (craig-web + craig-intake templates mounted from a jurisdiction-specific volume) as a stopgap.

Revisit this ADR before onboarding the second state/tribe.

Context

CRAIG’s public intake form needs jurisdiction-specific content that varies by state statute. The immediate trigger is mandated reporter categories — Georgia’s O.C.G.A. §19-7-5 defines 15 categories, but other states have different lists. Beyond reporter categories, other form elements may vary by jurisdiction: required vs optional fields, guidance text, dropdown options, and conditional field logic.

Universal demographic enums (Gender, Race, Ethnicity) are federally standardized (AFCARS/NCANDS) and belong in craig-reference as Rust enums. Jurisdiction-specific statutory content is a different category — it changes by state law, not by code logic.

Options

Option A: YAML jurisdiction config files loaded at startup

Jurisdiction-specific form definitions stored as YAML files under config/jurisdictions/{state}/intake-form.yaml. craig-intake loads the file at startup based on CRAIG_INTAKE__JURISDICTION env var. Served via a GET /public/v1/form-config endpoint. The web form fetches config to populate dropdowns dynamically.

Pros:

  • Easy to add states without code changes — YAML editable by non-developers

  • Follows the existing pattern of rulesets/{jurisdiction}/ for jurisdiction-specific content

  • Supports Epic &9 (Standalone Mode) — standalone deployments ship their own YAML

  • Version-controllable in git

Cons:

  • Need to define a YAML schema and validate at startup

  • Conditional logic in YAML can get complex if taken too far

  • No compile-time type safety on config values

Option B: Database-driven form config (admin UI)

Store form definitions in a form_configs table, editable via admin API endpoints.

Pros:

  • Runtime-changeable without redeploy

  • Admin users can update without git access

Cons:

  • Over-engineered for current stage

  • Requires admin UI for editing

  • Harder to version control and audit

  • Migration path unclear for multi-jurisdiction

Option C: Rust enums per jurisdiction (extend current pattern)

Add MandatedReporterCategory and other jurisdiction-specific enums to craig-reference with strum serialization, gated by jurisdiction feature flags or separate enum sets.

Pros:

  • Compile-time safety

  • Consistent with existing enum pattern

Cons:

  • Adding a state requires code change + redeploy

  • Enum explosion as jurisdictions multiply (50 states × N categories)

  • Mixes universal standards (AFCARS) with state-specific statutes in same crate

Decision

TBD — to be decided before Phase 2c implementation begins.

Consequences

The chosen approach affects:

  • How craig-intake loads and serves form metadata

  • Whether the web form uses static <option> elements or dynamically populates from an API

  • How new jurisdictions are onboarded (code change vs config file vs admin UI)

  • Epic &9 (Standalone Mode) architecture — standalone deployments need form config

  • Whether mandated reporter categories need a craig-reference enum or live outside the crate

References

Edit this page · latest