diff --git a/components/ResultsTable.tsx b/components/ResultsTable.tsx index e8e4d4d..87c8a8b 100644 --- a/components/ResultsTable.tsx +++ b/components/ResultsTable.tsx @@ -13,8 +13,8 @@ import { isPostRevealSurveyComplete, isPreRevealSurveyComplete, } from "@/utils/researchMetrics"; -import { getStoredSession, saveStoredSession } from "@/utils/session"; -import type { ComputedResearchMetrics, GameSummary, HiddenCostGameState, PostRevealSurveyAnswers, PreRevealSurveyAnswers, ResearchSession } from "@/types/research"; +import { assignCostVisibilityCondition, getStoredSession, saveStoredSession } from "@/utils/session"; +import type { ComputedResearchMetrics, CostVisibilityConditionName, GameSummary, HiddenCostGameState, PostRevealSurveyAnswers, PreRevealSurveyAnswers, ResearchSession } from "@/types/research"; const fictionalPlayers = [ { name: "Player 1", score: 164 }, @@ -52,10 +52,11 @@ export function ResultsTable({ mode = "visible" }: { mode?: ResultsMode }) { return; } - const nextSession: ResearchSession = { + const stagedSession: ResearchSession = { ...storedSession, currentStage: targetStage, }; + const nextSession = mode === "visible" ? assignCostVisibilityCondition(stagedSession) : stagedSession; saveStoredSession(nextSession); setSession(nextSession); @@ -90,6 +91,8 @@ export function ResultsTable({ mode = "visible" }: { mode?: ResultsMode }) { At this stage, you can see the score table. Please answer based only on the information currently available.
+ {session?.costVisibilityCondition ?+ {message} +
+ ); +} + function IndividualResults({ session, game, @@ -117,6 +137,7 @@ function IndividualResults({ postRevealSurvey, preRevealSurveyOriginal: session.preRevealSurveyOriginal, explanationFrameCondition: session.explanationFrameCondition, + costVisibilityCondition: session.costVisibilityCondition, }); const interpretations = buildParticipantInterpretation(computedMetrics); @@ -232,6 +253,9 @@ function MetricsGrid({ metrics }: { metrics: ComputedResearchMetrics }) { ["Remembered attribution matches original", metrics.rememberedPrimaryAttributionMatchesOriginal ? "Yes" : "No"], ["Memory confidence", metrics.memoryConfidence], ["Memory distortion magnitude", metrics.memoryDistortionMagnitude], + ["Cost visibility condition", metrics.costVisibilityCondition ?? "Not assigned"], + ["Had any cost hint", metrics.hadAnyCostHint ? "Yes" : "No"], + ["Had strong cost hint", metrics.hadStrongCostHint ? "Yes" : "No"], ] as const; return ( diff --git a/lib/adminSubmissions.ts b/lib/adminSubmissions.ts index 0a3f840..c882cee 100644 --- a/lib/adminSubmissions.ts +++ b/lib/adminSubmissions.ts @@ -295,6 +295,7 @@ const CSV_COLUMNS: CsvColumn[] = [ dbColumn("completed_game_rounds", "completedGameRounds"), payloadColumn("reveal_timing_condition", ["revealTimingCondition", "condition"]), payloadColumn("explanation_frame_condition", ["explanationFrameCondition", "condition"]), + payloadColumn("cost_visibility_condition", ["costVisibilityCondition", "condition"]), payloadColumn("stand_by_initial_interpretation", ["preRevealCommitment", "standByInitialInterpretation"]), payloadColumn("pre_reveal_commitment_text", ["preRevealCommitment", "explanationConfidenceText"]), payloadColumn("final_financial_score", ["gameSummary", "finalFinancialScore"]), @@ -306,6 +307,9 @@ const CSV_COLUMNS: CsvColumn[] = [ payloadColumn("skipped_treatment_choices", ["gameSummary", "skippedTreatmentChoices"]), payloadColumn("burden", ["computedMetrics", "burden"]), payloadColumn("care_avoidance", ["computedMetrics", "careAvoidance"]), + payloadColumn("metric_cost_visibility_condition", ["computedMetrics", "costVisibilityCondition"]), + payloadColumn("had_any_cost_hint", ["computedMetrics", "hadAnyCostHint"]), + payloadColumn("had_strong_cost_hint", ["computedMetrics", "hadStrongCostHint"]), payloadColumn("responsibility_shift", ["computedMetrics", "responsibilityShift"]), payloadColumn("constraint_recognition_shift", ["computedMetrics", "constraintRecognitionShift"]), payloadColumn("protest_legitimacy_shift", ["computedMetrics", "protestLegitimacyShift"]), diff --git a/lib/researchExportSchema.ts b/lib/researchExportSchema.ts index 2a144fe..ca2355e 100644 --- a/lib/researchExportSchema.ts +++ b/lib/researchExportSchema.ts @@ -12,6 +12,7 @@ const serverSubmissionStatusSchema = z.enum(["not_enabled", "not_submitted", "su const revisionAccessConditionSchema = z.enum(["revision-unlocked", "revision-locked"]); const revealTimingConditionNameSchema = z.enum(["immediate-reveal", "delayed-reveal"]); const explanationFrameConditionNameSchema = z.enum(["explain-to-self", "explain-to-other"]); +const costVisibilityConditionNameSchema = z.enum(["no-cost-info", "partial-cost-hint", "full-cost-preview"]); export const revisionAccessSchema = z .object({ @@ -55,6 +56,13 @@ export const explanationFrameConditionSchema = z }) .passthrough(); +export const costVisibilityConditionSchema = z + .object({ + condition: costVisibilityConditionNameSchema, + assignedAt: isoDateStringSchema, + }) + .passthrough(); + export const participantProfileSchema = z .object({ ageGroup: z.string().min(1), @@ -166,6 +174,9 @@ export const computedMetricsSchema = z memoryConfidence: likertSchema, memoryDistortionMagnitude: z.number().nonnegative(), explanationFrame: explanationFrameConditionNameSchema.nullable(), + costVisibilityCondition: costVisibilityConditionNameSchema.nullable(), + hadAnyCostHint: z.boolean(), + hadStrongCostHint: z.boolean(), attributionCategoryShift: z .object({ pre: z.string().min(1), @@ -213,6 +224,7 @@ export const researchExportSchema = z revealTimingCondition: revealTimingConditionSchema.optional(), preRevealCommitment: preRevealCommitmentSchema.optional(), explanationFrameCondition: explanationFrameConditionSchema.optional(), + costVisibilityCondition: costVisibilityConditionSchema.optional(), assignedProfile: assignedProfileSchema, gameSummary: gameSummarySchema, gameRounds: z.array(gameRoundSchema).min(1), diff --git a/types/research.ts b/types/research.ts index 870bcdc..8ef5b8d 100644 --- a/types/research.ts +++ b/types/research.ts @@ -155,6 +155,7 @@ export interface PreRevealRevision { export type RevealTimingConditionName = "immediate-reveal" | "delayed-reveal"; export type ExplanationFrameConditionName = "explain-to-self" | "explain-to-other"; +export type CostVisibilityConditionName = "no-cost-info" | "partial-cost-hint" | "full-cost-preview"; export interface RevealTimingCondition { condition: RevealTimingConditionName; @@ -172,6 +173,11 @@ export interface ExplanationFrameCondition { assignedAt: string; } +export interface CostVisibilityCondition { + condition: CostVisibilityConditionName; + assignedAt: string; +} + export interface ComputedResearchMetrics { responsibilityShift: number; constraintRecognitionShift: number; @@ -204,6 +210,9 @@ export interface ComputedResearchMetrics { memoryConfidence: number; memoryDistortionMagnitude: number; explanationFrame: ExplanationFrameConditionName | null; + costVisibilityCondition: CostVisibilityConditionName | null; + hadAnyCostHint: boolean; + hadStrongCostHint: boolean; } export interface ResearchExportAssignedProfile { @@ -233,6 +242,7 @@ export interface ResearchExport { revealTimingCondition?: RevealTimingCondition; preRevealCommitment?: PreRevealCommitment; explanationFrameCondition?: ExplanationFrameCondition; + costVisibilityCondition?: CostVisibilityCondition; assignedProfile: ResearchExportAssignedProfile; gameSummary: GameSummary; gameRounds: GameRoundData[]; @@ -272,6 +282,7 @@ export interface ResearchSession { revealTimingCondition?: RevealTimingCondition; preRevealCommitment?: PreRevealCommitment; explanationFrameCondition?: ExplanationFrameCondition; + costVisibilityCondition?: CostVisibilityCondition; postRevealSurvey?: PostRevealSurveyAnswers; preRevealSurveyStartedAt?: string; preRevealSurveyCompletedAt?: string; diff --git a/utils/researchMetrics.ts b/utils/researchMetrics.ts index c651f9d..42f65a8 100644 --- a/utils/researchMetrics.ts +++ b/utils/researchMetrics.ts @@ -13,7 +13,7 @@ import type { } from "@/types/research"; export const RESEARCH_EXPORT_VERSION = "prototype-1.2"; -export const RESEARCH_SCHEMA_VERSION = "hidden-cost-game-research-schema-4"; +export const RESEARCH_SCHEMA_VERSION = "hidden-cost-game-research-schema-5"; export const RESEARCH_CONSENT_VERSION = "pilot-consent-v1"; const choiceCountKeys: Record