DOM-SUP-001: Support Domain
| Reference Number | Version | Effective Date | Supersedes | Authority Level |
|---|---|---|---|---|
| DOM-SUP-001 | 1.9 | 2026-10-14 | 1.8 | Constitutional |
Important
v1.9 (2026-10-14), developer-documentation modernization pass. Corrected a mislabeled Authority Level: DOM-* documents are Tier 1 (Constitutional), not Tier 2 (Normative) — Normative is reserved for SPEC-*/FEAT-*/SOP-*. No substantive rule in this document changed.
I. Purpose
Define the Support domain as the authority over issue lifecycle state, resolution action history, correlation artifacts, user-submitted feedback, and teacher-to-class communication records.
The Support domain records what actors reported and what corrective actions were taken. It does not execute those corrective actions itself.
II. Scope
This domain owns:
- support issue taxonomy
- student-submitted issue records and their lifecycle
- issue status audit trail
- resolution action records (what action was declared, not the effect)
- correlation packs (immutable diagnostic snapshot at submission time)
- user-submitted bug reports and suggestions
- teacher- and sysadmin-authored announcements
This domain does not own:
- ledger mutations (resolution actions that affect money go through FEAT → Ledger)
- identity records
- attendance facts
- configuration policy
- analytics snapshots
III. Dependencies
INV-CORE-000_CORE_INVARIANTS.mdDOM-CORE-000_DOMAIN_FOUNDATION.mdDOM-LED-001_LEDGER_DOMAIN.md(resolution actions may reference transactions; Ledger owns those rows)DOM-ITR-001_INTERPRETATION_DOMAIN.md(correlation packs read from observability tables at submission time)
IV. Schema Authority Declaration
This domain is the sole schema and mutation authority over:
issue_categoriesissuesissue_status_historyissue_resolution_actionsticket_correlation_packsuser_reportsannouncements
No other domain may define fields, impose lifecycle rules, or mutate these tables directly.
V. Owned Tables
issue_categories
Taxonomy of support issue types. Defines the vocabulary used to classify student issues.
issues
Student-submitted support tickets. Core lifecycle record for a class-scoped issue. Carries immutable submission content, mutable review and resolution state, and cached display-safe identity fields.
issue_status_history
Complete audit trail of every status transition on an issue. Append-only.
issue_resolution_actions
Log of corrective actions declared by teachers or sysadmins against an issue. Records the declared action and outcome but does not own the economic side-effects.
ticket_correlation_packs
Immutable diagnostic snapshot captured at issue-submission time. Links the issue to the actor’s recent request traces and error events at the moment of filing.
user_reports
Anonymous or attributed bug reports, suggestions, and comments submitted by any user type. Separate from the issue ticket system; lighter-weight free-form channel.
announcements
Teacher-authored or sysadmin-authored communications displayed to class or system-wide audiences.
VI. Schema Contract
issue_categories
Key fields:
idname— unique; the canonical category labeldescriptioncategory_type—transaction|generalis_activedisplay_order
Rules:
- Categories are system-managed. Students select from this list at submission time; they do not create categories.
is_active = falsecategories may not be selected for new issues but must be retained for display on historical issues.
issues
Key fields:
idseat_id— FK to seats (the student seat that filed the issue)class_id— FK toclassesescalated_by_public_id— teacher seat public reference scoped to the issue classcategory_id— FK to issue_categoriesstatus—OPEN|TEACHER_REVIEW|ESCALATED_TO_DEV|DEV_RESOLVED|TEACHER_FINAL_REVIEW|CLOSEDissue_type—transaction|general- Immutable submission fields:
student_explanationstudent_expected_outcomesubmitted_at
- Display-safe cached identity fields (written at submission; never updated):
student_first_name— encryptedstudent_last_initialactor_public_id— FK to UUID-encodedseats.public_idwith ON DELETE CASCADE, used in sysadmin views instead of a raw integer seat FK
- Class context cache:
class_label— frozen display name at submission time
- Related record context:
related_transaction_id— nullable FK to transactionrelated_record_type—transaction|tap_event|rent_paymentrelated_record_id— opaque ID of the related recordcontext_snapshot— JSON; frozen state of the related record at submission timepage_urlsystem_metadata— JSON
- Teacher review fields:
reviewed_atreviewer_notesresolutionresolved_atsupport_permissions— per-ticket boolean grants for individual diagnostic categories (§X)share_class_name_with_sysadmin— explicit teacher consent for escalation contexteligible_for_rewardescalated_atescalation_reasondiagnostic_note
- Sysadmin fields:
sysadmin_reviewed_atsysadmin_notessysadmin_resolved_at
- Closure:
closed_atclosed_by_type—teacher|sysadmin|system
Rules:
- Submission fields (
student_explanation,student_expected_outcome,submitted_at,context_snapshot) are immutable after creation. actor_public_idis the seat’s UUID public ID and the only participant identity value visible to sysadmin; the rawseat_idFK is not surfaced in sysadmin views.class_labelis cached at submission time; it must not be re-fetched live from ClassEconomy after submission.share_class_name_with_sysadmindefaults to false; it is set only by explicit teacher action during escalation. Class name must not be included in escalation payload without this flag being true.- Status transitions follow a defined state machine (see §VIII).
- An issue is locked to student mutation once it reaches
TEACHER_REVIEW.
issue_status_history
Key fields:
idissue_id— FK to issues (CASCADE)previous_statusnew_statuschanged_atchanged_by_type—student|teacher|sysadmin|systemchanged_by_idnotes
Rules:
- Every status transition on an issue MUST produce a history row atomically with the
status change on the
issuesrow. - History rows are append-only. No row is edited or deleted after creation.
issue_resolution_actions
Key fields:
idissue_id— FK to issues (CASCADE)action_type—reverse_transaction|correct_amount|waive_fee| or other enumerated typesaction_descriptionperformed_by_typeandperformed_by_public_id— role plus class-scoped teacher seat public reference; external operator review has no participant identifierrelated_transaction_id— nullable FK to transaction (cross-domain reference)amount_changedbefore_value/after_valuecreated_at
Rules:
- Resolution actions are a declaration log, not an execution mechanism. The Support domain records that an action was taken; it does not own the ledger rows created or reversed as a result.
related_transaction_idis a cross-domain reference to the Ledger domain. It does not transfer ledger ownership to the Support domain.- Resolution action rows are append-only. Actions declared in error must be addressed by subsequent actions, not by editing history.
ticket_correlation_packs
Key fields:
issue_id— PK and FK to issues (CASCADE); one-to-onecorrelation_version— identifies the correlation schema usedactor_typeactor_public_id— UUID-encodedseats.public_id; matches the actor reference used inactor_request_traceanderror_eventsclass_idrequest_trace_json— frozen list of relevant request trace entries at submission timeerror_refs_json— frozen list of relevant error event references at submission timecreated_at
Rules:
- Correlation packs are created once at issue-submission time and are never mutated.
request_trace_jsonanderror_refs_jsonare frozen snapshots; they are not live references. Even if the source rows inactor_request_traceorerror_eventsare purged, the pack retains its captured data.actor_public_idis the canonical deidentified seat actor reference. It resolves through Identity under the ticket’sclass_id; it does not grant authority by itself.
user_reports
Key fields:
idanonymous_code— HMAC-derived opaque reference; indexed; used for sysadmin display instead of raw identityuser_type—student|teacher|anonymousjoin_code— nullable; present for class-contextual reportsreport_type—bug|suggestion|commenterror_code— nullable HTTP status code associated with the reporttitledescriptionsteps_to_reproduce— nullableexpected_behavior— nullablepage_urlsubmitted_atip_address/user_agentstatus—new|reviewed|rewarded|closed|spamadmin_notesreviewed_atreward_amount/reward_sent_at- Internal FK (hidden from sysadmin):
actor_public_id— UUID-encodedseats.public_idused for reverse-routing a reward; never surfaced in sysadmin views
Rules:
anonymous_codeanduser_typeare the only actor identifiers surfaced in sysadmin views.actor_public_idis for internal reward routing only and must not appear in any sysadmin display.- Sysadmin reward delivery uses
actor_public_idto route the reward through FEAT → Ledger; that cross-domain effect does not transfer ledger ownership. - Report submission content (
title,description,steps_to_reproduce,expected_behavior) is immutable after creation.
announcements
Key fields:
idcreated_by_seat_id— teacher seat author, FK toseatsclass_id— required class boundary, FK toclassestitlemessageis_activepriority—low|normal|high|urgentcreated_at/updated_atexpires_at— nullable; announcement becomes inert after this timestamp
Rules:
- Announcements are informational only. They do not grant permissions, alter capability state, change configuration, or affect any other domain’s tables.
- These announcements are class-scoped. Authentication principal IDs are neither authors nor audiences.
expires_atis a display hint. The announcement row is not deleted when it expires; display logic usesexpires_atto suppress rendering.- A teacher may only create announcements for classes they hold a teacher seat in
(
created_by_seat_idmust resolve to a teacher seat in the explicitclass_id). FEAT-SUP-002 enforces this on create, and on every edit, toggle and delete, for the announcement’s own class; the domain stores the result.
VII. Constraints
- Issue submission content is immutable after creation.
- Correlation packs are immutable after creation.
- Resolution actions are append-only; correction of an erroneous action requires a subsequent action row, not in-place editing.
- Issue status transitions must atomically produce a history row.
- The Support domain reads ledger, attendance, and identity data for context but does not mutate those domains directly.
- Corrective money effects (e.g., transaction reverse or refund outcomes) must be executed via FEAT, which invokes Ledger. The resolution action row records the declaration; Ledger owns the resulting transaction row.
- Sysadmin-accessible actor references use UUID-encoded
seats.public_id, carried asactor_public_idwhere a support-specific column name is needed. - Sysadmins must not be exposed to raw
seat_idoruser_idvalues via the support system. All sysadmin-accessible actor references must use UUID-encodedseats.public_id.
VIII. Issue Status Machine
States: OPEN → TEACHER_REVIEW → ESCALATED_TO_DEV → DEV_RESOLVED →
TEACHER_FINAL_REVIEW → CLOSED
Allowed transitions:
| From | To | Actor |
|---|---|---|
OPEN |
TEACHER_REVIEW |
Teacher or system |
TEACHER_REVIEW |
ESCALATED_TO_DEV |
Teacher |
TEACHER_REVIEW |
CLOSED |
Teacher |
ESCALATED_TO_DEV |
DEV_RESOLVED |
Sysadmin |
DEV_RESOLVED |
TEACHER_FINAL_REVIEW |
System or sysadmin |
TEACHER_FINAL_REVIEW |
CLOSED |
Teacher |
| Any | CLOSED |
System (e.g., TTL expiry) |
Constraints:
- An issue is locked to student mutation once it leaves
OPEN. - Every transition MUST atomically produce an
issue_status_historyrow. - No transition may skip states (e.g.,
OPEN→CLOSEDis only allowed via system cleanup or explicit short-circuit; it must still produce a history row).
IX. Derived / Cross-Domain Rules
- When a resolution action involves a transaction reversal, FEAT coordinates the Ledger reversal and the Support resolution action row in a single operation. Ledger owns the resulting transaction rows; Support owns the resolution action row.
- For an unused or pending item, the teacher resolution MUST explicitly choose
REVERSE(revoke entitlement and compensate money) orREFUND(retain entitlement and compensate money). Both are terminal and preserve the originalcorrelation_id. A used item cannot take either path; a teacher manual credit is a separate new transaction. related_transaction_idonissue_resolution_actionsis a read-only reference to the Ledger domain for audit traceability. It does not transfer ledger write authority.- Sysadmin reward delivery for
user_reportsis initiated via FEAT; the resulting ledger credit is owned by Ledger, not Support. ticket_correlation_packsread fromactor_request_traceanderror_eventsat issue-submission time. After pack creation, the Support domain is self-contained; it does not re-query observability tables for existing packs.
X. Teacher-authorized diagnostic disclosure
The correlation pack (including route information), IP address, browser details, page URL, and capture timestamp remain technical support context. They are not removed by the optional class-data permissions.
issues.support_permissions is a JSON object of independently selected boolean
permissions: balances, transaction (the reported transaction),
recent_transactions (up to ten frozen transactions), and student_report
(the student’s explanation and expected outcome). Each permission defaults to
false, is specific to one ticket, and is set only by its class-owning teacher on
escalation. Class-name disclosure keeps its separate existing checkbox.
No blanket permission, inferred consent, or student-supplied grant is accepted.
Escalation records the teacher’s public actor reference and time atomically with
the selections. Missing permissions on older tickets grant no access.
The student submission snapshot remains immutable context for its teacher. System-support projections include only selected categories from that frozen snapshot. The gate is server-side and applies to every operator list/detail surface, including alternate report URLs. Unknown snapshot fields are withheld. This is disclosure of a teacher-authorized diagnostic copy, not authority to query or mutate live class instances. Permission in one category grants no permission in another, and does not authorize future data capture.
Frozen capture and deletion closure
Capture snapshot values and correlation rows once at submission. Never refresh, recompute, or overwrite them when permissions, review status, profile names, balances, or other source facts change. Escalation selects from saved values only; operator reads do not return to teacher or student seats for context. Ticket workflow and disclosure-permission fields are distinct from the immutable capture.
There is no account-level or unscoped ticket category. Every ticket is seat-scoped
and therefore class-scoped; a teacher reporting a problem about themselves still
uses their active seat and class. issues.class_public_id is required.
Every student and teacher ticket, regardless of the problem being reported, references
its originating seat by canonical public ID. Support holds only that public ID: there is
no foreign key to seats, because INV-ARC-021 §V.7 permits cross-domain foreign keys
only to class_id, seat_id, and user_id. Seat deletion resolves the seat’s public ID
and deletes its issues in the same transaction; database cascades within Support then
remove the correlation pack, history, and resolution records. Account deletion
removes its seats and therefore these support rows; class deletion likewise
removes its owned seats and tickets. Immutable means unchanged while retained,
not exempt from identity deletion. Do not preserve a detached sysadmin copy.
The source actor_request_trace rows obey the same ownership lifetime. Each
trace requires an existing canonical Seat public ID and class ID. Deleting that Seat
or its User deletes its traces explicitly by public ID in the same transaction;
deleting its class deletes them through the class_id foreign key. Nulling the class
reference is not deletion. TTL/count pruning is supplemental
retention management, never a substitute for deletion closure. A request that
finishes after destroying its own seat or class must not recreate a trace from
cached context. The writers of issues and traces validate the live seat/class pair under a share lock
on the seat row, so a concurrent seat deletion waits for them or they observe that the
seat is gone and write nothing.
A teacher’s directly submitted report is an explicit submission of that text; class names must not be silently embedded in metadata headers. Such a form does not automatically attach balances, transactions, or roster notes. No assertion is made about the contents of user-entered free text.
XI. Outbound filing notification
Support authorizes one best-effort outbound IFTTT notification when a teacher files a direct ticket or escalates a student ticket for system support. Student submission alone does not notify the external operator. Escalation changes an existing issue; it does not insert a second issue. Reads, review, resolution, closure, and rejected submissions do not trigger filing notifications.
Only a successful outer database commit authorizes delivery. Rollback discards the transaction-local notification; a savepoint release is not delivery authority. Notification failure never changes the committed issue, history, or response. Delivery uses the existing application process, a fixed IFTTT HTTPS destination, a three-second socket timeout, and no redirects, retries, durable outbox, or additional service. Process loss can lose an alert; exactly-once delivery and email receipt are not promised.
The complete external payload allow-list is a fixed teacher-filing or student- escalation label, an encrypted opaque issue reference, and the UTC event timestamp. No participant or class identifiers, names, free text (including title), category labels, page URLs, IP/browser information, diagnostics, permissions, correlation packs, or economic records may be sent. In-app diagnostic consent does not widen this external allow-list. The reference identifies a Support record only and grants no access; authenticated operator visibility checks still apply. The email recipient is configured within IFTTT, never collected or processed by CTH.
SUPPORT_IFTTT_EVENT and SUPPORT_IFTTT_KEY configure this optional integration.
Both unset disables it. Partial or invalid configuration is a delivery failure,
not a ticket failure. Credentials, webhook URL, payload, response bodies, and
exception text must never be logged or traced. Logs record delivery outcome only.
XII. Amendment
Revisions require version increment, effective-date update, and continued consistency with higher-order invariants.
Last updated: 2026-10-14