Files
ajarbot/.kiro/specs/child-safety-profile/requirements.md
Jordan Ramos 916f86725d feat: RSO observation system, child safety, Discord adapter, Telegram watchdog, email attachments
Core agent improvements:
- RSO (Relevance Scoring & Observation) system: interaction_logger, memory_scorer, signal_detector
- Memory access logging (memory_access_log table) for relevance scoring; high-signal turn detection
- Rich conversation storage for notable turns; compact_conversation truncates long user messages
- Task-type classifier (query/action/analysis/creative) for observation tagging
- Nested sub-agent visibility: deep delegations now register against the main agent's manager

Child safety (Gabriel profile):
- child_safety.py: filtering, audit logging, prompt constants for restricted sessions
- .kiro/specs/child-safety-profile: requirements, design, tasks specs
- GABRIEL_BOT_PROPOSAL.md: initial proposal doc
- Reduced context window (10 msgs) and tutor-mode identity for restricted users

Telegram adapter:
- Polling watchdog: auto-restarts updater if polling drops unexpectedly
- get_me() with exponential-backoff retry on NetworkError at startup
- Correct stop() ordering: signal watchdog before cancelling tasks

Email / Gmail:
- send_email: supports file attachments (attachments list param)
- get_email: surfaces attachment metadata in response

Scheduled tasks / weather:
- Remove OpenWeatherMap API calls from morning-weather task; use wttr.in exclusively
- New scheduled tasks and scheduler state persistence

Discord:
- adapters/discord/__init__.py scaffold
- discord-plugin: MCP plugin for Claude Code Discord integration (server.ts, skills, config)

Infrastructure:
- n8n workflow exports (garvis_webhook, content_pipeline variants)
- memory_workspace: context, homelab-repo-updates, weekly observation summaries, error logs
- UCS C240 migration plan doc
- requirements.txt: new deps
- .claude/settings.json, fix_hooks.py: hook/permission tuning
2026-04-23 07:54:01 -06:00

339 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Requirements — Child Safety Profile
## Overview
Add a restricted child user profile to Ajarbot that allows a 13-year-old to use the bot as an
educational and creative tool — focused on gaming, Lua scripting, and Roblox Studio — while
preventing access to age-inappropriate content. Parents retain full oversight via an audit log.
---
## User Stories
### REQ-01 — Child User Access
**As a parent**, I want to add my gabriel as an allowed user on Slack so he can interact with
the bot using his own account.
**Acceptance Criteria:**
- His Slack user ID is mapped to a named username (e.g., `gabriel`) in `adapters.local.yaml`
- His username appears in the `allowed_users` list
- He can send messages and receive responses through the existing Slack adapter
- His session is isolated from the parent's session (separate conversation history)
---
### REQ-02 — Age-Appropriate System Persona
**As a parent**, I want the bot to behave differently for my gabriel — patient, educational, and
enthusiastic about game dev — rather than presenting the full Garvis homelab persona.
**Acceptance Criteria:**
- Child users receive a modified system prompt that replaces homelab/admin context with
an educational game-dev tutor persona
- Tone is encouraging, uses simple language, avoids jargon where possible
- References to SSH, Proxmox, home network, or admin tooling are suppressed for child users
- Son's profile (`memory_workspace/users/gabriel.md`) captures his interests, age, and learning style
---
### REQ-03 — Context-Aware Content Filtering (Input)
**As a parent**, I want the bot to block genuinely harmful requests without false-positiving
on legitimate game development questions that use words like "shoot", "kill", "weapon", or "knife"
in a coding/game context.
**Acceptance Criteria:**
- A preprocessor runs on every inbound message from a child user before it reaches the LLM
- The preprocessor uses **intent patterns**, not keyword matching — a block requires both a
harm verb and a real-world target/context
- Game development context signals (e.g., `in my game`, `roblox`, `lua`, `script`, `code`,
`function`, `NPC`, `hitbox`) exempt a message from weapon/violence keyword blocks
- The following are always blocked regardless of context:
- Real-world harm instructions ("how do I hurt/stab/shoot a person")
- Requests for actual weapon construction
- Sexual or explicit content
- Social engineering or personal data requests
- Content with no plausible game/educational framing
- Blocked messages receive a friendly, non-alarming response explaining the bot can't help
with that topic
- The following are always allowed regardless of words used:
- Lua scripting and Roblox Studio mechanics
- Horror game design (atmosphere, enemy AI, damage systems, jump scares)
- Game weapon mechanics, hitboxes, damage values, animations
- General coding help (Python, JavaScript basics)
- School subjects, creative writing, general knowledge
---
### REQ-04 — Context-Aware Content Filtering (Output)
**As a parent**, I want a secondary check on what the bot sends back so that even if the LLM
produces something borderline, it is caught before delivery.
**Acceptance Criteria:**
- A postprocessor scans every outgoing response to a child user
- Detects and replaces responses that contain explicit language, adult content, or real-world
harm instructions that slipped through the system prompt
- If a response is flagged, a safe fallback message is sent and the event is logged
- Clean responses pass through unmodified with zero added latency beyond the scan
---
### REQ-05 — System Prompt Guardrails
**As a parent**, I want the LLM itself to understand the rules so it handles gray-area
questions correctly without requiring every edge case to be coded explicitly.
**Acceptance Criteria:**
- Child users receive a guardrail block appended to their system prompt on every turn
- The guardrail block explicitly tells the LLM:
- Game dev / horror game design / weapon mechanics in a game context = encouraged
- Real-world harm, adult content, explicit language = refuse politely
- If unsure, treat the question as game/educational context if any signal supports it
- The guardrail block is injected in `_build_system_prompt()` when the username is in the
configured `RESTRICTED_USERS` list
---
### REQ-06 — Tool Restrictions
**As a parent**, I want my gabriel to be unable to trigger homelab tools, SSH commands, file
system operations, or admin-level actions even if he asks.
**Acceptance Criteria:**
- System prompt for child users instructs the LLM never to use SSH, file system, Proxmox,
network, or infrastructure tools
- This is enforced at the system prompt level (model instruction), not by removing MCP servers
- Tool invocations from child users that attempt admin tooling are logged as anomalies
---
### REQ-07 — Parental Audit Log
**As a parent**, I want a complete, searchable record of every conversation my gabriel has with
the bot so I can review what he's been asking and what the bot responded.
**Acceptance Criteria:**
- Every interaction from a child user is written to a dedicated audit log, separate from
the RSO/memory-scoring logs
- Audit log location: `memory_workspace/audit/{username}/YYYY-MM-DD.jsonl`
- Each audit entry contains:
- ISO timestamp
- Username
- Full inbound message (not truncated)
- Filter action taken (allowed / blocked / flagged)
- Filter reason (if blocked/flagged)
- Full outbound response
- Audit writes are non-blocking (background thread, same pattern as InteractionLogger)
- Audit log retention: 365 days (configurable)
- Existing RSO interaction logs are not modified — audit log is additive
---
### REQ-08 — Configuration-Driven Restricted Users
**As a parent**, I want the child safety features to be controlled by config, not hardcoded,
so I can add or remove restricted users without modifying Python source.
**Acceptance Criteria:**
- A `child_safety` block in `config/adapters.local.yaml` defines which usernames are restricted
- Example:
```yaml
child_safety:
restricted_users:
- gabriel
audit_retention_days: 365
```
- The `child_safety.py` module reads this config at startup
- Adding a new restricted user requires only a config change and bot restart
---
## Non-Functional Requirements
| ID | Requirement |
|----|-------------|
| NFR-01 | Filtering must add < 50ms latency to message processing |
| NFR-02 | Audit log writes must never block response delivery |
| NFR-03 | A filter failure (exception) must fail safe — block the message, not pass it |
| NFR-04 | Audit log files must not be accessible via any bot tool or command |
| NFR-05 | Restricted user config must survive bot restarts |
| NFR-06 | False positive rate on game dev questions must be near zero for common Roblox/Lua vocabulary |
---
### REQ-09 — Guided Learning Approach (Skill Development Over Answer Delivery)
**As a parent**, I want the bot to teach my gabriel how to think and build, not just hand him
answers — so that he develops real coding and problem-solving skills over time rather than
becoming dependent on the bot.
**Acceptance Criteria:**
- The bot's default mode is **guide first, answer second** — not the reverse
- Before giving a solution, the bot asks what the user has already tried or what they think
might work, unless the question is purely factual ("what does `pairs()` do in Lua?")
- When code is provided, it is **always accompanied by an explanation** of what it does and
why — never a bare code block with no context
- Explanations use the **minimum necessary detail** for his age/level — short, plain-language
sentences before diving into code
- The bot breaks problems into **smaller steps** and guides through each one rather than
solving the whole thing at once:
- "Let's tackle the shooting mechanic first. What do you think needs to happen when the
player pulls the trigger?"
- The bot **celebrates attempts and effort**, not just correct answers:
- "Nice — you got the loop right, that's the hard part. The issue is just this one line..."
- When the user shares broken code, the bot **guides them to find the bug** rather than
pointing straight to it:
- "Take a look at line 12 — what do you think that variable is at that point in the loop?"
- After giving code, the bot **leaves something for the user to do**:
- "I've written the basic function — can you add the part that checks if the player has
enough ammo before it fires?"
- The bot periodically uses **transfer learning** to connect new concepts to ones already
covered:
- "Remember the loop we used for the enemy spawner? This is the same idea."
- Code IS shown when asked — this is not a Socratic-only mode. The teaching layer wraps
the code, it does not replace it.
- Purely factual or lookup questions ("what's the Roblox service for detecting player input?")
get a direct answer — no forced Socratic preamble for simple lookups.
---
### REQ-10 — Token Optimization for Child Sessions
**As a parent**, I want Gabriel's sessions to consume as few tokens as possible since he shares
the same API pool as me, without degrading the quality of his experience.
**Acceptance Criteria:**
- Gabriel's system prompt **skips SOUL.md** (the Garvis homelab persona) — irrelevant to him,
currently costs ~935 tokens per turn
- Gabriel's system prompt **skips context.md** (SSH hosts, Proxmox VMs, networking) — entirely
irrelevant to Lua help, currently costs ~227 tokens per turn
- Gabriel's system prompt uses a **lightweight tutor identity block** (~100 tokens) in place
of SOUL.md — enough to set tone without the homelab baggage
- The **hybrid memory search is skipped** for Gabriel — the memory store is Jordan's homelab
operational data and returns irrelevant chunks that waste tokens
- Gabriel's **conversation history window is capped at 10 messages** (vs Jordan's 20) — Lua
help sessions rarely need deep context; this roughly halves history token cost
- The **delegation/sub-agent block** is omitted from Gabriel's system prompt — he will never
trigger multi-agent tasks (~80 tokens saved)
- All optimizations are conditional on `is_restricted(username)` — Jordan's experience is
completely unchanged
**Expected savings per Gabriel turn:**
| Removed component | Token saving |
|---|---|
| SOUL.md | ~935 |
| context.md | ~227 |
| Memory search (5 chunks avg) | ~300500 |
| History window 20→10 (avg) | ~2050% of history |
| Delegation block | ~80 |
| **Total** | **~1,5001,800 tokens/turn** |
---
### REQ-11 — AI Literacy as Part of the Teaching Approach
**As a parent**, I want the bot to teach Gabriel how to use AI tools well — not just what to
ask, but how to ask — so he builds self-sufficiency with these tools rather than dependency.
**Acceptance Criteria:**
- When Gabriel asks a vague or broad question, the bot **models good question-asking** by
clarifying its understanding before answering:
> "Just to make sure I give you the most useful answer — you want the enemy to deal damage
> on touch, right? Or is it supposed to chase first?"
- When Gabriel notices the bot "forgot" something earlier, the bot **explains context windows**
in plain terms, naturally:
> "Yeah — I can only hold so much of our conversation in memory at once. At the start of
> next session, just remind me what you're building and I'll be straight back up to speed."
- The bot **teaches the ideal coding question format** when the opportunity arises naturally:
> "Next time try: what your code does now, what you want it to do, and what you've already
> tried. That combo gets you a much faster answer."
- The bot **flags its own assumptions** so Gabriel learns to spot ambiguity:
> "I'm assuming you want this to reset on respawn — let me know if that's not right."
- AI literacy is woven into responses naturally — never a separate lecture unless Gabriel
directly asks how the bot works.
---
### REQ-12 — Cross-Session Project Continuity
**As a parent**, I want the bot to remember what Gabriel is building between sessions so he
doesn't have to re-explain his project every time, and the teaching approach stays coherent
over days and weeks — not just within a single conversation.
**Acceptance Criteria:**
- A lightweight project context file exists at `memory_workspace/users/gabriel_context.md`
- This file is injected into Gabriel's system prompt on every turn (replaces memory search,
which is skipped for Gabriel per REQ-10)
- The bot updates `gabriel_context.md` at the end of each session with a brief summary of:
- What Gabriel is currently building (project name/description)
- What was worked on in this session (features, bugs fixed, concepts covered)
- Any open threads or "next steps" Gabriel mentioned
- Any new concepts introduced this session (feeds into REQ-13)
- The update is concise — target < 30 lines total; the file is overwritten, not appended
- On first session (file doesn't exist), the bot starts fresh and creates it after the
first substantive exchange
- The file is human-readable so Jordan can review it directly in Slack's file system or
the memory workspace
---
### REQ-13 — Skill Progression Tracking
**As a parent**, I want the bot to remember what Gabriel has already been taught so it doesn't
re-explain concepts he's mastered, and can reference them naturally when introducing related ideas.
**Acceptance Criteria:**
- A skills log section exists within `gabriel_context.md` (same file as REQ-12, separate section)
- Each entry records: concept name, brief description, date first introduced
- Example entries:
- `for loops` — iterating over tables, introduced 2026-04-21
- `functions` — defining and calling, parameters vs arguments, introduced 2026-04-22
- `RemoteEvents` — client-server communication in Roblox, introduced 2026-04-25
- The bot checks this log before explaining a concept — if already introduced, it references
it rather than re-explaining from scratch:
> "You've used this before — remember the loop we wrote for the enemy spawner?"
- The log grows over time; the bot adds an entry the first time it meaningfully teaches a new
concept, not for every mention
- Skills log is appended to `gabriel_context.md` under a `## Skills Introduced` heading
---
### REQ-14 — First-Run Onboarding Experience
**As a parent**, I want Gabriel to receive a friendly welcome the first time he messages the
bot that sets expectations — what it can help him with, how it works, and that it's there
to teach him, not do the work for him.
**Acceptance Criteria:**
- The bot detects a first-run state by checking whether `gabriel_context.md` exists
- On first message from Gabriel, before processing his question, the bot sends a welcome
message that covers:
- What it can help with (Lua, Roblox Studio, game design, coding questions)
- How the teaching approach works — that it'll guide him and ask questions, not just
hand over answers ("I'm here to help you figure it out, not just give you the answer")
- That it'll remember his projects between sessions
- An invitation to tell it what he's working on
- The welcome is sent as a separate message before the response to his first question
- The welcome is conversational and age-appropriate — not a terms-and-conditions wall
- After the welcome, his first actual question is answered normally
- The first-run check only fires once; subsequent sessions go straight to his question
---
### REQ-15 — Slack Allow-List (Gap Fix)
**As a parent**, I want only authorised users to be able to message the bot on Slack, since
the Slack adapter currently processes messages from any workspace member with no restriction.
**Acceptance Criteria:**
- The Slack adapter checks an `allowed_users` list from config before processing any message
- Messages from users not on the allow-list are silently dropped (no response sent)
- The allow-list is read from `config/adapters.local.yaml` under the slack adapter settings,
matching the pattern already used by the Slack adapter for other config
- Jordan's existing Slack user ID remains on the list; Gabriel's is added
- No change to Telegram adapter behaviour (already has this check)
---
## Out of Scope
- Time-of-day restrictions (not enforceable at bot level — use Slack parental controls)
- Per-topic whitelists managed via chat commands
- Automated parent notifications on blocked requests (future enhancement)
- Web dashboard for audit log review (future enhancement)