#!/bin/bash # test-onboarding-parity.sh — K4 (privat-sektor-paritet) regresjonsvakt for onboarding. # # Låser audit §5 punkt #1 (devils-advocate-audit-2026-06-18.md:167): en privat # virksomhet skal kunne fullføre onboarding UTEN å velge «Annet» på sektor, med en # relevant (privat) reguleringsliste, og uten å bli spurt rene offentlig-sektor-lover. # # Onboarding-intervjuet er interaktivt (AskUserQuestion) → ingen fixture-fangst som de # andre output-testene. Vi asserterer derfor mot KILDEN (agents/onboarding-agent.md + # commands/onboard.md): forgreningen og de private alternativene må være til stede. set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" source "$SCRIPT_DIR/lib/e2e-helpers.sh" PLUGIN_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" # Tillat override (rød-fase-diskrimineringsbevis kjører testen mot en strippet kopi). AGENT="${ONBOARDING_AGENT_FILE:-$PLUGIN_ROOT/agents/onboarding-agent.md}" ORCH="${ONBOARDING_ORCH_FILE:-$PLUGIN_ROOT/commands/onboard.md}" if [ ! -f "$AGENT" ]; then echo "ERROR: Onboarding agent not found: $AGENT" exit 1 fi init_suite "Onboarding-agent kildesjekk (K4-paritet + Phase 6 guided-prose)" # === Forgrening: sektortype velges først og styrer oppfølgingen === assert_matches_pattern "$AGENT" "Sektortype.*Offentlig sektor, Privat sektor" \ "Sektortype forgrener offentlig/privat på toppnivå" assert_matches_pattern "$AGENT" "forgren intervjuet" \ "Eksplisitt instruks om å forgrene intervjuet på sektortype" # === K4 kjerne: privat virksomhet trenger IKKE velge «Annet» på sektor === # Den private sektor-greinen må liste navngitte private sektorer. assert_matches_pattern "$AGENT" "Hvis Privat sektor.*Finans/bank/forsikring" \ "Privat sektor-grein lister Finans/bank/forsikring (bank slipper «Annet»)" for sektor in "Industri/produksjon" "Handel" "Energi" "Telekom"; do assert_matches_pattern "$AGENT" "Hvis Privat sektor.*${sektor}" \ "Privat sektor-grein lister ${sektor}" done # === K4 kjerne: relevant privat reguleringsliste === for reg in "DORA" "Finansforetaksloven" "Finanstilsynets IKT-forskrift" \ "Verdipapirhandelloven" "Hvitvaskingsloven"; do assert_matches_pattern "$AGENT" "$reg" \ "Privat reg-sett inneholder ${reg}" done # === Forgrening verner private mot rene offentlig-sektor-lover === assert_matches_pattern "$AGENT" "ALDRI private virksomheter om Offentleglova" \ "Private spørres ALDRI Offentleglova/Arkivloven/Forvaltningsloven" # === Spor E krav 2: Phase 6 guided-prose (coaching) === # Operatør-direktiv S39 (memory: onboarding-quality-bar): fri-kontekst-fasen skal # ikke bare stille ÉtT blankt spørsmål — den skal COACHE brukeren på hva som typisk # hører hjemme og hva som trygt kan hoppes over, så fri-konteksten faktisk gjør # rådene mer treffsikre (multiplikator-effekten). Vi asserterer mot agent-kilden. assert_matches_pattern "$AGENT" "coache? brukeren" \ "Phase 6 instruerer agenten om å coache brukeren (ikke bare stille blankt spørsmål)" assert_matches_pattern "$AGENT" "[Pp]resenter de to listene" \ "Phase 6 ber agenten presentere coaching-listene for brukeren" assert_matches_pattern "$AGENT" "typisk hører hjemme" \ "Phase 6 lister hva som typisk bør med (inkluderings-coaching)" assert_matches_pattern "$AGENT" "vanligvis kan hoppe over" \ "Phase 6 lister hva som vanligvis kan hoppes over (ekskluderings-coaching)" assert_matches_pattern "$AGENT" "allerede.*fanget i de fem strukturerte kategori" \ "Phase 6 ber bruker ikke gjenta det de fem strukturerte kategoriene alt fanget" assert_matches_pattern "$AGENT" "(secrets|sensitive personopplysninger).*ALDRI" \ "Phase 6 advarer mot å lime inn secrets/sensitive personopplysninger" # Regresjon: fri-konteksten lagres fortsatt ordrett (ikke omskrevet/forkortet) assert_matches_pattern "$AGENT" "ordrett" \ "Phase 6 lagrer fortsatt brukerens prosa ordrett (regresjonsvakt)" # === Spor E (E-polish): per-kategori prosa-hint Phase 1–5 (speilet på Phase 6-coachingen) === # De fem strukturerte fasene bruker AskUserQuestion, men skal IKKE fyres av blankt: hver fase # skal gi et kort «typisk relevant / sjelden relevant»-hint slik at agenten coacher brukeren mot # riktig alternativ — samme multiplikator-tanke som Phase 6, komprimert til strukturerte valg. # ASCII-only anker (ingen em-dash/gåseøyne i shell-var → bash 3.2 + set -u-trygt). HINT_ANCHOR='typisk relevant / sjelden relevant' hint_count=$(grep -cF "$HINT_ANCHOR" "$AGENT" || true) if [ "${hint_count:-0}" -ge 5 ]; then pass "Alle 5 strukturerte faser har et per-kategori typisk/sjelden-hint ($hint_count funnet)" else fail "Per-kategori hint mangler — fant kun ${hint_count:-0}/5 hint-ankere" fi # Agenten skal faktisk DELE hintet med brukeren (speiler Phase 6 «Presenter de to listene ... FØR») assert_matches_pattern "$AGENT" "Del med brukeren" \ "Per-kategori hint instrueres delt med brukeren før spørsmålene" # Begge framing-ordene finnes (inkludering + ekskludering, som Phase 6s to lister) assert_matches_pattern "$AGENT" "typisk relevant" \ "Hint-framing inneholder «typisk relevant» (inkludering)" assert_matches_pattern "$AGENT" "[Ss]jelden relevant" \ "Hint-framing inneholder «sjelden relevant» (ekskludering)" # === Orkestratoren er sektor-nøytral (delegerer, hardkoder ikke offentlig) === if [ -f "$ORCH" ]; then if grep -qE "Onboarding-agenten styrer selve intervjuet" "$ORCH"; then pass "Orkestrator delegerer intervjuet til agenten (sektor-nøytral)" else warn "Orkestrator: fant ikke delegerings-note (sjekk onboard.md)" fi fi print_summary