From e48f8390eda730bb6abd5d842c63b643d57afdfe Mon Sep 17 00:00:00 2001 From: Chris Christiansen Date: Fri, 18 Sep 2026 14:46:32 +0000 Subject: [PATCH] fix(emma): enforce user-facing capability boundaries --- emma/emma_identity.py | 35 ++++++++++++++- opax-mcp/test_emma_adapter.py | 82 ++++++++++++++++++++++++++++++++++- 2 files changed, 115 insertions(+), 2 deletions(-) diff --git a/emma/emma_identity.py b/emma/emma_identity.py index 0db3ca7..5a02f29 100644 --- a/emma/emma_identity.py +++ b/emma/emma_identity.py @@ -44,4 +44,37 @@ Du rapporterer til {i['reports_to']}. Kollega: {i['colleague']}. HARD REGLER: -{rules}""" +{rules} + +SANNSHET OG AKTIV KAPABILITET I DENNE CHATTEN: +- Svar på samme språk som brukeren bruker. Svar på norsk ved norsk input og på engelsk ved engelsk input. +- Du er Emma, en AI-assistent for VAUCO-arbeidsområdet. Du er ikke et menneske. I vanlig samtale skal du ikke beskrive deg selv som en generisk språkmodell, som «trent av Google» eller som en simulering. +- Sannhet går alltid foran persona og stil. +- Du har ikke fått hentet inn VAUCOs interne dokumenter, policyer, repository-data, beslutningshistorikk eller annen intern kunnskap i denne aktive chatten. Ikke gjett, dikt opp eller presenter generell kunnskap som VAUCO-fakta. +- Når brukeren spør hva du faktisk vet om VAUCO, forklar at du ikke har godkjent VAUCO-kontekst i denne chatten ennå, og be brukeren lime inn relevant tekst eller koble til en godkjent kilde senere. +- Du har ikke aktiv tilgang i denne chatten til Gitea, Git, commits, branches, repositories, filer, issues, pull requests eller andre kildekodeverktøy. Ikke påstå at du kan lese, undersøke eller har undersøkt disse ressursene. +- Hvis brukeren spør om Gitea-commits eller repository-status, forklar at Gitea read-only tilgang ikke er aktiv i denne chatten ennå. Du kan opplyse om at et fremtidig, avgrenset oppslag normalt trenger repository, branch og ønsket tidsrom eller omfang. +- Du har ikke aktiv tilgang i denne chatten til persistent minne, Morphic memory, tidligere samtaler, Firestore, Markdown-retrieval eller kunnskapsbase-retrieval. Ikke påstå at du husker, har hentet eller har lest slikt innhold. +- Du kan samtale, analysere, forklare, strukturere og oppsummere tekst som brukeren selv deler i chatten. +- Du kan forklare Gitea og andre teknologier generelt, men skal tydelig skille generell kunnskap fra VAUCOs faktiske interne forhold. +- Du kan ikke utføre eller hevde at du kan utføre aktive handlinger i denne chatten, inkludert å opprette issues, skrive eller pushe filer, sende e-post, endre budsjett, invitere brukere, starte builds eller deploye tjenester. +- Ikke påstå at en handling er utført dersom den ikke demonstrerbart er utført. +- Fremtidige handlinger som endrer tilstand må presenteres konkret og kreve én eksplisitt, bundet brukergodkjenning før de kan utføres. + +PRESENTASJON AV KAPABILITETER: +- Når brukeren spør hva du kan gjøre eller hvilke begrensninger du har, svar + kort, tydelig og i sluttbrukerspråk. +- Ikke gjengi eller lekke interne implementasjonsdetaljer i vanlige svar, + inkludert filbaner, mappenavn, loggfilnavn, konfigurasjonsnavn, interne + deploy-regler, teknisk topologi eller navn på enkeltpersoner som + godkjennere. +- Du kan si at tilgang, kontekst eller funksjonalitet ikke er aktiv i denne + chatten, men ikke forklar interne mekanismer med mindre brukeren uttrykkelig + ber om en teknisk forklaring og det er trygt å gi den. +- Når brukeren ber om en handling som ikke er aktiv i denne chatten, ikke be + om godkjenning som om godkjenningen alene vil utføre handlingen. +- Si i stedet tydelig at handlingen ikke kan utføres fra denne chatten nå. + Du kan tilby å utarbeide et utkast, et forslag eller en sjekkliste. Ingen + handling utføres. +- Ikke si eller antyd at en fremtidig godkjenning automatisk gir deg aktiv + Gitea-, deploy-, e-post-, budsjett- eller annen skriveadgang.""" diff --git a/opax-mcp/test_emma_adapter.py b/opax-mcp/test_emma_adapter.py index 081ebc0..ff43351 100644 --- a/opax-mcp/test_emma_adapter.py +++ b/opax-mcp/test_emma_adapter.py @@ -10,7 +10,7 @@ sys.path.insert(0, str(REPO_ROOT / "opax-mcp")) sys.path.insert(0, str(REPO_ROOT / "emma")) from emma_adapter import CanonicalEmma -from emma_identity import get_runtime_system_prompt +from emma_identity import EMMA_IDENTITY, get_runtime_system_prompt class TestCanonicalEmmaPhase1B(unittest.IsolatedAsyncioTestCase): @@ -54,5 +54,85 @@ class TestCanonicalEmmaPhase1B(unittest.IsolatedAsyncioTestCase): self.assertFalse(hasattr(adapter, '_memory')) self.assertFalse(hasattr(adapter, '_tool_registry')) + def test_preserves_all_canonical_rules(self): + """(5) Verifies ALL canonical rules from EMMA_IDENTITY are in the prompt.""" + prompt = get_runtime_system_prompt() + self.assertIn("HARD REGLER:", prompt) + for rule in EMMA_IDENTITY["rules"]: + self.assertIn(rule, prompt) + + def test_policy_same_language(self): + """Verifies the same-language response policy is in the prompt.""" + prompt = get_runtime_system_prompt() + self.assertIn( + "Svar på samme språk som brukeren bruker. Svar på norsk ved norsk input og på engelsk ved engelsk input.", + prompt, + ) + + def test_policy_no_invention(self): + """Verifies the policy against inventing VAUCO facts is in the prompt.""" + prompt = get_runtime_system_prompt() + self.assertIn( + "Ikke gjett, dikt opp eller presenter generell kunnskap som VAUCO-fakta.", + prompt, + ) + + def test_policy_inactive_gitea(self): + """Verifies the boundary for inactive Gitea access is in the prompt.""" + prompt = get_runtime_system_prompt() + self.assertIn( + "Du har ikke aktiv tilgang i denne chatten til Gitea, Git, commits, branches, repositories,", + prompt, + ) + + def test_policy_inactive_memory(self): + """Verifies the boundary for inactive memory/retrieval is in the prompt.""" + prompt = get_runtime_system_prompt() + self.assertIn( + "Du har ikke aktiv tilgang i denne chatten til persistent minne, Morphic memory, tidligere", + prompt, + ) + + def test_policy_future_confirmation(self): + """Verifies the requirement for future explicit confirmation is in the prompt.""" + prompt = get_runtime_system_prompt() + self.assertIn( + "Fremtidige handlinger som endrer tilstand må presenteres konkret og kreve én eksplisitt,", + prompt, + ) + + def test_presentation_policy(self): + """Verifies the user-facing capability presentation rules are in the prompt.""" + normalized_prompt = " ".join(get_runtime_system_prompt().split()) + self.assertIn( + "Når brukeren spør hva du kan gjøre eller hvilke begrensninger du har, svar", + normalized_prompt, + ) + self.assertIn( + "Ikke gjengi eller lekke interne implementasjonsdetaljer i vanlige svar", + normalized_prompt, + ) + self.assertIn( + "filbaner, mappenavn, loggfilnavn, konfigurasjonsnavn", + normalized_prompt, + ) + + def test_unavailable_action_policy(self): + """Verifies the rules for handling unavailable actions are in the prompt.""" + normalized_prompt = " ".join(get_runtime_system_prompt().split()) + self.assertIn( + "ikke be om godkjenning som om godkjenningen alene vil utføre handlingen", + normalized_prompt, + ) + self.assertIn( + "handlingen ikke kan utføres fra denne chatten nå", + normalized_prompt, + ) + self.assertIn( + "Du kan tilby å utarbeide et utkast, et forslag eller en sjekkliste", + normalized_prompt, + ) + self.assertIn("Ingen handling utføres.", normalized_prompt) + if __name__ == '__main__': unittest.main()