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 deployment is performed by the environment/code-promotion workflows.
# Validate the local SQL project before dispatching a deployment.
dotnet build db/sqlproj/Mastra.Platform.K12SafetyDb/Mastra.Platform.K12SafetyDb.sqlproj
# 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/category target is isolated; partial target failures do not discard successful targets
QRG_GENERATION_CONCURRENCYdefaults to 4 and is clamped to 1-6 across a process-wide permit pool- Exact General User Tag Category IDs are authoritative; no free-text role mapping occurs
dryRun: truepersists to internal DB but skips K12 platform writes- Final stats track generated, skipped, failed, and external-save-failed targets
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 |
qrg-category-guide-generation | Category-first QRG recommendation and generation | Core.AgentProfiles |
qrg-category-guide-editor | Category-specific 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 the MOEOP Emergency Operation Plan services for snapshot, form, organization, role, and category context. Keep the acronym in operator guidance unless the upstream product owner supplies an official expanded name; historical pages have used inconsistent expansions.
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 |
| No category targets resolve | Compiled EOP categories | Verify the compiled snapshot/configuration exposes General User Tag Categories and any explicit category IDs are valid |
| 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