MOPC-App/docs/codex-architecture-redesign.../04-unified-domain-model-and...

318 lines
10 KiB
Markdown
Raw Normal View History

# 04. Unified Domain Model And Config Contracts
## 1) Objectives
This target model turns the Monaco flow into strict, reusable contracts while preserving the current pipeline infrastructure.
Goals:
1. Minimize schema churn where current models are already correct.
2. Remove ambiguity for jury identity, assignment policy, round bundles, mentoring promotion, and final lock.
3. Keep customization through explicit policy layers, not ad hoc conditionals.
## 2) Proposed Domain Additions
## 2.1 Stage Purpose Contract
### Add To `Stage`
- `purposeKey` (enum/string, required after migration)
Proposed purpose enum values:
- `submission_r1_intake`
- `eligibility_filter`
- `jury1_evaluation`
- `submission_r2_intake`
- `jury2_evaluation`
- `jury3_live_finals`
- `final_confirmation`
- `results_publication`
- `award_evaluation`
- `award_results`
Rule:
- `StageType` remains coarse technical type.
- `purposeKey` controls business behavior and policy validation.
## 2.2 Jury As First-Class Entity
### New `Jury`
Fields:
- `id`, `programId`, `purposeKey`, `code`, `displayLabel`, `kind` (`MAIN`, `AWARD`), `isActive`
Examples:
- `code=main-semifinal`, `displayLabel=Technical Semi-Final Jury`
- `code=main-finalist`, `displayLabel=Grand Jury`
- `code=award-blue-innovation`, `displayLabel=Blue Innovation Prize Jury`
### New `JuryMembership`
Fields:
- `juryId`, `userId`, `roleInJury` (`CHAIR`, `MEMBER`), `activeFrom`, `activeTo`, `status`
### New `JuryStageBinding`
Fields:
- `juryId`, `stageId`, `isPrimary`, `permissionsJson`
Use:
- controls which juries are allowed to evaluate/vote in each stage
- allows overlap across juries and awards
## 2.3 Assignment Policy Contract
### New `AssignmentPolicy` (stage + jury scoped)
Fields:
- `stageId`
- `juryId`
- `requiredReviews`
- `defaultCap`
- `defaultCapMode` (`HARD`, `SOFT`)
- `softBuffer` (default 10, configurable)
- `categoryBiasPolicyJson` (startup/concept mix preference weights; non-deterministic)
- `overflowPolicy` (`manual_queue`, `expand_pool`, `reduce_reviews`)
- `isActive`
### New `JudgePolicyOverride`
Fields:
- `assignmentPolicyId`
- `userId`
- `cap`, `capMode`, `softBuffer`
- `startupBiasWeight`, `conceptBiasWeight`
- `biasDisclosureAcceptedAt` (onboarding acknowledgement that bias is suggestive only)
- `source` (`ADMIN_SET`, `JUDGE_ONBOARDING`)
### New `AssignmentIntent`
Purpose:
- records desired assignments created before assignment materialization (e.g., from member invite page)
Fields:
- `stageId`, `juryId`, `userId`, `projectId`, `intentSource`, `status`
### New `AssignmentException`
Purpose:
- explicit record for manual over-cap assignments
Fields:
- `assignmentId`, `policyId`, `exceptionType`, `reasonCode`, `reasonText`, `approvedBy`
## 2.4 Submission Round Bundle Contract
### New `SubmissionRound`
Fields:
- `id`, `stageId`, `roundKey`, `name`, `openAt`, `closeAt`, `latePolicy`, `lateGraceHours`, `editabilityPolicy`
### Evolve `FileRequirement`
Add:
- `submissionRoundId` (nullable transitional, required after migration)
- `slotKey` (stable requirement slot identifier)
### Evolve `ProjectFile`
Add:
- `submissionRoundId`
- `submissionSlotKey`
- `sourceType` (`DIRECT_UPLOAD`, `MENTOR_PROMOTION`, `ADMIN_REPLACEMENT`)
- `sourceReferenceId` (e.g., mentor file id)
- `isOfficial`
### New `SubmissionBundleState`
Fields:
- `projectId`, `submissionRoundId`, `status` (`DRAFT`, `SUBMITTED`, `LOCKED`), `lockedAt`, `lockedBy`
## 2.5 Mentoring Collaboration + Promotion
### New `MentorWorkspaceFile`
Fields:
- `projectId`, `mentorId` (nullable for team upload), file storage metadata, visibility, status
### New `MentorWorkspaceComment`
Fields:
- `workspaceFileId`, `authorId`, `threadKey`, `content`, timestamps
### New `SubmissionPromotionEvent`
Fields:
- `projectId`, `workspaceFileId`, `targetSubmissionRoundId`, `targetSlotKey`
- `promotedById`, `promotedAt`
- `resultProjectFileId`
- `approvalState` (if mentor/admin approvals required)
Invariant:
- promotion always creates immutable provenance link from mentoring artifact to official submission slot.
- promotion authority is limited to team lead and admin.
## 2.6 Special Award Governance Contract
### Extend `SpecialAward`
Add:
- `participationMode` (`SEPARATE_POOL`, `DUAL_TRACK`)
- `routingBehavior` (`PULL_FROM_MAIN`, `KEEP_IN_MAIN`)
- `routingConfirmationMode` (`AUTO`, `ADMIN_CONFIRMED`) // Monaco default: `ADMIN_CONFIRMED` for pull-out
- `requiresDedicatedJury` (bool)
- `winnerDecisionMode` (`JURY_CONFIRMATION`, `SINGLE_JUDGE_DECIDES`)
- `singleJudgeUserId` (nullable, required when `winnerDecisionMode=SINGLE_JUDGE_DECIDES`)
- `winnerCandidateSource` (`ELIGIBILITY_REMAINING_POOL`, `CUSTOM_SHORTLIST`) // Monaco single-judge awards use eligibility remaining pool
- `submissionRequirementMode` (`REUSE_MAIN`, `CUSTOM`)
### New `AwardStageBinding`
Fields:
- `awardId`, `filterStageId`, `evaluationStageId`, `resultStageId`
## 2.7 Final Confirmation Contract
### New `FinalConfirmationSession`
Fields:
- `stageId` (final confirmation stage)
- `status` (`OPEN`, `PENDING_ADMIN_APPROVAL`, `FINALIZED`, `CANCELLED`)
- `decisionRule` (`UNANIMOUS`, `SUPERMAJORITY`, `SIMPLE_MAJORITY`, `SINGLE_JUDGE_DECIDES`)
- `quorumPolicy` (`ACTIVE_MEMBERS_ONLY`, `ALLOW_REPLACEMENT`)
- `requiredApprovalsCount` (resolved from active quorum after absence/replacement decisions)
- `scope` (`CATEGORY`, `AWARD`)
- `isAdminOverridden` (bool)
- `overrideReasonCode`, `overrideReasonText`, `overriddenByAdminId`, `overriddenAt`
- `finalizedByAdminId`, `finalizedAt`
### New `FinalConfirmationParticipant`
Fields:
- `sessionId`, `juryMemberId`
- `status` (`REQUIRED`, `ABSENT_EXCUSED`, `REPLACED`, `REPLACEMENT_ACTIVE`)
- `replacedByJuryMemberId` (nullable)
- `absenceReasonCode`, `absenceReasonText`
- `updatedByAdminId`, `updatedAt`
### New `FinalConfirmationVote`
Fields:
- `sessionId`, `juryMemberId`, `projectId/category/awardScope`, `decision`, `comment`
### New `ResultLock`
Fields:
- `sessionId`, `programId`, `lockVersion`, `lockedAt`, `lockedBy`, `snapshotJson`
### New `ResultUnlockEvent`
Fields:
- `resultLockId`, `programId`
- `unlockedBySuperAdminId`, `unlockedAt`
- `reasonCode`, `reasonText`
- `relockVersion` (nullable until relock)
Invariant:
- once `ResultLock` is created, winner outputs are immutable except via explicit super-admin unlock workflow with mandatory audit reason.
## 3) Policy Precedence Model
Order of precedence (highest to lowest):
1. Explicit admin override action (with reason)
2. Per-user override policy (e.g., judge cap override)
3. Stage+jury policy
4. Program-level default policy
5. System default
All runtime evaluators must return:
- resolved value
- source layer
- explanation payload for audit/debug UI
## 4) Access Contract (Role + Context)
Replace scattered checks with `resolveAccess(user, context)` where context includes:
- `programId`
- `trackId`
- `stageId`
- `stagePurposeKey`
- `juryId` (if applicable)
- `projectId` (if applicable)
- `submissionRoundId` (if applicable)
Returns typed permissions:
- `canViewProject`
- `canUploadSubmissionSlot`
- `canViewPreviousRoundDocs`
- `canAssignProjects`
- `canVoteLive`
- `canConfirmFinalWinners`
- etc.
## 5) Assignment Engine Contract
Input:
- projects eligible in stage
- jury members bound to stage
- resolved per-judge policy (cap mode, cap, buffer, category-bias preference)
- required reviews
- COI and availability constraints
Required behavior:
1. satisfy hard-cap constraints strictly
2. satisfy soft-cap targets before using buffer
3. apply startup/concept distribution as a soft scoring bias (never a strict blocker)
4. when all soft-cap members hit cap+buffer, place remainder in manual queue
5. never silently drop unassigned projects
Output:
- assignment set
- unassigned queue with explicit reasons
- policy-compliance summary
## 6) Document Contract
For every stage with submission behavior:
- active `SubmissionRound` must exist
- all required slots represented by `FileRequirement.slotKey`
- applicant write rights only for active round if open
- previous round slots read-only in applicant scope
- judges see current + previous according to stage policy
- admins full mutate rights with replacement provenance
## 7) Invite + Onboarding Contract
## 7.1 Admin Member Invite
- Invitations create `User` + optional `JuryMembership`.
- Jury selection uses program-defined custom jury labels, while binding to explicit stage-purpose contracts.
- Pre-assignment supports both modes:
- intent-first mode via `AssignmentIntent` (default recommended)
- direct assignment mode via `Assignment` (explicitly enabled and policy-validated)
## 7.2 Team Invite
- Team lead invites create/attach `TeamMember`
- invite token acceptance path remains, but post-accept routing resolves to proper applicant/team context
## 7.3 Accept Invite Routing
After acceptance:
- if user has jury memberships pending onboarding -> jury onboarding journey
- if mentor -> mentor onboarding
- if applicant team member only -> applicant team dashboard
## 8) Event/Audit Contract
Every significant workflow event emits:
- business event type (`assignment.generated`, `final_confirmation.finalized`, etc.)
- actor
- entity scope
- policy snapshot
- before/after state where relevant
Override events must include:
- reason code
- reason text
- impacted policy key
- reviewer chain (if applicable)
## 9) API Contract Evolution (Router-Level)
## 9.1 Keep and Evolve
- Keep existing routers (`pipeline`, `stage`, `assignment`, `evaluation`, `file`, `mentor`, `live-voting`, `decision`, `user`, etc.)
- Add new endpoints for purpose/policy/jury/final confirmation and deprecate ambiguous patterns progressively
## 9.2 New Endpoint Families
- `jury.*`: CRUD memberships/bindings
- `assignmentPolicy.*`: configure and inspect effective policy
- `submissionRound.*`: lifecycle and bundle state
- `mentorWorkspace.*`: files/comments/promotion
- `finalConfirmation.*`: create/collect/finalize lock, handle quorum fallback, and manage juror replacement/absence
- `resultUnlock.*`: super-admin unlock/relock workflow with audit capture
- `access.*`: debug effective permissions (admin-only)
## 10) Migration Safety Rules
1. Additive migrations first.
2. Backfill `purposeKey` and policy references from existing configs.
3. Dual-read / single-write transition windows where needed.
4. Feature flags for critical runtime path switches.
5. No silent behavior changes in production without compatibility mode.
## 11) End State
A coherent operating model where all functions, pages, and APIs are consistent with Monaco flow and each key behavior is explainable, testable, and auditable.