System QA Checks
The System QA Checks module is the cross-cutting backstop of Amal’s safety architecture. It maintains a versioned registry of 32 named checks, routes runtime failures deterministically, and exposes three read endpoints for monitoring, without duplicating a single line of enforcement logic from the engine modules that already own it.
What this module does and does not do
This module registers and routes. It does not re-implement. Every check’s enforcement lives in the module that owns the rule (the skill-status engine, the cluster module, the bundle catalog, the language-safety layer, etc.). The QA module provides a single place to discover what checks exist, what their severity is, and when they fired at runtime.
The three responsibilities:
- Register: a versioned, publish-then-freeze config table (
qa_check_definition, 32 rows, global with no tenant scoping) describes every check: its ID (QA_001..QA_032), the CI lint command that enforces it, a human-readable failure message in Arabic, and its severity. - Route: when an engine module detects a violation, it calls
routeQaFailure({ qaCheckId }). The router looks up the check’s severity and applies the deterministic routing matrix. - Read: three admin/eng-only HTTP endpoints expose the registry and the runtime alert log.
The registry (32 checks)
The 32 checks cover the major cross-cutting categories:
| Category | Examples |
|---|---|
| Data sufficiency | QA_001 Tie Rule / do_not_decide_yet guard |
| Acute regression | QA_002 ≥20% drop triggers review, never auto-fail |
| Bulk activation safety | QA_003 DATA_INCOMPLETE exclusion, QA_004 acute-regression exclusion |
| Cluster consistency | QA_005 5-key cluster identity |
| Scaffold-tier integrity | QA_006 per-(student × skill × bundle × task) uniqueness |
| Teacher confirmation | QA_007 explicit confirmation required for bulk-activate |
| Sensitive language | QA_022 language filter applied, QA_023 parent-report safety |
| Arabic agreement | QA_008 grammar check at import |
| Comparable data | QA_009 only comparable probes compared |
| Anchor preservation | QA_020 anchor weight must remain the largest share |
| Overload prevention | QA_021 max 2 supporting threads |
| Content validity | QA_010 inference items need an evidence_sentence |
| Arabic feature scope | QA_011 no Arabic-feature sub-flag alone may trigger escalation |
| Runtime audit | QA_012 rule-version pinning on all decisions |
| Parent-report safety | QA_023 no internal labels in parent PDF |
| Over-assessment guard | QA_032 alert when probes exceed threshold in a window (new) |
QA_027 (RTI fidelity gate) and QA_030 (fidelity-tracker cross-check) are registered and routing-ready but are not yet wired to their consumer WPs (WP-RTI-BE / fidelity-tracker). They will fire when those WPs bind routeQaFailure.
Severity routing matrix
The routing is deterministic by the registry severity field; no call-site can override it:
| Severity | What happens |
|---|---|
critical | writeQaAlert (alert logged, surfacedToTeacher=true) + notifier.notifyCritical |
high | writeQaAlert + notifier.notifyEngineering (not teacher-surfaced) |
medium | writeQaAlert only |
low | Log to the row’s runtimeLogTarget only; no alert written, no notifier |
In Wave 1, the notifier is a log-only seam (logOnlyQaNotifier). No network call is made. The Slack #amal-alerts integration is queued for post-launch (Q-QAC-1).
Runtime alerts land in the existing platform_alert_log table (no new table). QA-routed rows set alertKind = "qa_NNN" (lowercase) and a severity column, making filtered reads a covered index scan.
The over-assessment guard (QA_032)
QA_032 is a new check added by this work package (not from the partner v5 spec). It fires when a student accumulates more than over_testing_threshold progress-monitoring probes within over_testing_window days.
Key design points:
- Never a block. The probe is always recorded. The guard fires after writing, as a soft advisory.
- Working defaults: threshold = 8 probes, window = 14 days (to be confirmed with partner June-25).
- The guard is injected into
monitoring/repository/evidence-writer.tsat startup. There is no import cycle betweenmodules/qaandmodules/monitoring.
Registry versioning
Admins publish a new check-set with POST /api/admin/qa/checks-set. The endpoint enforces:
- Partner approval must be supplied (
partnerApprovedBy+partnerApprovedAt). - The diff must be explicitly acknowledged (
acknowledgeDiff: true). - All 32 checks must be present in the payload.
- Every row’s
ciLintCommandmust be a runnable workspace command. - Every row’s
failureMessageArmust pass the language-safety filter (no banned substring).
A 422 is returned on any gap. The seed (v1) is the path used at initial deploy; this endpoint governs subsequent publishes.
API endpoints
All three read routes and the version-publish route require elevated permissions; they are not accessible by teacher or student tokens (returns 403):
| Endpoint | Permission | What it returns |
|---|---|---|
GET /api/qa/checks | VIEW_QA_REGISTRY or RUN_BACKOFFICE or SUPER_ADMIN | The full registry (global, no tenant filter, but requires requireTenantContext to derive permissions) |
GET /api/qa/runtime-alerts | same | Org-scoped alerts where alertKind LIKE 'qa_%'; supports ?severity=, ?from=, ?to=, ?qaCheckId= |
GET /api/admin/qa/audit-summary | RUN_BACKOFFICE or SUPER_ADMIN | By-check and by-severity counts for a date range (the partner pre-launch walkthrough feed) |
POST /api/admin/qa/checks-set | RUN_BACKOFFICE or SUPER_ADMIN | Publish a new registry version |
VIEW_QA_REGISTRY is the 25th permission code in the closed 25-value enum.
test:qa:NNN convention
Each registry row’s ciLintCommand is the real workspace command that enforces it. package.json adds a thin test:qa:NNN alias per row delegating to that native command (e.g. test:qa:003 → test:cluster:exclusions), plus the deduplicated aggregate pnpm test:qa. The CI qa-registry job runs pnpm test:qa and pnpm lint:qa and blocks the PR on failure.
Related pages
- Safety Rules: the cross-cutting safety architecture this module backstops
- Language Safety: QA_022 and QA_023 reference enforcement here
- Smart Clustering: QA_003 and QA_004 are enforced in the cluster module
- Progress Monitoring: QA_032 is fired from the evidence writer