Skip to main content

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

ToggleValuePurpose
deployAppServicetrueMain API container
deployFunctionApptrueAsync workflow processing
deploySqlServertrueK12SafetyDb (jobs, results, prompts)
deployKeyVaulttrueSecrets (MOEOP creds, APIM keys, OAuth)
deployAiFoundrytrueLLM inference
deployMultiRegionAiFoundrytrueMulti-region for quota resilience
aiFoundryDeploymentTierdualSweden Central + East US 2
deployContentSafetytrueContent moderation
deployFormRecognizertrueDocument Intelligence
deployApimtrueAPI gateway (JWT + subscription key)
deployContentSafetyBlocklisttrueBlocklist management
deployAiSearchfalseNot required for K12
deployIngestionServertrueFile ingestion (container-based)
deployWidgetApptrueAI chat widget
deployDocsInternaltrueInternal Docusaurus SWA
deployDocsExternaltrueExternal Docusaurus SWA
deployDashboardtrueAdmin dashboard
deployVpnGatewayfalseWireGuard VPN (enable if MOEOP requires VPN access)
mastraTargetk12Domain 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}

ResourceDev Name
App Servicedev-k12-aitools-t27p-app
Function Appdev-k12-aitools-t27p-func
SQL Serverdev-k12-aitools-t27p-sql
Key Vaultdev-k12-aitools-t27pkv
APIMdev-k12-aitools-t27p-apim
ACRdevk12aitoolst27pacr
AI Foundrydev-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:

SecretPurpose
moeop-internal-base-urlMO-EOP API base URL
moeop-basic-auth-usernameBasic auth username for MOEOP
moeop-basic-auth-passwordBasic auth password for MOEOP
moeop-internal-usernameInternal API username
moeop-internal-passwordInternal API password
apim-subscription-keyAPIM subscription key
swa-docs-internal-deployment-tokenInternal docs SWA deploy token
swa-docs-deployment-tokenExternal docs SWA deploy token

K12-Specific App Settings

These are injected by the Bicep template into the App Service:

SettingDefaultDescription
MOEOP_INTERNAL_BASE_URLFrom KVMO-EOP endpoint URL
MOEOP_INTERNAL_TIMEOUT_MS10000Request timeout for MOEOP calls
MOEOP_INTERNAL_AUTH_MODEbasicAuthentication mode
K12_ANNEX_FORM_TYPE_IDS''Form type filter (empty = all)
QRG_SAVE_TO_K12true in dev/prod, false in test/stagingEnable K12 platform writes
QRG_DEBUGfalseVerbose QRG pipeline logging
MASTRA_TARGETk12Domain scope for registered components
WRITE_GUARDunsetWrite gate for external APIs (external) or all writes (all)
warning

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 IDSourceWorkflow
k12-district-planning-kickoffannex-editing-scenario-kickoffs.tsk12-annex.workflow.ts
k12-facility-planning-kickoffannex-editing-scenario-kickoffs.tsk12-annex.workflow.ts
k12-functional-annex-kickoffannex-editing-scenario-kickoffs.tsk12-annex.workflow.ts
k12-hazard-annex-kickoffannex-editing-scenario-kickoffs.tsk12-annex.workflow.ts
k12-functional-annex-suggest-roles-kickoffannex-editing-scenario-kickoffs.tsk12-annex.workflow.ts
k12-hazard-annex-suggest-roles-kickoffannex-editing-scenario-kickoffs.tsk12-annex.workflow.ts
k12-annex-editing-kickoffannex-editing-kickoff.tsk12-annex.workflow.ts
k12-annex-editing-statusVia task status toolsDB lookup
k12-annex-editing-resultVia task result toolsDB lookup
k12-qrg-generation-kickoffqrg/qrg-generation-kickoff.tsqrg-generation.workflow.ts
k12-qrg-generation-statusqrg/qrg-status-tools.tsDB lookup
k12-qrg-generation-resultqrg/qrg-status-tools.tsDB lookup
k12-qrg-editing-kickoffqrg/qrg-editing-kickoff.tsqrg-editing.workflow.ts
k12-qrg-editing-statusqrg/qrg-status-tools.tsDB lookup
k12-qrg-editing-resultqrg/qrg-status-tools.tsDB lookup
k12-cancel-task-requestcancel-task-request-tool.tsDB update
k12-qrg-cancelqrg/qrg-status-tools.tsDB update

Internal (MCP/logging):

Tool IDPurpose
k12-insert-task-request-logAudit trail persistence
k12-insert-task-in-monitoring-tableMonitoring row creation
k12-update-task-statusStatus update
k12-update-task-request-responseResponse persistence
k12-get-emergency-operation-plan-snapshotEOP 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.AgentProfiles with hash-based cache invalidation
  • waitForResult: true runs the workflow synchronously within the HTTP request
  • Request deduplication uses requestId as 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_CONCURRENCY defaults 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: true persists 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: requestId is server-generated (not accepted from client)
  • No emergencyOperationPlanId required -- context comes from quickReferenceGuide.organization
  • Always async (no waitForResult option)
  • 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 IDPurposeProfile Table
k12-safety-agentEOP editing (main agent)Core.AgentProfiles
qrg-category-guide-generationCategory-first QRG recommendation and generationCore.AgentProfiles
qrg-category-guide-editorCategory-specific QRG section editingCore.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

SettingSource
Base URLKey Vault: moeop-internal-base-url
Auth Modebasic (HTTP Basic Authentication)
UsernameKey Vault: moeop-basic-auth-username
PasswordKey Vault: moeop-basic-auth-password
TimeoutApp 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:

VariableValue
INFRA_PROJECT_PREFIXk12
INFRA_PROFILEk12
INFRA_LOCATIONcentralus
DOCS_BUILD_SCRIPT_INTERNALbuild:k12
DOCS_BUILD_SCRIPT_EXTERNALbuild:k12:external

Pipeline Stages

  1. CI (ci.yml): Lint, typecheck, build, test (filtered to K12)
  2. CD (cd.yml): Build container, push to ACR, deploy to App Service
  3. DACPAC (db-deploy.yml): Deploy K12SafetyDb schema changes
  4. 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

SymptomCheckFix
All AI steps fail with PermissionDeniedAI Foundry RBACVerify managed identity has Cognitive Services OpenAI User role
MOEOP snapshot returns 401Key Vault secretsVerify moeop-basic-auth-* secrets are correct
MOEOP snapshot times outNetwork / VPNCheck MOEOP_INTERNAL_TIMEOUT_MS, verify VPN if required
QRGs not appearing in K12 platformQRG_SAVE_TO_K12Must be true in dev/prod
No category targets resolveCompiled EOP categoriesVerify the compiled snapshot/configuration exposes General User Tag Categories and any explicit category IDs are valid
Agent uses wrong promptProfile hashCheck Core.AgentProfiles for stale IsCurrent rows
Status stuck on processingWorkflow runnerCheck 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:

CollectionPath
K12 Agentsbruno/20-agents-k12
K12 Workflowsbruno/40-workflows-k12
K12 Flowsbruno/60-flows-k12

Run with Bruno:

npm run bruno:test:k12

Environment Comparison

DevStagingProd
Resource GroupDEV-k12-aitools-rgSTAGING-k12-aitools-rgPROD-k12-aitools-rg
App Service SKUB2B2P0v3
AI RegionsSweden Central + East US 2Sweden Central + East US 2Sweden Central + East US 2
Private EndpointsNoYesYes
Network LockdownNoYesYes
Deployment SlotsNoNoYes
QRG_SAVE_TO_K12truefalsetrue
APIMEnabledEnabledEnabled