diff --git a/README.md b/README.md index 80eaf29..d464c38 100644 --- a/README.md +++ b/README.md @@ -368,6 +368,7 @@ Implemented measures include: - **Information sufficiency**: perceived adequacy of available information before the reveal. - **Structural impact**: post-reveal perceived effect of the hidden cost difference. - **Perspective change**: post-reveal reported change in views of lower-scoring players. +- **Remembered initial judgment**: post-reveal memory of the participant's pre-reveal attribution, responsibility rating, constraint-suspicion rating, and confidence in that memory. ## Computed metrics @@ -384,6 +385,11 @@ Computed metrics are derived from game and survey responses for prototype analys - **`burden`**: total treatment cost paid divided by total available income. - **`careAvoidance`**: skipped treatments plus half of partial treatments. - **`attributionCategoryShift`**: pre-reveal primary attribution compared with post-reveal revised primary attribution. +- **`rememberedResponsibilityError`**: remembered pre-reveal responsibility minus the original pre-reveal responsibility rating. +- **`rememberedConstraintSuspicionError`**: remembered pre-reveal constraint suspicion minus the original pre-reveal constraint-suspicion rating. +- **`rememberedPrimaryAttributionMatchesOriginal`**: whether remembered and original pre-reveal primary attribution match. +- **`memoryConfidence`**: confidence in memory of the initial interpretation. +- **`memoryDistortionMagnitude`**: absolute remembered responsibility error plus absolute remembered constraint-suspicion error. ## Contact and collaboration diff --git a/components/PostRevealSurveyForm.tsx b/components/PostRevealSurveyForm.tsx index d00d76e..132da83 100644 --- a/components/PostRevealSurveyForm.tsx +++ b/components/PostRevealSurveyForm.tsx @@ -8,6 +8,15 @@ import { HelperNote, LikertQuestion, PrimaryButton, SingleChoiceQuestion, TextQu import { getStoredSession, saveStoredSession } from "@/utils/session"; import type { PostRevealSurveyAnswers, ResearchSession } from "@/types/research"; +const rememberedPrimaryAttributionOptions = [ + "They made less effective decisions during the game", + "They accepted too much risk", + "Factors not shown in the score table may have influenced the results", + "Random variation or luck may have played a role", + "I did not have enough information to judge", + "I am not sure what I initially thought", +]; + const revisedPrimaryAttributionOptions = [ "Their decisions still seem to be the main explanation", "Hidden cost conditions seem to be the main explanation", @@ -17,6 +26,10 @@ const revisedPrimaryAttributionOptions = [ ]; const initialAnswers: PostRevealSurveyAnswers = { + rememberedPrimaryAttribution: "", + rememberedIndividualResponsibility: 0, + rememberedConstraintSuspicion: 0, + rememberedConfidence: 0, revisedPrimaryAttribution: "", revisedIndividualResponsibility: 0, perceivedStructuralImpact: 0, @@ -56,7 +69,7 @@ export function PostRevealSurveyForm() { saveStoredSession(nextSession); setSession(nextSession); - setAnswers(nextSession.postRevealSurvey ?? initialAnswers); + setAnswers({ ...initialAnswers, ...nextSession.postRevealSurvey }); }, [router]); useEffect(() => { @@ -71,6 +84,10 @@ export function PostRevealSurveyForm() { const openLength = answers.openRevision.trim().length; const isComplete = + answers.rememberedPrimaryAttribution.length > 0 && + answers.rememberedIndividualResponsibility > 0 && + answers.rememberedConstraintSuspicion > 0 && + answers.rememberedConfidence > 0 && answers.revisedPrimaryAttribution.length > 0 && answers.revisedIndividualResponsibility > 0 && answers.perceivedStructuralImpact > 0 && @@ -117,22 +134,34 @@ export function PostRevealSurveyForm() { Please answer after considering the cost rule that was just disclosed. These responses describe interpretations, not personal worth, fairness, or character. updateAnswer("rememberedPrimaryAttribution", value)} + /> + + updateAnswer("rememberedIndividualResponsibility", value)} /> + updateAnswer("rememberedConstraintSuspicion", value)} /> + updateAnswer("rememberedConfidence", value)} /> + + updateAnswer("revisedPrimaryAttribution", value)} /> - updateAnswer("revisedIndividualResponsibility", value)} /> - updateAnswer("perceivedStructuralImpact", value)} /> - updateAnswer("postProtestLegitimacy", value)} /> - updateAnswer("postRuleCorrectionSupport", value)} /> - updateAnswer("postRedistributionSupport", value)} /> - updateAnswer("initialJudgmentAccuracy", value)} /> - updateAnswer("perspectiveChange", value)} /> + updateAnswer("revisedIndividualResponsibility", value)} /> + updateAnswer("perceivedStructuralImpact", value)} /> + updateAnswer("postProtestLegitimacy", value)} /> + updateAnswer("postRuleCorrectionSupport", value)} /> + updateAnswer("postRedistributionSupport", value)} /> + updateAnswer("initialJudgmentAccuracy", value)} /> + updateAnswer("perspectiveChange", value)} /> - updateAnswer("openRevision", value)} minLength={10} maxLength={500} /> + updateAnswer("openRevision", value)} minLength={10} maxLength={500} /> {showValidation && !isComplete ? Please answer all closed-ended items and write 10–500 characters in the revision. Your draft has been saved in this browser. : null} diff --git a/components/ResultsTable.tsx b/components/ResultsTable.tsx index de901fd..63b10eb 100644 --- a/components/ResultsTable.tsx +++ b/components/ResultsTable.tsx @@ -111,7 +111,7 @@ function IndividualResults({ postRevealSurvey: PostRevealSurveyAnswers; }) { const gameSummary = calculateGameSummary(game); - const computedMetrics = calculateComputedResearchMetrics({ game, preRevealSurvey, postRevealSurvey }); + const computedMetrics = calculateComputedResearchMetrics({ game, preRevealSurvey, postRevealSurvey, preRevealSurveyOriginal: session.preRevealSurveyOriginal }); const interpretations = buildParticipantInterpretation(computedMetrics); return ( @@ -221,6 +221,11 @@ function MetricsGrid({ metrics }: { metrics: ComputedResearchMetrics }) { ["Cost burden ratio", metrics.burden], ["Care avoidance index", metrics.careAvoidance], ["Attribution category shift", `${metrics.attributionCategoryShift.pre} → ${metrics.attributionCategoryShift.post}`], + ["Remembered responsibility error", metrics.rememberedResponsibilityError], + ["Remembered constraint suspicion error", metrics.rememberedConstraintSuspicionError], + ["Remembered attribution matches original", metrics.rememberedPrimaryAttributionMatchesOriginal ? "Yes" : "No"], + ["Memory confidence", metrics.memoryConfidence], + ["Memory distortion magnitude", metrics.memoryDistortionMagnitude], ] as const; return ( diff --git a/docs/RESEARCH_NOTES.md b/docs/RESEARCH_NOTES.md index e389df6..ad1ac91 100644 --- a/docs/RESEARCH_NOTES.md +++ b/docs/RESEARCH_NOTES.md @@ -40,6 +40,7 @@ Computed metrics summarize analysis-ready outcomes, including: - `ruleCorrectionSupportShift` — change in support for correcting the rules. - `redistributionSupportShift` — change in redistributive support. - `certaintyCorrection`, `informationCaution`, and `perspectiveChange` — additional post-reveal interpretation and reflection measures. +- `rememberedResponsibilityError`, `rememberedConstraintSuspicionError`, `rememberedPrimaryAttributionMatchesOriginal`, `memoryConfidence`, and `memoryDistortionMagnitude` — post-reveal memory-of-initial-judgment measures compared against the original pre-reveal responses when available. ## Ethics and limitations diff --git a/lib/adminSubmissions.ts b/lib/adminSubmissions.ts index b8164d6..bd3ab3c 100644 --- a/lib/adminSubmissions.ts +++ b/lib/adminSubmissions.ts @@ -313,12 +313,20 @@ const CSV_COLUMNS: CsvColumn[] = [ payloadColumn("certainty_correction", ["computedMetrics", "certaintyCorrection"]), payloadColumn("information_caution", ["computedMetrics", "informationCaution"]), payloadColumn("perspective_change", ["computedMetrics", "perspectiveChange"]), + payloadColumn("remembered_responsibility_error", ["computedMetrics", "rememberedResponsibilityError"]), + payloadColumn("remembered_constraint_suspicion_error", ["computedMetrics", "rememberedConstraintSuspicionError"]), + payloadColumn("remembered_primary_attribution_matches_original", ["computedMetrics", "rememberedPrimaryAttributionMatchesOriginal"]), + payloadColumn("memory_confidence", ["computedMetrics", "memoryConfidence"]), + payloadColumn("memory_distortion_magnitude", ["computedMetrics", "memoryDistortionMagnitude"]), payloadColumn("pre_primary_attribution", ["preRevealSurvey", "primaryAttribution"]), + payloadColumn("remembered_primary_attribution", ["postRevealSurvey", "rememberedPrimaryAttribution"]), payloadColumn("post_revised_primary_attribution", ["postRevealSurvey", "revisedPrimaryAttribution"]), payloadColumn("pre_individual_responsibility", ["preRevealSurvey", "individualResponsibility"]), payloadColumn("post_revised_individual_responsibility", ["postRevealSurvey", "revisedIndividualResponsibility"]), + payloadColumn("remembered_individual_responsibility", ["postRevealSurvey", "rememberedIndividualResponsibility"]), payloadColumn("pre_constraint_suspicion", ["preRevealSurvey", "constraintSuspicion"]), payloadColumn("post_perceived_structural_impact", ["postRevealSurvey", "perceivedStructuralImpact"]), + payloadColumn("remembered_constraint_suspicion", ["postRevealSurvey", "rememberedConstraintSuspicion"]), payloadColumn("pre_protest_legitimacy", ["preRevealSurvey", "protestLegitimacy"]), payloadColumn("post_protest_legitimacy", ["postRevealSurvey", "postProtestLegitimacy"]), payloadColumn("pre_rule_correction_support", ["preRevealSurvey", "ruleCorrectionSupport"]), @@ -326,6 +334,7 @@ const CSV_COLUMNS: CsvColumn[] = [ payloadColumn("pre_redistribution_support", ["preRevealSurvey", "redistributionSupport"]), payloadColumn("post_redistribution_support", ["postRevealSurvey", "postRedistributionSupport"]), payloadColumn("pre_confidence", ["preRevealSurvey", "confidence"]), + payloadColumn("remembered_confidence", ["postRevealSurvey", "rememberedConfidence"]), payloadColumn("post_initial_judgment_accuracy", ["postRevealSurvey", "initialJudgmentAccuracy"]), payloadColumn("pre_information_sufficiency", ["preRevealSurvey", "informationSufficiency"]), payloadColumn("post_perspective_change", ["postRevealSurvey", "perspectiveChange"]), diff --git a/lib/researchExportSchema.ts b/lib/researchExportSchema.ts index cd026ac..775766b 100644 --- a/lib/researchExportSchema.ts +++ b/lib/researchExportSchema.ts @@ -122,6 +122,10 @@ export const preRevealSurveySchema = z export const postRevealSurveySchema = z .object({ + rememberedPrimaryAttribution: z.string().min(1), + rememberedIndividualResponsibility: likertSchema, + rememberedConstraintSuspicion: likertSchema, + rememberedConfidence: likertSchema, revisedPrimaryAttribution: z.string().min(1), revisedIndividualResponsibility: likertSchema, perceivedStructuralImpact: likertSchema, @@ -148,6 +152,11 @@ export const computedMetricsSchema = z careAvoidance: z.number(), delayedReveal: z.boolean(), standByInitialInterpretation: likertSchema.optional(), + rememberedResponsibilityError: z.number(), + rememberedConstraintSuspicionError: z.number(), + rememberedPrimaryAttributionMatchesOriginal: z.boolean(), + memoryConfidence: likertSchema, + memoryDistortionMagnitude: z.number().nonnegative(), attributionCategoryShift: z .object({ pre: z.string().min(1), diff --git a/sample-data/complete-research-export.example.json b/sample-data/complete-research-export.example.json index b989550..18ededf 100644 --- a/sample-data/complete-research-export.example.json +++ b/sample-data/complete-research-export.example.json @@ -1,6 +1,6 @@ { - "exportVersion": "prototype-1.1", - "schemaVersion": "research-export-v1", + "exportVersion": "prototype-1.2", + "schemaVersion": "hidden-cost-game-research-schema-3", "sessionId": "hcg-example-session", "consentVersion": "pilot-consent-v1", "serverSubmissionStatus": "not_submitted", @@ -143,6 +143,10 @@ "openExplanation": "This is fake sample text for validating a complete export." }, "postRevealSurvey": { + "rememberedPrimaryAttribution": "individual choices", + "rememberedIndividualResponsibility": 4, + "rememberedConstraintSuspicion": 4, + "rememberedConfidence": 5, "revisedPrimaryAttribution": "structural constraints", "revisedIndividualResponsibility": 3, "perceivedStructuralImpact": 6, @@ -164,6 +168,12 @@ "perspectiveChange": 6, "burden": 0.775, "careAvoidance": 1.5, + "delayedReveal": false, + "rememberedResponsibilityError": -1, + "rememberedConstraintSuspicionError": 1, + "rememberedPrimaryAttributionMatchesOriginal": true, + "memoryConfidence": 5, + "memoryDistortionMagnitude": 2, "attributionCategoryShift": { "pre": "individual choices", "post": "structural constraints" diff --git a/types/research.ts b/types/research.ts index e715e4f..44f560a 100644 --- a/types/research.ts +++ b/types/research.ts @@ -102,6 +102,10 @@ export type PreRevealSurveyAnswers = { }; export type PostRevealSurveyAnswers = { + rememberedPrimaryAttribution: string; + rememberedIndividualResponsibility: number; + rememberedConstraintSuspicion: number; + rememberedConfidence: number; revisedPrimaryAttribution: string; revisedIndividualResponsibility: number; perceivedStructuralImpact: number; @@ -188,6 +192,11 @@ export interface ComputedResearchMetrics { revisionMagnitude?: number; delayedReveal: boolean; standByInitialInterpretation?: number; + rememberedResponsibilityError: number; + rememberedConstraintSuspicionError: number; + rememberedPrimaryAttributionMatchesOriginal: boolean; + memoryConfidence: number; + memoryDistortionMagnitude: number; } export interface ResearchExportAssignedProfile { diff --git a/utils/researchMetrics.ts b/utils/researchMetrics.ts index 155824e..842e2b6 100644 --- a/utils/researchMetrics.ts +++ b/utils/researchMetrics.ts @@ -12,8 +12,8 @@ import type { TreatmentChoiceCounts, } from "@/types/research"; -export const RESEARCH_EXPORT_VERSION = "prototype-1.1"; -export const RESEARCH_SCHEMA_VERSION = "hidden-cost-game-research-schema-2"; +export const RESEARCH_EXPORT_VERSION = "prototype-1.2"; +export const RESEARCH_SCHEMA_VERSION = "hidden-cost-game-research-schema-3"; export const RESEARCH_CONSENT_VERSION = "pilot-consent-v1"; const choiceCountKeys: Record = { @@ -43,7 +43,11 @@ export function isPostRevealSurveyComplete(survey: PostRevealSurveyAnswers | und const openLength = survey?.openRevision.trim().length ?? 0; return Boolean( - survey?.revisedPrimaryAttribution && + survey?.rememberedPrimaryAttribution && + survey.rememberedIndividualResponsibility > 0 && + survey.rememberedConstraintSuspicion > 0 && + survey.rememberedConfidence > 0 && + survey.revisedPrimaryAttribution && survey.revisedIndividualResponsibility > 0 && survey.perceivedStructuralImpact > 0 && survey.postProtestLegitimacy > 0 && @@ -114,6 +118,10 @@ export function calculateComputedResearchMetrics({ preRevealCommitment?: ResearchSession["preRevealCommitment"]; }): ComputedResearchMetrics { const summary = calculateGameSummary(game); + const originalPreRevealSurvey = preRevealSurveyOriginal ?? preRevealSurvey; + const rememberedResponsibilityError = postRevealSurvey.rememberedIndividualResponsibility - originalPreRevealSurvey.individualResponsibility; + const rememberedConstraintSuspicionError = postRevealSurvey.rememberedConstraintSuspicion - originalPreRevealSurvey.constraintSuspicion; + const memoryDistortionMagnitude = Math.abs(rememberedResponsibilityError) + Math.abs(rememberedConstraintSuspicionError); const responsibilityRevisionDelta = preRevealSurveyRevisedAfterReveal && preRevealSurveyOriginal ? preRevealSurveyRevisedAfterReveal.individualResponsibility - preRevealSurveyOriginal.individualResponsibility : undefined; const constraintSuspicionRevisionDelta = preRevealSurveyRevisedAfterReveal && preRevealSurveyOriginal ? preRevealSurveyRevisedAfterReveal.constraintSuspicion - preRevealSurveyOriginal.constraintSuspicion : undefined; const protestLegitimacyRevisionDelta = preRevealSurveyRevisedAfterReveal && preRevealSurveyOriginal ? preRevealSurveyRevisedAfterReveal.protestLegitimacy - preRevealSurveyOriginal.protestLegitimacy : undefined; @@ -145,6 +153,11 @@ export function calculateComputedResearchMetrics({ careAvoidance: summary.skippedTreatmentChoices + 0.5 * summary.partialTreatmentChoices, delayedReveal: revealTimingCondition?.condition === "delayed-reveal", ...(preRevealCommitment ? { standByInitialInterpretation: preRevealCommitment.standByInitialInterpretation } : {}), + rememberedResponsibilityError, + rememberedConstraintSuspicionError, + rememberedPrimaryAttributionMatchesOriginal: postRevealSurvey.rememberedPrimaryAttribution === originalPreRevealSurvey.primaryAttribution, + memoryConfidence: postRevealSurvey.rememberedConfidence, + memoryDistortionMagnitude: roundMetric(memoryDistortionMagnitude), attributionCategoryShift: { pre: preRevealSurvey.primaryAttribution, post: postRevealSurvey.revisedPrimaryAttribution,