Skip to main content

SDAC Database Schema

Overview

The SDAC schema stores cost report data, personnel records, validation results, and review workflow state. It follows a dimensional modeling approach with fact and dimension tables.

Schema source: db/sqlproj/Mastra.Platform.SDACDb/model/

Schema Summary

TypeCountTables
Dimension tables (dim_)2Districts, ValidationRules
Data tables (data_)2CostReports, PersonnelRecords
Fact tables (fact_)3AIAnalysisResults, ConversationHistory, UserFeedback
Analysis tables (analysis_)6AgentRetrievalAudit, Evidence, EvidenceFindings, EvidenceReportRefs, Results, ReviewDecisions
Validation tables (val_)1ValidationResults
Workflow tables (wf_)2ReviewRequests, ReviewDecisions
Log tables (log_)3AIAgentCalls, FileProcessing, PromptTuningRuns
Views3PersonnelWithValidation, ReviewStatus, ValidationErrorSummary

Tables

SDAC.data_CostReports

Central repository for SDAC cost report submissions.

ColumnTypeDescription
ReportIdUNIQUEIDENTIFIERPrimary key
DistrictSKBIGINTForeign key to dim_Districts
QuarterINTQuarter number (1-4)
YearINTFiscal year
FiscalPeriodNVARCHAR(20)Period identifier (e.g., "Q1-2024")
SourceFileNameNVARCHAR(500)Original uploaded file name
FileHashCHAR(64)SHA256 hash for deduplication
ProcessingStatusNVARCHAR(20)pending, processing, success, error, partial
ExternalDistrictIdNVARCHAR(100)External district identifier at time of report submission (e.g. TherapyLog ID). Denormalized from dim_Districts for traceability when the district mapping changes. Indexed by period.
TotalSalaryPool1DECIMAL(18,2)Sum of Pool 1 salaries
TotalSalaryPool2DECIMAL(18,2)Sum of Pool 2 salaries
TotalFringePool1DECIMAL(18,2)Sum of Pool 1 fringe benefits
TotalFringePool2DECIMAL(18,2)Sum of Pool 2 fringe benefits

Key indexes:

  • IX_data_CostReports_FileHash — Fast duplicate detection
  • IX_data_CostReports_District_Period — District + fiscal period lookups
  • IX_data_CostReports_Status — Filter by processing status

SDAC.data_PersonnelRecords

Individual personnel records extracted from cost reports.

ColumnTypeDescription
PersonnelRecordIdBIGINTPrimary key
ReportIdUNIQUEIDENTIFIERFK to data_CostReports
RowNumberINTOriginal row number from Excel
EmployeeIdNVARCHAR(100)Employee identifier
JobTitleNVARCHAR(200)Job title
SourceCodeNVARCHAR(10)Source code (0-4)
FunctionCodeNVARCHAR(10)Function code
CostPoolNVARCHAR(10)Cost pool assignment
GrossSalaryDECIMAL(18,2)Total salary
ClaimableSalaryDECIMAL(18,2)Claimable portion
GrossFringeDECIMAL(18,2)Total fringe benefits
ClaimableFringeDECIMAL(18,2)Claimable fringe
IsReplacementBITReplacement position flag
CommentsNVARCHAR(MAX)Comment field from report

SDAC.dim_Districts

Reference table for school districts.

ColumnTypeDescription
DistrictSKBIGINTSurrogate key
DistrictIdNVARCHAR(50)Business key
DistrictNameNVARCHAR(200)Full district name
ExternalDistrictIdNVARCHAR(100)External system identifier (e.g. TherapyLog district ID). Stored separately so sync systems cannot overwrite the human-readable DistrictName with a raw ID. Unique filtered index (non-NULL rows only).
CountyNameNVARCHAR(100)County
RegionCodeNVARCHAR(20)Regional code
IsActiveBITActive flag

SDAC.dim_ValidationRules

Validation rule definitions for cost report validation.

ColumnTypeDescription
RuleIdBIGINTPrimary key
RuleNameNVARCHAR(100)Rule identifier
CategoryNVARCHAR(50)SOURCE_CODE, FUNCTION_CODE, SALARY, etc.
DescriptionNVARCHAR(MAX)Human-readable description
SeverityNVARCHAR(20)ERROR or WARNING
IsActiveBITWhether rule is enabled
IsCurrentBITCurrent version flag

SDAC.val_ValidationResults

Stores validation execution results.

ColumnTypeDescription
ValidationResultIdBIGINTPrimary key
ReportIdUNIQUEIDENTIFIERFK to cost report
PersonnelRecordIdBIGINTFK to personnel record (nullable for report-level)
RuleIdBIGINTFK to validation rule
PassedBITWhether validation passed
ErrorMessageNVARCHAR(MAX)Error details
CurrentValueNVARCHAR(500)Actual value found
ExpectedValueNVARCHAR(500)Expected value
ValidatedAtUtcDATETIME2(3)Validation timestamp

SDAC.ingestion_UploadJobs

Durable upload job state for the Mastra-owned SDAC upload workflow. The widget polls Mastra for this state while the internal ingestion worker parses the workbook.

ColumnTypeDescription
JobIdUNIQUEIDENTIFIERPublic Mastra upload job ID
ReportIdUNIQUEIDENTIFIERParsed cost report ID, nullable until parsing completes
WorkerJobIdNVARCHAR(100)Internal ingestion worker job ID
StatusNVARCHAR(30)queued, processing, success, duplicate, error, or not_found
StageNVARCHAR(50)Current processing stage, such as queued, processing_file, parsed, or failed
MessageNVARCHAR(2000)Human-readable progress message
FileNameNVARCHAR(512)Uploaded workbook filename
FileSizeBytesBIGINTUploaded file size
LargeFileBITWhether the upload exceeded the async progress threshold
FileHashNVARCHAR(128)Hash used for duplicate detection
UserEmail, UserName, DistrictNameNVARCHARUpload context supplied by the widget or API caller
ForceReingestBITWhether duplicate detection was bypassed
WorkerStatusUrlNVARCHAR(1000)Internal worker status URL mirrored by Mastra
AnalysisTriggeredBITWhether Mastra has started sdac-report-analysis
AnalysisRunIdNVARCHAR(100)Report-analysis runtime run ID
AnalysisStatusNVARCHAR(MAX)JSON workflow status snapshot
ResultJsonNVARCHAR(MAX)Last successful worker payload
ErrorJsonNVARCHAR(MAX)Last worker or Mastra upload error payload
CreatedAtUtc, UpdatedAtUtc, LastHeartbeatAtUtc, CompletedAtUtc, ExpiresAtUtcDATETIME2(3)Lifecycle timestamps

Key indexes:

  • IX_ingestion_UploadJobs_StatusUpdated -- active job/status dashboard lookups
  • IX_ingestion_UploadJobs_ReportId -- report-to-upload traceability
  • IX_ingestion_UploadJobs_WorkerJobId -- worker status correlation

SDAC.analysis_Results

Pre-computed analysis results from validation tools and the post-ingestion report analysis workflow. Results are cached for fast retrieval by agents, dashboards, retry flows, and report packet construction.

ColumnTypeDescription
AnalysisIdBIGINTPrimary key (identity)
ReportIdUNIQUEIDENTIFIERFK to data_CostReports
AnalysisTypeNVARCHAR(50)SOURCE_CODE, FUNCTION_CODE, SALARY, LINKAGE_CANONICAL, REPORT_ANALYSIS_PACKET, etc.
ToolIdNVARCHAR(100)Tool or workflow that produced the result (e.g., sdac-validate-source-codes, sdac-report-analysis-workflow)
StatusNVARCHAR(20)pending, running, completed, error, stale, blocked
VisibilityStatusNVARCHAR(20)visible or disabled; conversational tools hide disabled rows while preserving stored results
AnalysisVersionNVARCHAR(64)Algorithm/prompt version that produced the row
InputHashNVARCHAR(128)Stable input/dependency hash used for freshness checks
DependencyJsonNVARCHAR(MAX)JSON dependency metadata, such as canonical linkage hash
BlockedReasonNVARCHAR(1000)Reason an analysis was blocked by prerequisite issues
ResultJsonNVARCHAR(MAX)Full tool output JSON (NULL if pending/error)
PassedBITWhether all checks passed (denormalized)
ErrorCountINTNumber of errors found
WarningCountINTNumber of warnings found
RecordsCheckedINTRecords evaluated
ErrorMessageNVARCHAR(2000)Error description (when Status = error)
ErrorDetailsNVARCHAR(MAX)Stack trace or context (when Status = error)
StartedAtUtcDATETIME2(3)Analysis start time
CompletedAtUtcDATETIME2(3)Analysis completion time
ExecutionTimeMsINTDuration in milliseconds
TriggeredByNVARCHAR(50)ingestion, on_demand, or retry
CreatedAtUtcDATETIME2(3)Row creation timestamp
UpdatedAtUtcDATETIME2(3)Last update timestamp

Key constraints:

  • UQ_analysis_Results_ReportType -- One row per report + analysis type
  • CK_analysis_Results_Status -- Status must be one of the six values above
  • CK_analysis_Results_VisibilityStatus -- Visibility must be visible or disabled
  • CK_analysis_Results_ResultJson -- ResultJson must be valid JSON or NULL
  • CK_analysis_Results_DependencyJson -- DependencyJson must be valid JSON or NULL

Key indexes:

  • IX_analysis_Results_ReportId -- Fast lookup by report with summary columns
  • IX_analysis_Results_ReportStatus -- Report + status for finding completed/stale analyses
  • IX_analysis_Results_ReportVisibilityStatus -- Report + visibility + status for conversational retrieval
  • IX_analysis_Results_Errors -- Filtered index for error retry dashboard
  • IX_analysis_Results_Stale -- Filtered index for re-computation queue

The report analysis workflow stores a final packet in this table using AnalysisType = REPORT_ANALYSIS_PACKET and ToolId = sdac-report-analysis-workflow. Agents read that packet through sdac-get-analysis-packet for broad review questions and read individual rows through sdac-get-analysis-result for focused questions.

SDAC.analysis_AgentRetrievalAudit

Audit trail of SDAC conversational-agent analysis retrieval. This table is written from actual Mastra chat stream tool-call and tool-result events, so it records what analysis retrieval tools the agent really called during a conversation turn.

ColumnTypeDescription
AuditIdBIGINTPrimary key (identity)
ReportIdUNIQUEIDENTIFIERFK to data_CostReports; nullable when a tool is called before report context is resolved
ConversationIdNVARCHAR(100)Conversation/session key from the SDAC chat route
TurnNumberINTConversation turn number
UserIdNVARCHAR(100)Authenticated user identifier when available
AgentIdNVARCHAR(128)Agent profile that made the tool call
ToolCallIdNVARCHAR(128)Mastra stream tool-call ID
ToolNameNVARCHAR(128)Tool called by the agent
RetrievalKindNVARCHAR(50)packet, result, evidence_search, workflow_status, raw_tool, or other
StatusNVARCHAR(20)started, completed, or error
ForConversationBITWhether the tool was called for conversational output filtering
IncludeRawBITWhether raw result payloads were requested
InputJsonNVARCHAR(MAX)Tool input captured from the stream payload
RequestedAnalysisJsonNVARCHAR(MAX)Analysis types requested by the agent
ReturnedAnalysisJsonNVARCHAR(MAX)Analysis types returned by the tool
MissingAnalysisJsonNVARCHAR(MAX)Requested analysis types that were unavailable or filtered
CoverageStatusNVARCHAR(20)complete, partial, single, none, unknown, or not_applicable
CoverageJsonNVARCHAR(MAX)Compact coverage summary, including counts by status where available
OutputSummaryJsonNVARCHAR(MAX)Compact summarized output; full tool output is not duplicated here
OutputHashCHAR(64)SHA256 hash of the summarized output for change/audit comparison
ResultSummaryNVARCHAR(1000)Human-readable summary of the retrieval result
StartedAtUtc, CompletedAtUtc, DurationMsDATETIME2 / INTTool-call timing
ExpiresAtUtcDATETIME2(3)Retention expiry, currently 180 days after creation

Key constraints:

  • CK_analysis_AgentRetrievalAudit_RetrievalKind -- RetrievalKind must be one of the supported categories
  • CK_analysis_AgentRetrievalAudit_Status -- Status must be started, completed, or error
  • CK_analysis_AgentRetrievalAudit_CoverageStatus -- CoverageStatus must be one of the supported values
  • JSON check constraints validate all JSON summary columns when they are not NULL

Key indexes:

  • IX_analysis_AgentRetrievalAudit_ReportTurn -- Report + conversation turn audit review
  • IX_analysis_AgentRetrievalAudit_Conversation -- Conversation-level audit review
  • IX_analysis_AgentRetrievalAudit_Tool -- Tool usage auditing and troubleshooting
  • UX_analysis_AgentRetrievalAudit_ToolCall -- One audit row per conversation turn + tool call ID when those values are present

Example audit query:

SELECT
StartedAtUtc,
CompletedAtUtc,
AgentId,
ToolName,
RetrievalKind,
Status,
CoverageStatus,
RequestedAnalysisJson,
ReturnedAnalysisJson,
MissingAnalysisJson,
ResultSummary
FROM SDAC.analysis_AgentRetrievalAudit
WHERE ReportId = @ReportId
AND ConversationId = @ConversationId
ORDER BY TurnNumber, StartedAtUtc;

Use this table with SDAC.fact_ConversationHistory to compare what the agent retrieved against what it said in the final response.

SDAC.fact_AIAnalysisResults

Stores AI analysis findings from agent processing.

ColumnTypeDescription
AnalysisResultIdBIGINTPrimary key
ReportIdUNIQUEIDENTIFIERFK to cost report
AgentIdNVARCHAR(128)Agent that produced analysis
AnalysisTypeNVARCHAR(50)Type of analysis performed
FindingsJsonNVARCHAR(MAX)JSON-formatted findings
ConfidenceDECIMAL(5,4)Confidence score (0-1)
CreatedAtUtcDATETIME2(3)Analysis timestamp

SDAC.fact_ConversationHistory

Conversation turns for the SDAC AI Widget, enabling context-aware responses and conversation resume.

ColumnTypeDescription
ConversationSKBIGINTPrimary key (identity)
ReportIdUNIQUEIDENTIFIERFK to data_CostReports
UserIdNVARCHAR(100)User identifier from auth
SessionIdNVARCHAR(100)Browser session ID (used as conversation key)
TurnNumberINTSequence number within conversation
RoleNVARCHAR(20)user, assistant, or system
MessageContentNVARCHAR(MAX)Message content
RecordSKBIGINTFK to data_PersonnelRecords (optional context)
RuleIdNVARCHAR(50)Validation rule reference (optional context)
ModelUsedNVARCHAR(100)LLM deployment name used
PromptTokensINTInput tokens consumed
CompletionTokensINTOutput tokens consumed
TotalTokensINTTotal tokens consumed
CreatedAtUtcDATETIME2(3)Turn timestamp
UpdatedAtUtcDATETIME2(3)Last update timestamp
ExpiresAtUtcDATETIME2(3)Retention expiry (90-day default)

Key indexes:

  • IX_fact_ConversationHistory_Session — Session + turn number lookups
  • IX_fact_ConversationHistory_Report — Report-based history queries
  • IX_fact_ConversationHistory_User — User-based history queries

API Endpoint: POST /sdac/chat direct runtime, or POST /api/ingestion/sdac/chat through the widget proxy -- SSE streaming endpoint with server-side session management

SDAC.wf_ReviewRequests

Review request workflow queue.

ColumnTypeDescription
ReviewRequestIdBIGINTPrimary key
ReportIdUNIQUEIDENTIFIERFK to cost report
RequestTypeNVARCHAR(50)Type of review requested
PriorityINTPriority level (1-5)
StatusNVARCHAR(20)pending, in_review, completed, rejected
AssignedToNVARCHAR(128)Assigned reviewer
RequestedAtUtcDATETIME2(3)Request timestamp
DueAtUtcDATETIME2(3)Due date

SDAC.wf_ReviewDecisions

Review workflow decisions and approvals.

ColumnTypeDescription
DecisionIdBIGINTPrimary key
ReviewRequestIdBIGINTFK to review request
DecisionNVARCHAR(20)approved, rejected, needs_revision
DecisionNotesNVARCHAR(MAX)Reviewer comments
DecidedByNVARCHAR(128)Decision maker
DecidedAtUtcDATETIME2(3)Decision timestamp

SDAC.log_AIAgentCalls

Audit trail of AI agent invocations.

ColumnTypeDescription
CallIdBIGINTPrimary key
ReportIdUNIQUEIDENTIFIERFK to cost report
AgentIdNVARCHAR(128)Agent identifier
ToolNameNVARCHAR(128)Tool invoked
InputPayloadJsonNVARCHAR(MAX)Input parameters
OutputPayloadJsonNVARCHAR(MAX)Agent response
DurationMsINTExecution time
TokensUsedINTTotal tokens consumed
CreatedAtUtcDATETIME2(3)Call timestamp

SDAC.fact_UserFeedback

User feedback on agent responses, enabling feedback-driven prompt tuning.

ColumnTypeDescription
FeedbackSKBIGINTPrimary key (identity)
ConversationSKBIGINTFK to fact_ConversationHistory (the turn being rated)
ReportIdUNIQUEIDENTIFIERFK to data_CostReports
UserIdNVARCHAR(100)User identifier
SessionIdNVARCHAR(100)Browser session ID
RatingTINYINTRating 1-5
CategoryNVARCHAR(30)accuracy, clarity, relevance, helpfulness, tone, other
CommentNVARCHAR(2000)Optional free-text feedback
AgentIdNVARCHAR(128)Denormalized for aggregation
TurnNumberINTTurn being rated
CreatedAtUtcDATETIME2(3)Feedback timestamp
ExpiresAtUtcDATETIME2(3)Retention expiry (90-day default)

Key indexes:

  • IX_fact_UserFeedback_AgentId -- Agent + date aggregation
  • UQ_fact_UserFeedback_UserTurn -- Prevents duplicate ratings (same user + turn)

API Endpoints:

  • POST /sdac/feedback -- Submit feedback; widget callers use /api/ingestion/sdac/feedback
  • GET /sdac/feedback/stats/{agentId} -- Aggregated feedback stats

SDAC.log_PromptTuningRuns

Audit trail of prompt tuning operations performed by the Prompt Tuner agent.

ColumnTypeDescription
RunIdBIGINTPrimary key (identity)
TargetAgentIdNVARCHAR(128)Agent whose prompt was tuned
FeedbackCountINTNumber of feedback records analyzed
FeedbackSummaryJsonNVARCHAR(MAX)JSON summary of feedback patterns
CurrentPromptNVARCHAR(MAX)Snapshot of prompt before change
ProposedPromptNVARCHAR(MAX)New prompt applied
ChangeRationaleNVARCHAR(MAX)Why the change was made
ConfidenceScoreDECIMAL(3,2)Tuner confidence (0.00-1.00)
StatusNVARCHAR(20)applied, rolled_back, error
AppliedProfileSKBIGINTFK to Core.AgentProfiles after apply
TriggeredByNVARCHAR(128)Who triggered the run
CreatedAtUtcDATETIME2(3)Run timestamp
ExpiresAtUtcDATETIME2(3)Retention expiry (180-day default)

SDAC.log_FileProcessing

File processing pipeline tracking.

ColumnTypeDescription
ProcessingIdBIGINTPrimary key
ReportIdUNIQUEIDENTIFIERFK to cost report
StepNVARCHAR(50)Processing step name
StatusNVARCHAR(20)Step status
StartedAtUtcDATETIME2(3)Step start time
CompletedAtUtcDATETIME2(3)Step completion time
ErrorMessageNVARCHAR(MAX)Error details (if failed)

Views

SDAC.vw_PersonnelWithValidation

Joins personnel records with their validation results.

SELECT
pr.*,
vr.Passed,
vr.ErrorMessage,
vr.RuleId
FROM SDAC.data_PersonnelRecords pr
LEFT JOIN SDAC.val_ValidationResults vr ON pr.PersonnelRecordId = vr.PersonnelRecordId;

SDAC.vw_ReviewStatus

Current status of all review requests with decision history.

SELECT
rr.ReviewRequestId,
rr.ReportId,
rr.Status,
rr.AssignedTo,
rd.Decision,
rd.DecisionNotes
FROM SDAC.wf_ReviewRequests rr
LEFT JOIN SDAC.wf_ReviewDecisions rd ON rr.ReviewRequestId = rd.ReviewRequestId;

SDAC.vw_ValidationErrorSummary

Aggregated validation error counts and patterns.

SELECT
ReportId,
RuleId,
COUNT(*) AS ErrorCount,
STRING_AGG(ErrorMessage, '; ') AS ErrorMessages
FROM SDAC.val_ValidationResults
WHERE Passed = 0
GROUP BY ReportId, RuleId;

ER Diagram

Data Flow

1. File Upload
└── log_FileProcessing (step: UPLOAD)
└── data_CostReports (status: pending)

2. File Processing
└── log_FileProcessing (step: PARSE, EXTRACT)
└── data_PersonnelRecords (bulk insert)
└── data_CostReports (status: success)

3. Validation
└── dim_ValidationRules (get active rules)
└── val_ValidationResults (store results)
└── analysis_Results (cache tool output)

4. AI Analysis
└── log_AIAgentCalls (audit trail)
└── fact_AIAnalysisResults (findings)

5. Human Review
└── wf_ReviewRequests (queue)
└── wf_ReviewDecisions (decisions)

6. User Feedback
└── fact_UserFeedback (ratings, comments)
└── log_PromptTuningRuns (prompt optimization audit)

Query Examples

-- Get all validation errors for a report
SELECT pr.RowNumber, pr.EmployeeId, pr.JobTitle,
vr.RuleId, vr.ErrorMessage
FROM SDAC.data_PersonnelRecords pr
JOIN SDAC.val_ValidationResults vr ON pr.PersonnelRecordId = vr.PersonnelRecordId
WHERE pr.ReportId = @reportId AND vr.Passed = 0
ORDER BY pr.RowNumber;

-- District validation summary
SELECT d.DistrictName,
COUNT(DISTINCT cr.ReportId) AS TotalReports,
SUM(CASE WHEN cr.ProcessingStatus = 'success' THEN 1 ELSE 0 END) AS SuccessfulReports,
AVG(cr.TotalSalaryPool1 + cr.TotalSalaryPool2) AS AvgTotalSalary
FROM SDAC.dim_Districts d
JOIN SDAC.data_CostReports cr ON d.DistrictSK = cr.DistrictSK
GROUP BY d.DistrictName;

-- Review queue status
SELECT rr.Status, COUNT(*) AS RequestCount
FROM SDAC.wf_ReviewRequests rr
WHERE rr.RequestedAtUtc >= DATEADD(day, -30, GETUTCDATE())
GROUP BY rr.Status;