K12 Implementation Guide
Internal guide for deploying, configuring, and operating the K12 Safety platform end-to-end. Covers the Azure Bicep template, K12-specific infrastructure, all three workflow families (EOP, QRG Generation, QRG Editing), database setup, and operational debugging.
Architecture
Azure Template: K12 Profile
The K12 deployment uses azure_template/profiles/k12.json, which configures a full-stack deployment including AI, compute, networking, and documentation hosting.
Profile Toggles
| Toggle | Value | Purpose |
|---|---|---|
deployAppService | true | Main API container |
deployFunctionApp | true | Async workflow processing |
deploySqlServer | true | K12SafetyDb (jobs, results, prompts) |
deployKeyVault | true | Secrets (MOEOP creds, APIM keys, OAuth) |
deployAiFoundry | true | LLM inference |
deployMultiRegionAiFoundry | true | Multi-region for quota resilience |
aiFoundryDeploymentTier | dual | Sweden Central + East US 2 |
deployContentSafety | true | Content moderation |
deployFormRecognizer | true | Document Intelligence |
deployApim | true | API gateway (JWT + subscription key) |
deployContentSafetyBlocklist | true | Blocklist management |
deployAiSearch | false | Not required for K12 |
deployIngestionServer | true | File ingestion (container-based) |
deployWidgetApp | true | AI chat widget |
deployDocsInternal | true | Internal Docusaurus SWA |
deployDocsExternal | true | External Docusaurus SWA |
deployDashboard | true | Admin dashboard |
deployVpnGateway | false | WireGuard VPN (enable if MOEOP requires VPN access) |
mastraTarget | k12 | Domain filter for agent/tool/workflow registration |
Deploy Command
# Development
az deployment sub create \
--location centralus \
--template-file azure_template/main.bicep \
--parameters @azure_template/environments/dev.bicepparam \
--parameters projectPrefix='k12' \
--parameters sqlAdminPassword='<password>'
# With K12 profile (via GitHub Actions deployment workflow)
# The workflow reads profiles/k12.json and passes all toggles automatically
Resource Naming Convention
All resources follow: {environment}-{projectPrefix}-{projectName}-{suffix}-{resourceType}
| Resource | Dev Name |
|---|---|
| App Service | dev-k12-aitools-t27p-app |
| Function App | dev-k12-aitools-t27p-func |
| SQL Server | dev-k12-aitools-t27p-sql |
| Key Vault | dev-k12-aitools-t27pkv |
| APIM | dev-k12-aitools-t27p-apim |
| ACR | devk12aitoolst27pacr |
| AI Foundry | dev-k12-aitools-t27p-ais-swe |
The t27p suffix is auto-generated during initial kickstart deployment for global uniqueness.
K12-Specific Key Vault Secrets
Beyond standard secrets, K12 requires MO-EOP integration credentials:
| Secret | Purpose |
|---|---|
moeop-internal-base-url | MO-EOP API base URL |
moeop-basic-auth-username | Basic auth username for MOEOP |
moeop-basic-auth-password | Basic auth password for MOEOP |
moeop-internal-username | Internal API username |
moeop-internal-password | Internal API password |
apim-subscription-key | APIM subscription key |
swa-docs-internal-deployment-token | Internal docs SWA deploy token |
swa-docs-deployment-token | External docs SWA deploy token |
K12-Specific App Settings
These are injected by the Bicep template into the App Service:
| Setting | Default | Description |
|---|---|---|
MOEOP_INTERNAL_BASE_URL | From KV | MO-EOP endpoint URL |
MOEOP_INTERNAL_TIMEOUT_MS | 10000 | Request timeout for MOEOP calls |
MOEOP_INTERNAL_AUTH_MODE | basic | Authentication mode |
K12_ANNEX_FORM_TYPE_IDS | '' | Form type filter (empty = all) |
QRG_SAVE_TO_K12 | true in dev/prod, false in test/staging | Enable K12 platform writes |
QRG_DEBUG | false | Verbose QRG pipeline logging |
MASTRA_TARGET | k12 | Domain scope for registered components |
WRITE_GUARD | unset | Write gate for external APIs (external) or all writes (all) |
QRG_SAVE_TO_K12 is enabled by infrastructure in dev and prod, and disabled in test and staging. When false, results are stored in the internal SQL database only.
Database: K12SafetyDb
The K12 platform uses a dedicated SQL database schema managed by DACPAC.
Schema Overview
K12Safety/
qrg_requests -- Job tracking (requestId, sessionId, status)
qrg_generations -- QRG generation results (resultPayloadJson)
qrg_edits -- QRG editing results (isEdit, answer, newEdition)
qrg_edit_context -- Editing context (organization, roles, enrichment)
qrg_request_prompts -- Prompt audit trail (system + user prompts)
log_Jobs -- Job lifecycle events
log_EOP_TaskRequests -- EOP request payloads
log_EOP_TaskStatus -- EOP status history
Deploy Database
# DACPAC deploy via GitHub Actions workflow
npm run db:deploy:k12
# Or manual via sqlpackage
sqlpackage /Action:Publish \
/SourceFile:db/sqlproj/Mastra.Platform.K12SafetyDb/bin/Release/Mastra.Platform.K12SafetyDb.dacpac \
/TargetConnectionString:"$CONNECTION_STRING"
Key Diagnostic Queries
-- Recent QRG generation jobs
SELECT TOP 20 requestId, sessionId, status, statusMessage, createdAt
FROM K12Safety.qrg_requests
ORDER BY createdAt DESC;
-- QRG generation results with AI stats
SELECT r.requestId, r.status, g.resultPayloadJson
FROM K12Safety.qrg_requests r
LEFT JOIN K12Safety.qrg_generations g ON r.id = g.requestId
WHERE r.requestId = '<request-id>';
-- QRG editing results
SELECT r.requestId, r.status, e.isEdit, e.answer
FROM K12Safety.qrg_requests r
LEFT JOIN K12Safety.qrg_edits e ON r.id = e.requestId
WHERE r.requestId = '<request-id>';
-- EOP editing job status
SELECT requestId, sessionId, status, response, createdAt
FROM K12Safety.log_EOP_TaskRequests
WHERE requestId = '<request-id>';
-- Prompt audit trail
SELECT requestId, userPrompt, systemPromptUsed, enrichmentJson
FROM K12Safety.qrg_request_prompts
WHERE requestId = '<request-id>';
Endpoints: Internal Architecture
Route Registration
K12 endpoints are registered via domain-based routing in packages/platform-runtime/src/server/routes/:
POST /ai/api/tools/{toolId}/execute -- Legacy tool execution (data wrapper)
POST /ai/k12/tools/{slug} -- Domain tool execution (direct JSON)
POST /ai/k12/workflows/{slug} -- Synchronous workflow
POST /ai/k12/workflows/{slug}/start-async -- Async workflow
GET /ai/k12/workflows/{slug}/runs/:runId -- Workflow run status
The APIM gateway proxies external requests to the /ai/api/tools/{toolId}/execute endpoint with JWT validation and subscription key enforcement.
Tool Registry
All K12 tools are registered when MASTRA_TARGET=k12 or MASTRA_TARGET=all:
External (APIM-facing):
| Tool ID | Source | Workflow |
|---|---|---|
k12-district-planning-kickoff | annex-editing-scenario-kickoffs.ts | k12-annex.workflow.ts |
k12-facility-planning-kickoff | annex-editing-scenario-kickoffs.ts | k12-annex.workflow.ts |
k12-functional-annex-kickoff | annex-editing-scenario-kickoffs.ts | k12-annex.workflow.ts |
k12-hazard-annex-kickoff | annex-editing-scenario-kickoffs.ts | k12-annex.workflow.ts |
k12-functional-annex-suggest-roles-kickoff | annex-editing-scenario-kickoffs.ts | k12-annex.workflow.ts |
k12-hazard-annex-suggest-roles-kickoff | annex-editing-scenario-kickoffs.ts | k12-annex.workflow.ts |
k12-annex-editing-kickoff | annex-editing-kickoff.ts | k12-annex.workflow.ts |
k12-annex-editing-status | Via task status tools | DB lookup |
k12-annex-editing-result | Via task result tools | DB lookup |
k12-qrg-generation-kickoff | qrg/qrg-generation-kickoff.ts | qrg-generation.workflow.ts |
k12-qrg-generation-status | qrg/qrg-status-tools.ts | DB lookup |
k12-qrg-generation-result | qrg/qrg-status-tools.ts | DB lookup |
k12-qrg-editing-kickoff | qrg/qrg-editing-kickoff.ts | qrg-editing.workflow.ts |
k12-qrg-editing-status | qrg/qrg-status-tools.ts | DB lookup |
k12-qrg-editing-result | qrg/qrg-status-tools.ts | DB lookup |
k12-cancel-task-request | cancel-task-request-tool.ts | DB update |
k12-qrg-cancel | qrg/qrg-status-tools.ts | DB update |
Internal (MCP/logging):
| Tool ID | Purpose |
|---|---|
k12-insert-task-request-log | Audit trail persistence |
k12-insert-task-in-monitoring-table | Monitoring row creation |
k12-update-task-status | Status update |
k12-update-task-request-response | Response persistence |
k12-get-emergency-operation-plan-snapshot | EOP snapshot from MOEOP |
Workflow Internals
EOP Editing (k12-annex.workflow.ts)
Key internals:
- The workflow runner (
annex-editing-workflow-runner.ts) manages async execution - System prompts are loaded from
Core.AgentProfileswith hash-based cache invalidation waitForResult: trueruns the workflow synchronously within the HTTP request- Request deduplication uses
requestIdas the unique key
QRG Generation (qrg-generation.workflow.ts)
Key internals:
- Each annex is processed independently (per-annex isolation)
- Role mapping batches when role list exceeds
ROLE_BATCH_SIZE(default 12, configurable viaK12_ROLE_BATCH_THRESHOLDenv var) - HTML stripping reduces form content by ~70% before AI processing
dryRun: truepersists to internal DB but skips K12 platform writes- AI stats track success/error counts per pipeline step
QRG Editing (qrg-editing.workflow.ts)
Key internals:
- v3:
requestIdis server-generated (not accepted from client) - No
emergencyOperationPlanIdrequired -- context comes fromquickReferenceGuide.organization - Always async (no
waitForResultoption) - System prompt includes organization context and QRG metadata
- Prompt auditing: full computed prompt stored in
qrg_request_prompts
Agent Configuration
K12 uses database-backed agent profiles for prompt management and LLM overrides.
K12 Agents
| Agent ID | Purpose | Profile Table |
|---|---|---|
k12-safety-agent | EOP editing (main agent) | Core.AgentProfiles |
k12-qrg-classifier | Annex category classification | Core.AgentProfiles |
k12-qrg-extractor | Audience group extraction | Core.AgentProfiles |
k12-qrg-mapper | Group-to-role mapping | Core.AgentProfiles |
k12-qrg-generator | QRG content generation | Core.AgentProfiles |
k12-qrg-editor | QRG section editing | Core.AgentProfiles |
Update Agent Prompts
-- View current profile
SELECT AgentId, SystemPrompt, LlmOverridesJson, ProfileHash, IsCurrent
FROM Core.AgentProfiles
WHERE AgentId = 'k12-safety-agent' AND IsCurrent = 1;
-- Agent automatically picks up changes via hash invalidation on next request
LLM Model Configuration
K12 agents default to the model configured in Core.LLMModelCapabilities. Override per-agent via LlmOverridesJson in the profile:
{
"deploymentName": "gpt-4o",
"temperature": 0.3,
"maxTokens": 4096
}
MOEOP Integration
The K12 platform integrates with MO-EOP (Michigan Office of Educational Opportunities) for Emergency Operation Plan data.
Connection
| Setting | Source |
|---|---|
| Base URL | Key Vault: moeop-internal-base-url |
| Auth Mode | basic (HTTP Basic Authentication) |
| Username | Key Vault: moeop-basic-auth-username |
| Password | Key Vault: moeop-basic-auth-password |
| Timeout | App Setting: MOEOP_INTERNAL_TIMEOUT_MS (default: 10s) |
Client Implementation
The MOEOP client (moeop-internal-client.ts) handles:
- EOP snapshot retrieval by plan ID
- Configuration retrieval by configuration ID
- Basic auth header construction
- Timeout enforcement
- Error classification (auth failure vs. network vs. not found)
VPN Access
If the MOEOP API is behind a private network, enable the VPN gateway:
// profiles/k12.json
{
"deployVpnGateway": true
}
This provisions a WireGuard VPN gateway VM that the App Service routes through for MOEOP requests.
CI/CD Workflow
K12 uses the shared GitHub Actions deployment infrastructure with K12-specific configuration.
Workflow Config
Location: .github/config/projects/k12.yml
Key variables:
| Variable | Value |
|---|---|
INFRA_PROJECT_PREFIX | k12 |
INFRA_PROFILE | k12 |
INFRA_LOCATION | centralus |
DOCS_BUILD_SCRIPT_INTERNAL | build:k12 |
DOCS_BUILD_SCRIPT_EXTERNAL | build:k12:external |
Pipeline Stages
- CI (
ci.yml): Lint, typecheck, build, test (filtered to K12) - CD (
cd.yml): Build container, push to ACR, deploy to App Service - DACPAC (
db-deploy.yml): Deploy K12SafetyDb schema changes - Docs (within CD): Build and deploy internal + external Docusaurus sites
Test Commands
npm run test:k12 # K12 unit tests
npm run typecheck:k12 # K12 type checking
npm run bruno:test:k12 # Bruno collection tests
Debugging
Enable Debug Logging
# QRG pipeline debug (verbose per-annex logging)
QRG_DEBUG=true
# Agent factory debug
MASTRA_DEBUG_AGENT_FACTORY=true
# Full LLM message logging (shows what reaches the model)
MASTRA_DEBUG_LLM_MESSAGES=true
Common Issues
| Symptom | Check | Fix |
|---|---|---|
All AI steps fail with PermissionDenied | AI Foundry RBAC | Verify managed identity has Cognitive Services OpenAI User role |
| MOEOP snapshot returns 401 | Key Vault secrets | Verify moeop-basic-auth-* secrets are correct |
| MOEOP snapshot times out | Network / VPN | Check MOEOP_INTERNAL_TIMEOUT_MS, verify VPN if required |
| QRGs not appearing in K12 platform | QRG_SAVE_TO_K12 | Must be true in dev/prod |
| Role mapping returns empty | EOP role tags | Verify EOP has role tags assigned in K12 platform |
| Agent uses wrong prompt | Profile hash | Check Core.AgentProfiles for stale IsCurrent rows |
Status stuck on processing | Workflow runner | Check App Insights for errors in background processing |
Application Insights Queries
// Recent QRG generation errors
traces
| where message contains "qrg" and severityLevel >= 3
| order by timestamp desc
| take 50
// MOEOP call latency
dependencies
| where name contains "moeop"
| summarize avg(duration), max(duration) by bin(timestamp, 5m)
// K12 tool execution times
requests
| where name contains "k12"
| summarize avg(duration), p95=percentile(duration, 95) by name
| order by avg_duration desc
Bruno Collections
Pre-built Bruno folders for testing K12 endpoints:
| Collection | Path |
|---|---|
| K12 Agents | bruno/20-agents-k12 |
| K12 Workflows | bruno/40-workflows-k12 |
| K12 Flows | bruno/60-flows-k12 |
Run with Bruno:
npm run bruno:test:k12
Environment Comparison
| Dev | Staging | Prod | |
|---|---|---|---|
| Resource Group | DEV-k12-aitools-rg | STAGING-k12-aitools-rg | PROD-k12-aitools-rg |
| App Service SKU | B2 | B2 | P0v3 |
| AI Regions | Sweden Central + East US 2 | Sweden Central + East US 2 | Sweden Central + East US 2 |
| Private Endpoints | No | Yes | Yes |
| Network Lockdown | No | Yes | Yes |
| Deployment Slots | No | No | Yes |
| QRG_SAVE_TO_K12 | true | false | true |
| APIM | Enabled | Enabled | Enabled |
Related Documentation
- EOP Workflow -- Detailed EOP editing workflow internals
- QRG Workflow -- QRG generation and editing workflow details
- QRG Generation Deep Dive -- 4-step AI pipeline technical reference
- Agents -- K12 agent configuration and prompt management
- Database: EOP -- EOP database schema
- Database: QRG -- QRG database schema
- Tools & Endpoints -- Complete tool registry
- Azure Infrastructure Overview -- Full infrastructure architecture
- Template Parameters -- Bicep parameter reference