STORY-F-015: finnest_compliance schemas + ~100 credential_types seed¶
Epic: Compliance Seed Priority: Must Have Story Points: 3 Status: Not Started Assigned To: Unassigned Created: 2026-04-17 Sprint: 3
User Story¶
As a Recruitment Consultant (post-launch) and as a developer (now),
I want the compliance.credential_types table populated with ~100 real Australian credential types across industries,
so that downstream compliance checks (roster assignment, placement) have real data to reason over and multi-industry profiles (F-019) have targets to reference.
Description¶
Background¶
ADR-011-F makes FinnestCompliance.check/2 the single synchronous cross-domain exception — it gates writes in 5 domains (roster, timekeep, payroll, clients, assets). Before that check function is useful, the credential registry needs real data. Brainstorm-05 (multi-industry design) + brainstorm-11 (Traffio feature map) + brainstorm-12 (competitor audit) enumerate the required credential types.
This story builds the finnest_compliance schemas + seeds ~100 credential types — hierarchical, industry-tagged, with expiry rules. Industry profiles (labour_hire + construction) that reference these credentials land in F-019.
Scope¶
In scope:
- Migration creating
complianceschema: compliance.credential_types— id UUID, parent_id UUID (FK self-reference, nullable — hierarchy), code VARCHAR UNIQUE, name VARCHAR, category VARCHAR (high_risk_work | construction | mining | logistics | defence | retail | traffic | general | ...), nationally_recognized BOOLEAN, issuing_jurisdictions JSONB (["NSW","VIC",...]), mutual_recognition BOOLEAN, renewal_period_months INTEGER nullable, prerequisites JSONB DEFAULT '[]' (credential_type_id refs), industries JSONB DEFAULT '[]', verification_method VARCHAR (document|api|manual|self_declared), document_templates JSONB, metadata JSONB, inserted_at, updated_at, deleted_at — note NO org_id (platform-wide reference data per data.md)- Add
FinnestCompliance.CredentialTypeschema; allowlist for architecture tests (reference-data exception, no org_id) - Add
FinnestCompliance.CredentialCacheETS GenServer — loads credential types at boot + 6-hourly refresh; invalidated bycredential_type_updatedevent - Seed file
priv/repo/seeds/compliance_credential_types.exs— ~100 credentials per brainstorm-05 seed list: - Construction: white_card (General Construction Induction), EWP (Elevated Work Platform), telehandler, crane_licence (CB, CV, C0, C1, C2, C6, CN, CT), rigging_basic, rigging_intermediate, rigging_advanced, dogging, scaffolding_basic/intermediate/advanced, asbestos_awareness, asbestos_removal_class_A/B, confined_space, height_safety, site_induction_generic
- Mining: standard_11, coal_board_medical, drug_alcohol_clear, underground_induction_coal/metal, surface_induction, shot_firer, coal_mine_manager
- Logistics: driver_licence_C, driver_licence_LR, driver_licence_MR, driver_licence_HR, driver_licence_HC, driver_licence_MC, fatigue_management_bfm, fatigue_management_afm, dangerous_goods, forklift, reach_truck
- Defence: baseline_clearance, nv1, nv2, pv, disp_registration
- Retail: rsa, rcg, food_safety_level_½, barista_cert, cash_handling
- Traffic: traffic_controller_nsw/vic/qld, traffic_management_implementer, traffic_management_designer, road_safety_audit
- General (cross-industry): first_aid_provide, first_aid_hltaid011, cpr_hltaid009, manual_handling, working_at_heights, wwcc (Working With Children Check, per state), police_check_national, right_to_work_citizen/permanent_resident/visa
- Healthcare (future industry): ahpra_nursing, ahpra_midwifery, mandatory_reporter_training
- Hierarchical entries: e.g.
hrwl_forklifthasparent_id = hrwl_license(High Risk Work License top-level);rigging_advancedhasprerequisites = [rigging_intermediate];rigging_intermediatehasprerequisites = [rigging_basic] - FinnestCompliance.CredentialTypes context module:
get_by_code/1,list_by_industry/1,list_required_for/1,prerequisites_of/1(recursive) - Compliance.check/2 skeleton — the function signature + placeholder implementation returning
:compliantalways for now; real impl lands in Scout+Verify sprints when roster/timekeep gates are built - Architecture test
reference_data_no_org_id_test.exs:CredentialTypeis on allowlist (no org_id by design)
Out of scope:
- Industry profiles (F-019)
- Award rates / classifications (Migration Phase 4 / Scout+Verify sprints)
- Full
Compliance.check/2implementation with real rules (Scout+Verify sprints) - DVS integration (Migration Phase 2)
- Ongoing monitoring / re-screening (Migration Phase 2)
- Labour-hire licensing tracking (Migration Phase 3+)
Technical Notes¶
- Module namespace:
FinnestCompliance.*(flat), notFinnest.Compliance.*(dotted). Same Boundary constraint as F-003 (FinnestCore.*) and F-012 (FinnestAgents.*). Schema lives atFinnestCompliance.CredentialType; context atFinnestCompliance.CredentialTypes; cache atFinnestCompliance.CredentialCache; top-levelcheck/2atFinnestCompliance. - Seed format: individual
%CredentialType{}maps or SQL INSERT statements; prefer Elixir seed so Cloak/validation run - Run seed via
mix run priv/repo/seeds/compliance_credential_types.exs— idempotent (upsert bycode) - Hierarchy encoded via
parent_id— small enough to fit in memory; no need for materialized path / nested set issuing_jurisdictionsexample:["NSW", "VIC", "QLD", "SA", "WA", "TAS", "ACT", "NT"]for nationally recognised; single state for state-specificrenewal_period_months: nullmeans no expiry (e.g. White Card);renewal_period_months: 12for annual refresh (e.g. First Aid CPR, Working at Heights)- CredentialCache ETS: populated on boot from DB; refreshed every 6 hours or on
credential_type_updatedevent; miss path falls back to DB query - Real Australian credential catalogue — reference Safe Work Australia + state WorkSafe publications for accuracy. Don't invent codes.
Dependencies¶
- Blocked by: STORY-F-007 (architecture tests + reference-data allowlist pattern), STORY-F-008 (tenant enforcement — confirm reference-data allowlist works)
- Unblocks: F-019 (industry profiles reference credential_type_ids)
Acceptance Criteria¶
- Migration creates
compliance.credential_typeswith all fields per spec; reversible (CQ-11) -
FinnestCompliance.CredentialTypeschema compiles;Repo.all/2works (with reference-data allowlist so prepare_query doesn't require tenant context) - Seed script populates ≥100 credential types across all tracked industries
- Hierarchical structure verified:
hrwl_forklift.parent_id == hrwl_license.id; rigging chain (basic → intermediate → advanced) with prerequisites set -
CredentialTypes.get_by_code("white_card")returns the white_card record -
CredentialTypes.list_by_industry("construction")returns all construction-tagged credentials (≥10 entries) -
CredentialTypes.prerequisites_of("rigging_advanced")returns[rigging_intermediate, rigging_basic]transitively -
CredentialCacheloads on boot; cache hit <1ms vs DB lookup ~5-10ms -
FinnestCompliance.check/2callable; returns:compliant(placeholder) - Architecture test: CredentialType has no
org_idcolumn; allowlist confirms exception - Seed script idempotent: running twice produces same DB state
-
mix format,credo --strict,dialyzer,mix boundaryall green
Testing Requirements¶
- Unit: context functions (get_by_code, list_by_industry, prerequisites_of) happy + error paths
- Unit: CredentialCache boot + refresh + invalidation on event
- Integration: seed → DB → context query returns correct results
- Data audit: count per industry matches seed file expectations; all foreign-key prerequisites resolve
References¶
../architecture/data.md§compliance schema, §Reference Data vs Operational Data../brainstorms/brainstorm-05-multi-industry-design.md§Credential Registry, §Seed data by industry../brainstorms/brainstorm-11-traffio-laravel-migration-naming.md§Traffic Management Industry Profile../adrs/adr-011-F-compliance-auto-blocking.md- Safe Work Australia credential list (external): https://www.safeworkaustralia.gov.au/