library(readxl)
library(dplyr)
library(ggplot2)
library(sandwich)
library(lmtest)
library(knitr)

Autonomous Weapons Survey

Background

This report summarizes a survey experiment on public perceptions of autonomous weapons use in a U.S.-China military crisis scenario. The study asks whether respondents see missile strikes as more or less intentional depending on the country scenario and whether the weapons system was human-operated or fully autonomous AI-enabled.

The primary outcome is perceived intentionality. Secondary outcomes examine escalation expectations, the perceived justifiability of escalation, and responsibility attributions to political leaders, military officials, human operators, and AI systems.

The preregistered analyses focus on the 2 x 2 factorial design: country scenario and weapons-system autonomy. Exploratory analyses examine manipulation-check passers, secondary outcomes, moderators, and downstream associations.

Method

Data

The Qualtrics export and demographic append file are in this folder.

qualtrics_file <- "Technology and National Security_August 5, 2026_09.34.xlsx"
demographics_file <- "6a68eb13df3330a45698e7cc_appends.rds"

Read the two files and merge respondent demographics onto the Qualtrics data by vsid.

qualtrics <- read_excel(qualtrics_file)
demographics <- readRDS(demographics_file)

survey <- qualtrics %>%
  filter(vsid != "vsid") %>%
  mutate(vsid = as.character(vsid)) %>%
  left_join(
    demographics %>%
      mutate(vsid = as.character(vsid)),
    by = "vsid"
  )

names(survey) <- gsub("\u00A0", " ", names(survey))

survey <- survey %>%
  rename(
    comfort_doctor = comfort_1,
    comfort_news = comfort_2,
    comfort_banks = comfort_3,
    ai_us = `ai_effectiveness _1`,
    ai_china = `ai_effectiveness _7`,
    ai_sk = `ai_effectiveness _8`,
    intentionality = intentionality_1,
    escalation = escalation_1,
    justified = justified_1,
    resp_leader = `responsibility _1`,
    resp_military = `responsibility _2`,
    resp_pilot = `responsibility _4`,
    resp_ai = `responsibility _5`
  )

Quick merge check.

nrow(qualtrics)
## [1] 3081
nrow(demographics)
## [1] 2915
nrow(survey)
## [1] 3080
sum(!survey$vsid %in% demographics$vsid)
## [1] 122

Recode Party ID

survey <- survey %>%
  mutate(
    pid = case_when(
      pid_base == "Democrat" ~ "Democrat",
      pid_base == "Republican" ~ "Republican",
      pid_base %in% c("Independent", "Other or none") &
        pid_lean == "The Democratic Party" ~ "Democrat",
      pid_base %in% c("Independent", "Other or none") &
        pid_lean == "The Republican Party" ~ "Republican",
      pid_base %in% c("Independent", "Other or none") &
        (pid_lean == "Neither" | is.na(pid_lean)) ~ "Independent",
      TRUE ~ NA_character_
    )
  )

Data Dictionary

The table below describes the variables used in the analyses. Variables listed as constructed are created later in this file from the raw Qualtrics and demographic variables.

Variable Source Description
vsid Raw / merge key Respondent identifier used to merge the Qualtrics data with the demographic append file.
surveyid Raw Survey identifier from Qualtrics or the survey panel.
consent Raw Consent response. Analyses keep respondents who indicated that they were 18 or older and agreed to participate.
condition_agressor Raw treatment assignment Country whose weapons system launched the missiles. Values are China or US. The variable name is misspelled in the export and is left unchanged to match the raw data.
condition_autonomy Raw treatment assignment Weapons-system condition. Values are human or autonomous.
country_scenario Constructed treatment indicator Equals 1 for Chinese missiles striking a U.S. ship and 0 for U.S. missiles striking a Chinese ship.
autonomous_ai Constructed treatment indicator Equals 1 for the autonomous-AI condition and 0 for the human-operated condition.
country_condition Constructed label Readable label for the country scenario used in tables and plots.
weapons_condition Constructed label Readable label for the weapons-system condition used in tables and plots.
weight Raw demographic / panel variable Respondent weight appended to the survey data.
survey_weight Constructed robustness variable Numeric copy of weight used in the weighted primary-model robustness check.
comfort_doctor Raw pre-treatment item Comfort with AI helping doctors identify possible medical conditions.
comfort_news Raw pre-treatment item Comfort with AI helping news organizations check factual claims before publication.
comfort_banks Raw pre-treatment item Comfort with AI helping banks detect unusual account activity.
ai_comfort Constructed covariate Average of the three comfort items, recoded from -2 to 2, where higher values indicate more comfort with AI.
ai_comfort_c Constructed covariate Mean-centered version of ai_comfort.
ai_us Raw pre-treatment item Respondent rating of U.S. AI model effectiveness on a 0 to 100 scale.
ai_china Raw pre-treatment item Respondent rating of Chinese AI model effectiveness on a 0 to 100 scale.
ai_sk Raw pre-treatment item Respondent rating of South Korean AI model effectiveness on a 0 to 100 scale.
relative_ai Constructed exploratory covariate U.S. AI effectiveness minus Chinese AI effectiveness, used in earlier exploratory plots and models.
relative_china_us_ai Constructed preregistered covariate Chinese AI effectiveness minus U.S. AI effectiveness, used in preregistered models.
relative_china_us_ai_c Constructed covariate Mean-centered version of relative_china_us_ai.
us_ai_effectiveness_c Constructed moderator Mean-centered U.S. AI effectiveness rating.
china_ai_effectiveness_c Constructed moderator Mean-centered Chinese AI effectiveness rating.
southkorea_ai_effectiveness_c Constructed moderator Mean-centered South Korean AI effectiveness rating.
intentionality Raw outcome Respondent rating of how intentional the missile strike was, measured on a 0 to 100 scale.
perceived_intentionality Constructed outcome Numeric copy of intentionality used in regression models.
escalation Raw outcome Respondent rating of the likelihood of escalation, measured on a 0 to 100 scale.
escalation_likelihood Constructed outcome Numeric copy of escalation used in preregistered secondary models.
justified Raw outcome Respondent rating of the justifiability of escalation, measured on a 0 to 100 scale.
escalation_justifiability Constructed outcome Numeric copy of justified used in preregistered secondary models.
resp_leader Raw outcome Responsibility attributed to senior political leaders and their top advisers.
resp_military Raw outcome Responsibility attributed to military officials overseeing the operation.
resp_pilot Raw outcome Responsibility attributed to the human operator of the weapons system. Asked only in human-operated conditions.
resp_ai Raw outcome Responsibility attributed to the AI weapons system itself. Asked only in autonomous-AI conditions.
pid_base Raw demographic variable Initial party identification response from the demographic append file.
pid_lean Raw demographic variable Party-leaning follow-up for respondents who identify as independent or other/none.
pid Constructed demographic covariate Party ID recode. Leaning independents are assigned to Democrat or Republican; non-leaning independents and missing leaners are kept as Independent.
pid_republican Constructed demographic covariate Binary party indicator used in regression models. 1 = Republican; 0 = Democrat or Independent.
age_group4 Raw demographic variable Four-category age group: 18-29, 30-49, 50-64, or 65+.
age_ordinal Constructed demographic covariate Ordinal age variable coded 1 = 18-29, 2 = 30-49, 3 = 50-64, 4 = 65+.
gender Raw demographic covariate Gender from the demographic append file.
gender_female Constructed demographic covariate Binary gender indicator used in regression models. 1 = Female; 0 = Male or Other.
education Raw demographic variable Education category from the demographic append file.
education_ordinal Constructed demographic covariate Ordinal education variable coded from lowest to highest educational attainment.
income Raw demographic variable Household income category from the demographic append file.
income_ordinal Constructed demographic covariate Ordinal income variable coded from lowest to highest income category.
race Raw demographic variable Race category from the demographic append file.
race_white Constructed demographic covariate Binary race indicator. Reference category is Non-white; comparison category is White.
hispanic Raw demographic covariate Hispanic identity. Reference category in models is No.
metro Raw demographic covariate Metropolitan status. Reference category in models is Non-metro.
compCheck_1 Raw manipulation check Respondent answer identifying which country’s weapons system launched the strike.
compCheck_2 Raw manipulation check Respondent answer identifying whether the weapons system was human-operated or autonomous.
comp_country_correct Constructed manipulation check Whether the respondent correctly identified the country whose weapons system launched the strike.
comp_autonomy_correct Constructed manipulation check Whether the respondent correctly identified the weapons-system condition.
comp_both_correct Constructed manipulation check Whether the respondent passed both manipulation checks.

Respondents per Condition

survey %>%
  count(condition_agressor, condition_autonomy)
## # A tibble: 5 × 3
##   condition_agressor condition_autonomy     n
##   <chr>              <chr>              <int>
## 1 China              autonomous           743
## 2 China              human                742
## 3 US                 autonomous           743
## 4 US                 human                742
## 5 <NA>               <NA>                 110

Results

Preregistered Analyses

The following sections implement the analysis plan in OSF_PreReg.docx. The primary analyses are intent-to-treat: manipulation-check failures are not excluded from the confirmatory models. The experimental indicators are coded so that the reference category is the human-operated U.S.-strike / Chinese-victim condition.

Preregistered Analysis Data

comfort_levels <- c(
  "Very uncomfortable",
  "Somewhat uncomfortable",
  "Neither comfortable or uncomfortable",
  "Somewhat comfortable",
  "Very comfortable"
)

education_levels <- c(
  "Some high school or less",
  "High school graduate or GED",
  "Some college, no degree",
  "2-year or associate degree",
  "4-year or bachelor degree",
  "Post-graduate degree"
)

income_levels <- c(
  "Less than $15,000",
  "$15,000 to under $50,000",
  "$50,000 to under $75,000",
  "$75,000 to under $100,000",
  "$100,000 to under $150,000",
  "$150,000 to under $200,000",
  "More than $200,000"
)

age_levels <- c("18-29", "30-49", "50-64", "65+")

prereg_analysis_data <- survey %>%
  filter(!is.na(consent),
         grepl("I am 18 or older", consent),
         !is.na(condition_agressor),
         !is.na(condition_autonomy),
         !is.na(intentionality)) %>%
  mutate(
    perceived_intentionality = as.numeric(intentionality),
    escalation_likelihood = as.numeric(escalation),
    escalation_justifiability = as.numeric(justified),
    country_scenario = ifelse(condition_agressor == "China", 1, 0),
    autonomous_ai = ifelse(condition_autonomy == "autonomous", 1, 0),
    country_condition = ifelse(country_scenario == 1, "Chinese strike / U.S. victim", "U.S. strike / Chinese victim"),
    weapons_condition = ifelse(autonomous_ai == 1, "Autonomous AI", "Human-operated"),
    survey_weight = suppressWarnings(as.numeric(weight)),
    us_ai_effectiveness = as.numeric(ai_us),
    china_ai_effectiveness = as.numeric(ai_china),
    southkorea_ai_effectiveness = as.numeric(ai_sk),
    relative_china_us_ai = china_ai_effectiveness - us_ai_effectiveness,
    age_group4 = factor(age_group4, levels = age_levels, ordered = TRUE),
    age_ordinal = as.numeric(age_group4),
    gender = factor(gender, levels = c("Male", "Female", "Other")),
    education = factor(education, levels = education_levels, ordered = TRUE),
    education_ordinal = as.numeric(education),
    income = factor(income, levels = income_levels, ordered = TRUE),
    income_ordinal = as.numeric(income),
    race = factor(
      race,
      levels = c(
        "White",
        "Black or African-American",
        "Asian or Asian-American",
        "Native American or American Indian",
        "Pacific Islander",
        "Mixed race",
        "Some other race"
      )
    ),
    race_white = factor(ifelse(race == "White", "White", "Non-white"),
                        levels = c("Non-white", "White")),
    hispanic = factor(hispanic, levels = c("No", "Yes")),
    metro = factor(metro, levels = c("Non-metro", "Metropolitan")),
    ai_comfort = (
      (match(comfort_doctor, comfort_levels) - 3) +
      (match(comfort_news, comfort_levels) - 3) +
      (match(comfort_banks, comfort_levels) - 3)
    ) / 3,
    pid = case_when(
      pid_base == "Democrat" ~ "Democrat",
      pid_base == "Republican" ~ "Republican",
      pid_base %in% c("Independent", "Other or none") &
        pid_lean == "The Democratic Party" ~ "Democrat",
      pid_base %in% c("Independent", "Other or none") &
        pid_lean == "The Republican Party" ~ "Republican",
      pid_base %in% c("Independent", "Other or none") &
        (pid_lean == "Neither" | is.na(pid_lean)) ~ "Independent",
      TRUE ~ NA_character_
    ),
    pid = factor(pid, levels = c("Independent", "Democrat", "Republican")),
    pid_republican = case_when(
      pid == "Republican" ~ 1,
      pid %in% c("Democrat", "Independent") ~ 0,
      TRUE ~ NA_real_
    ),
    gender_female = case_when(
      gender == "Female" ~ 1,
      gender %in% c("Male", "Other") ~ 0,
      TRUE ~ NA_real_
    ),
    comp_country_correct = case_when(
      condition_agressor == "China" ~ compCheck_1 == "A Chinese weapons system",
      condition_agressor == "US" ~ compCheck_1 == "A U.S. weapons system",
      TRUE ~ NA
    ),
    comp_autonomy_correct = case_when(
      condition_autonomy == "autonomous" ~ grepl("fully autonomous", compCheck_2),
      condition_autonomy == "human" ~ grepl("human-operated", compCheck_2),
      TRUE ~ NA
    ),
    comp_both_correct = comp_country_correct & comp_autonomy_correct
  )

prereg_primary_data <- prereg_analysis_data %>%
  filter(!is.na(perceived_intentionality),
         !is.na(ai_comfort),
         !is.na(relative_china_us_ai),
         !is.na(pid_republican),
         !is.na(age_ordinal),
         !is.na(gender_female),
         !is.na(education_ordinal),
         !is.na(income_ordinal),
         !is.na(race_white),
         !is.na(hispanic),
         !is.na(metro)) %>%
  mutate(
    ai_comfort_c = ai_comfort - mean(ai_comfort, na.rm = TRUE),
    us_ai_effectiveness_c = us_ai_effectiveness - mean(us_ai_effectiveness, na.rm = TRUE),
    china_ai_effectiveness_c = china_ai_effectiveness - mean(china_ai_effectiveness, na.rm = TRUE),
    southkorea_ai_effectiveness_c = southkorea_ai_effectiveness - mean(southkorea_ai_effectiveness, na.rm = TRUE),
    relative_china_us_ai_c = relative_china_us_ai - mean(relative_china_us_ai, na.rm = TRUE)
  )

prereg_sample_summary <- data.frame(
  sample = c("Assigned condition and primary outcome", "Covariate-complete primary model with demographics"),
  n = c(nrow(prereg_analysis_data), nrow(prereg_primary_data))
)

kable(prereg_sample_summary, caption = "Preregistered analytic sample sizes.")
Preregistered analytic sample sizes.
sample n
Assigned condition and primary outcome 2935
Covariate-complete primary model with demographics 2833

Covariate Balance Checks

Coding note: these tables are diagnostics for the randomized design. They are not used to decide whether to keep or drop covariates. The goal is simply to show whether pretreatment characteristics look similar across the four randomly assigned experimental cells.

balance_data <- prereg_analysis_data %>%
  mutate(
    condition_cell = paste(country_condition, weapons_condition, sep = " | "),
    republican = pid_republican,
    female = gender_female,
    white = ifelse(race_white == "White", 1, 0),
    hispanic_yes = ifelse(hispanic == "Yes", 1, 0),
    metro_yes = ifelse(metro == "Metropolitan", 1, 0)
  )

numeric_balance <- bind_rows(
  balance_data %>%
    group_by(condition_cell) %>%
    summarize(
      covariate = "AI comfort",
      n = sum(!is.na(ai_comfort)),
      mean = mean(ai_comfort, na.rm = TRUE),
      sd = sd(ai_comfort, na.rm = TRUE),
      .groups = "drop"
    ),
  balance_data %>%
    group_by(condition_cell) %>%
    summarize(
      covariate = "China minus U.S. AI effectiveness",
      n = sum(!is.na(relative_china_us_ai)),
      mean = mean(relative_china_us_ai, na.rm = TRUE),
      sd = sd(relative_china_us_ai, na.rm = TRUE),
      .groups = "drop"
    ),
  balance_data %>%
    group_by(condition_cell) %>%
    summarize(
      covariate = "Age group (ordinal)",
      n = sum(!is.na(age_ordinal)),
      mean = mean(age_ordinal, na.rm = TRUE),
      sd = sd(age_ordinal, na.rm = TRUE),
      .groups = "drop"
    ),
  balance_data %>%
    group_by(condition_cell) %>%
    summarize(
      covariate = "Education (ordinal)",
      n = sum(!is.na(education_ordinal)),
      mean = mean(education_ordinal, na.rm = TRUE),
      sd = sd(education_ordinal, na.rm = TRUE),
      .groups = "drop"
    ),
  balance_data %>%
    group_by(condition_cell) %>%
    summarize(
      covariate = "Income (ordinal)",
      n = sum(!is.na(income_ordinal)),
      mean = mean(income_ordinal, na.rm = TRUE),
      sd = sd(income_ordinal, na.rm = TRUE),
      .groups = "drop"
    )
) %>%
  arrange(covariate, condition_cell)

binary_balance <- bind_rows(
  balance_data %>%
    group_by(condition_cell) %>%
    summarize(
      covariate = "Republican",
      n = sum(!is.na(republican)),
      proportion = mean(republican, na.rm = TRUE),
      .groups = "drop"
    ),
  balance_data %>%
    group_by(condition_cell) %>%
    summarize(
      covariate = "Female",
      n = sum(!is.na(female)),
      proportion = mean(female, na.rm = TRUE),
      .groups = "drop"
    ),
  balance_data %>%
    group_by(condition_cell) %>%
    summarize(
      covariate = "White",
      n = sum(!is.na(white)),
      proportion = mean(white, na.rm = TRUE),
      .groups = "drop"
    ),
  balance_data %>%
    group_by(condition_cell) %>%
    summarize(
      covariate = "Hispanic",
      n = sum(!is.na(hispanic_yes)),
      proportion = mean(hispanic_yes, na.rm = TRUE),
      .groups = "drop"
    ),
  balance_data %>%
    group_by(condition_cell) %>%
    summarize(
      covariate = "Metropolitan",
      n = sum(!is.na(metro_yes)),
      proportion = mean(metro_yes, na.rm = TRUE),
      .groups = "drop"
    )
) %>%
  arrange(covariate, condition_cell)

kable(
  numeric_balance,
  digits = 2,
  caption = "Covariate balance check for continuous and ordinal pretreatment variables by experimental cell."
)
Covariate balance check for continuous and ordinal pretreatment variables by experimental cell.
condition_cell covariate n mean sd
Chinese strike / U.S. victim | Autonomous AI AI comfort 733 0.52 1.09
Chinese strike / U.S. victim | Human-operated AI comfort 735 0.37 1.08
U.S. strike / Chinese victim | Autonomous AI AI comfort 724 0.47 1.09
U.S. strike / Chinese victim | Human-operated AI comfort 730 0.43 1.10
Chinese strike / U.S. victim | Autonomous AI Age group (ordinal) 726 2.72 0.95
Chinese strike / U.S. victim | Human-operated Age group (ordinal) 728 2.72 0.94
U.S. strike / Chinese victim | Autonomous AI Age group (ordinal) 725 2.73 0.94
U.S. strike / Chinese victim | Human-operated Age group (ordinal) 725 2.70 0.95
Chinese strike / U.S. victim | Autonomous AI China minus U.S. AI effectiveness 720 -7.38 21.80
Chinese strike / U.S. victim | Human-operated China minus U.S. AI effectiveness 722 -8.52 25.11
U.S. strike / Chinese victim | Autonomous AI China minus U.S. AI effectiveness 716 -8.65 26.67
U.S. strike / Chinese victim | Human-operated China minus U.S. AI effectiveness 718 -6.80 24.39
Chinese strike / U.S. victim | Autonomous AI Education (ordinal) 727 3.91 1.51
Chinese strike / U.S. victim | Human-operated Education (ordinal) 729 3.90 1.54
U.S. strike / Chinese victim | Autonomous AI Education (ordinal) 725 3.89 1.52
U.S. strike / Chinese victim | Human-operated Education (ordinal) 725 3.98 1.48
Chinese strike / U.S. victim | Autonomous AI Income (ordinal) 727 3.68 1.74
Chinese strike / U.S. victim | Human-operated Income (ordinal) 729 3.50 1.70
U.S. strike / Chinese victim | Autonomous AI Income (ordinal) 725 3.52 1.64
U.S. strike / Chinese victim | Human-operated Income (ordinal) 724 3.60 1.70
kable(
  binary_balance,
  digits = 3,
  caption = "Covariate balance check for binary demographic variables by experimental cell."
)
Covariate balance check for binary demographic variables by experimental cell.
condition_cell covariate n proportion
Chinese strike / U.S. victim | Autonomous AI Female 727 0.549
Chinese strike / U.S. victim | Human-operated Female 729 0.539
U.S. strike / Chinese victim | Autonomous AI Female 725 0.570
U.S. strike / Chinese victim | Human-operated Female 725 0.539
Chinese strike / U.S. victim | Autonomous AI Hispanic 727 0.122
Chinese strike / U.S. victim | Human-operated Hispanic 729 0.132
U.S. strike / Chinese victim | Autonomous AI Hispanic 725 0.124
U.S. strike / Chinese victim | Human-operated Hispanic 725 0.112
Chinese strike / U.S. victim | Autonomous AI Metropolitan 727 0.864
Chinese strike / U.S. victim | Human-operated Metropolitan 728 0.889
U.S. strike / Chinese victim | Autonomous AI Metropolitan 725 0.884
U.S. strike / Chinese victim | Human-operated Metropolitan 725 0.887
Chinese strike / U.S. victim | Autonomous AI Republican 727 0.374
Chinese strike / U.S. victim | Human-operated Republican 729 0.362
U.S. strike / Chinese victim | Autonomous AI Republican 725 0.374
U.S. strike / Chinese victim | Human-operated Republican 725 0.370
Chinese strike / U.S. victim | Autonomous AI White 727 0.757
Chinese strike / U.S. victim | Human-operated White 729 0.734
U.S. strike / Chinese victim | Autonomous AI White 725 0.746
U.S. strike / Chinese victim | Human-operated White 725 0.757

Complete-Case Diagnostics

The primary adjusted model uses complete cases for the outcome, preregistered covariates, and demographic controls. This section reports how many respondents are excluded by that requirement and whether the retained and excluded respondents differ on observed variables. These diagnostics help contextualize the covariate-adjusted model; they do not change the preregistered estimand.

complete_case_diagnostics <- prereg_analysis_data %>%
  mutate(
    primary_model_sample = ifelse(ResponseId %in% prereg_primary_data$ResponseId, "Included", "Dropped"),
    republican = pid_republican,
    female = gender_female,
    white = ifelse(race_white == "White", 1, 0),
    hispanic_yes = ifelse(hispanic == "Yes", 1, 0),
    metro_yes = ifelse(metro == "Metropolitan", 1, 0)
  )

complete_case_counts <- complete_case_diagnostics %>%
  count(primary_model_sample) %>%
  mutate(percent = n / sum(n))

complete_case_numeric <- bind_rows(
  complete_case_diagnostics %>%
    group_by(primary_model_sample) %>%
    summarize(variable = "Perceived intentionality", n = sum(!is.na(perceived_intentionality)), mean = mean(perceived_intentionality, na.rm = TRUE), sd = sd(perceived_intentionality, na.rm = TRUE), .groups = "drop"),
  complete_case_diagnostics %>%
    group_by(primary_model_sample) %>%
    summarize(variable = "AI comfort", n = sum(!is.na(ai_comfort)), mean = mean(ai_comfort, na.rm = TRUE), sd = sd(ai_comfort, na.rm = TRUE), .groups = "drop"),
  complete_case_diagnostics %>%
    group_by(primary_model_sample) %>%
    summarize(variable = "China minus U.S. AI effectiveness", n = sum(!is.na(relative_china_us_ai)), mean = mean(relative_china_us_ai, na.rm = TRUE), sd = sd(relative_china_us_ai, na.rm = TRUE), .groups = "drop"),
  complete_case_diagnostics %>%
    group_by(primary_model_sample) %>%
    summarize(variable = "Age group (ordinal)", n = sum(!is.na(age_ordinal)), mean = mean(age_ordinal, na.rm = TRUE), sd = sd(age_ordinal, na.rm = TRUE), .groups = "drop"),
  complete_case_diagnostics %>%
    group_by(primary_model_sample) %>%
    summarize(variable = "Education (ordinal)", n = sum(!is.na(education_ordinal)), mean = mean(education_ordinal, na.rm = TRUE), sd = sd(education_ordinal, na.rm = TRUE), .groups = "drop"),
  complete_case_diagnostics %>%
    group_by(primary_model_sample) %>%
    summarize(variable = "Income (ordinal)", n = sum(!is.na(income_ordinal)), mean = mean(income_ordinal, na.rm = TRUE), sd = sd(income_ordinal, na.rm = TRUE), .groups = "drop")
) %>%
  arrange(variable, primary_model_sample)

complete_case_binary <- bind_rows(
  complete_case_diagnostics %>%
    group_by(primary_model_sample) %>%
    summarize(variable = "Republican", n = sum(!is.na(republican)), proportion = mean(republican, na.rm = TRUE), .groups = "drop"),
  complete_case_diagnostics %>%
    group_by(primary_model_sample) %>%
    summarize(variable = "Female", n = sum(!is.na(female)), proportion = mean(female, na.rm = TRUE), .groups = "drop"),
  complete_case_diagnostics %>%
    group_by(primary_model_sample) %>%
    summarize(variable = "White", n = sum(!is.na(white)), proportion = mean(white, na.rm = TRUE), .groups = "drop"),
  complete_case_diagnostics %>%
    group_by(primary_model_sample) %>%
    summarize(variable = "Hispanic", n = sum(!is.na(hispanic_yes)), proportion = mean(hispanic_yes, na.rm = TRUE), .groups = "drop"),
  complete_case_diagnostics %>%
    group_by(primary_model_sample) %>%
    summarize(variable = "Metropolitan", n = sum(!is.na(metro_yes)), proportion = mean(metro_yes, na.rm = TRUE), .groups = "drop")
) %>%
  arrange(variable, primary_model_sample)

kable(complete_case_counts, digits = 3, caption = "Respondents included in and dropped from the covariate-complete primary model.")
Respondents included in and dropped from the covariate-complete primary model.
primary_model_sample n percent
Dropped 102 0.035
Included 2833 0.965
kable(complete_case_numeric, digits = 2, caption = "Observed differences between included and dropped respondents for numeric variables.")
Observed differences between included and dropped respondents for numeric variables.
primary_model_sample variable n mean sd
Dropped AI comfort 89 0.17 1.19
Included AI comfort 2833 0.46 1.09
Dropped Age group (ordinal) 71 2.75 1.02
Included Age group (ordinal) 2833 2.71 0.94
Dropped China minus U.S. AI effectiveness 43 -9.28 24.01
Included China minus U.S. AI effectiveness 2833 -7.82 24.57
Dropped Education (ordinal) 73 4.04 1.42
Included Education (ordinal) 2833 3.92 1.51
Dropped Income (ordinal) 72 3.58 1.75
Included Income (ordinal) 2833 3.57 1.69
Dropped Perceived intentionality 102 60.57 28.12
Included Perceived intentionality 2833 62.92 24.84
kable(complete_case_binary, digits = 3, caption = "Observed differences between included and dropped respondents for binary variables.")
Observed differences between included and dropped respondents for binary variables.
primary_model_sample variable n proportion
Dropped Female 73 0.507
Included Female 2833 0.550
Dropped Hispanic 73 0.096
Included Hispanic 2833 0.123
Dropped Metropolitan 72 0.903
Included Metropolitan 2833 0.880
Dropped Republican 73 0.301
Included Republican 2833 0.372
Dropped White 73 0.726
Included White 2833 0.749

Cell Means for Primary Outcome

prereg_cell_means <- prereg_analysis_data %>%
  group_by(country_condition, weapons_condition) %>%
  summarize(
    n = n(),
    mean = mean(perceived_intentionality),
    se = sd(perceived_intentionality) / sqrt(n),
    ci_low = mean - 1.96 * se,
    ci_high = mean + 1.96 * se,
    .groups = "drop"
  )

kable(
  prereg_cell_means,
  digits = 2,
  caption = "Mean perceived intentionality and 95% confidence intervals by experimental cell."
)
Mean perceived intentionality and 95% confidence intervals by experimental cell.
country_condition weapons_condition n mean se ci_low ci_high
Chinese strike / U.S. victim Autonomous AI 737 62.09 0.87 60.38 63.80
Chinese strike / U.S. victim Human-operated 737 72.11 0.85 70.45 73.77
U.S. strike / Chinese victim Autonomous AI 728 54.21 0.94 52.37 56.06
U.S. strike / Chinese victim Human-operated 733 62.83 0.91 61.06 64.61
prereg_cell_means_plot_data <- prereg_cell_means %>%
  mutate(
    country_condition = factor(
      country_condition,
      levels = c("Chinese strike / U.S. victim", "U.S. strike / Chinese victim"),
      labels = c("Chinese strike", "U.S. strike")
    ),
    weapons_condition = factor(
      weapons_condition,
      levels = c("Human-operated", "Autonomous AI")
    )
  )

ggplot(
  prereg_cell_means_plot_data,
  aes(x = weapons_condition, y = mean, color = country_condition, group = country_condition)
) +
  geom_line(linewidth = 1, alpha = 0.45) +
  geom_point(size = 3.3) +
  geom_errorbar(aes(ymin = ci_low, ymax = ci_high), width = 0.08, linewidth = 0.8) +
  scale_color_manual(
    values = c("Chinese strike" = "#A63A33", "U.S. strike" = "#235A8C"),
    name = "Country scenario"
  ) +
  scale_y_continuous(
    limits = c(50, 80),
    breaks = seq(50, 80, 5)
  ) +
  labs(
    title = "Preregistered Cell Means for Perceived Intentionality",
    x = "Weapons-system condition",
    y = "Perceived intentionality",
    caption = "Error bars are 95% confidence intervals."
  ) +
  theme_minimal(base_size = 14) +
  theme(
    plot.title = element_text(face = "bold", hjust = 0.5),
    legend.position = "top",
    panel.grid.major.x = element_blank()
  )

Primary Preregistered Intentionality Model

This model regresses perceived intentionality on the country-scenario indicator, the autonomous-AI indicator, their interaction, preregistered covariates, and demographic controls. Continuous covariates are demeaned. HC2 heteroskedasticity-consistent standard errors are used for two-sided tests with alpha = .05.

prereg_primary_model <- lm(
  perceived_intentionality ~ country_scenario * autonomous_ai +
    ai_comfort_c + relative_china_us_ai_c + pid_republican +
    age_ordinal + gender_female + education_ordinal + income_ordinal +
    race_white + hispanic + metro,
  data = prereg_primary_data
)

prereg_primary_vcov <- vcovHC(prereg_primary_model, type = "HC2")
prereg_primary_coeftest <- coeftest(prereg_primary_model, vcov. = prereg_primary_vcov)
prereg_primary_ci <- coefci(prereg_primary_model, vcov. = prereg_primary_vcov)

prereg_primary_results <- data.frame(
  term = rownames(prereg_primary_coeftest),
  estimate = prereg_primary_coeftest[, 1],
  hc2_se = prereg_primary_coeftest[, 2],
  statistic = prereg_primary_coeftest[, 3],
  p_value = prereg_primary_coeftest[, 4],
  ci_low = prereg_primary_ci[, 1],
  ci_high = prereg_primary_ci[, 2],
  row.names = NULL
)

kable(
  prereg_primary_results,
  digits = 3,
  caption = "Primary preregistered OLS model with HC2 standard errors."
)
Primary preregistered OLS model with HC2 standard errors.
term estimate hc2_se statistic p_value ci_low ci_high
(Intercept) 64.156 2.451 26.180 0.000 59.351 68.962
country_scenario 9.039 1.253 7.214 0.000 6.582 11.496
autonomous_ai -9.030 1.316 -6.864 0.000 -11.609 -6.450
ai_comfort_c 1.284 0.455 2.825 0.005 0.393 2.176
relative_china_us_ai_c 0.047 0.021 2.251 0.024 0.006 0.088
pid_republican 2.034 0.975 2.087 0.037 0.123 3.945
age_ordinal -0.578 0.500 -1.155 0.248 -1.558 0.403
gender_female 0.142 0.925 0.154 0.878 -1.672 1.956
education_ordinal -0.178 0.345 -0.516 0.606 -0.854 0.498
income_ordinal -0.047 0.325 -0.144 0.886 -0.683 0.590
race_whiteWhite -1.212 1.091 -1.110 0.267 -3.351 0.928
hispanicYes 2.772 1.378 2.012 0.044 0.071 5.473
metroMetropolitan 1.390 1.391 0.999 0.318 -1.338 4.117
country_scenario:autonomous_ai -1.231 1.806 -0.682 0.496 -4.773 2.311

Unadjusted and Adjusted Treatment Estimates

Coding note: the unadjusted model estimates the treatment-cell differences using only the randomized country-scenario indicator, the autonomous-AI indicator, and their interaction. The adjusted model is the preregistered primary model above. Showing them side by side makes it easier to see whether covariate adjustment changes the substantive treatment estimates.

prereg_unadjusted_full_model <- lm(
  perceived_intentionality ~ country_scenario * autonomous_ai,
  data = prereg_analysis_data
)

prereg_unadjusted_complete_model <- lm(
  perceived_intentionality ~ country_scenario * autonomous_ai,
  data = prereg_primary_data
)

prereg_unadjusted_full_vcov <- vcovHC(prereg_unadjusted_full_model, type = "HC2")
prereg_unadjusted_full_coeftest <- coeftest(prereg_unadjusted_full_model, vcov. = prereg_unadjusted_full_vcov)
prereg_unadjusted_full_ci <- coefci(prereg_unadjusted_full_model, vcov. = prereg_unadjusted_full_vcov)

prereg_unadjusted_complete_vcov <- vcovHC(prereg_unadjusted_complete_model, type = "HC2")
prereg_unadjusted_complete_coeftest <- coeftest(prereg_unadjusted_complete_model, vcov. = prereg_unadjusted_complete_vcov)
prereg_unadjusted_complete_ci <- coefci(prereg_unadjusted_complete_model, vcov. = prereg_unadjusted_complete_vcov)

prereg_unadjusted_full_results <- data.frame(
  model = "Unadjusted, full outcome sample",
  term = rownames(prereg_unadjusted_full_coeftest),
  estimate = prereg_unadjusted_full_coeftest[, 1],
  hc2_se = prereg_unadjusted_full_coeftest[, 2],
  p_value = prereg_unadjusted_full_coeftest[, 4],
  ci_low = prereg_unadjusted_full_ci[, 1],
  ci_high = prereg_unadjusted_full_ci[, 2],
  row.names = NULL
)

prereg_unadjusted_complete_results <- data.frame(
  model = "Unadjusted, covariate-complete sample",
  term = rownames(prereg_unadjusted_complete_coeftest),
  estimate = prereg_unadjusted_complete_coeftest[, 1],
  hc2_se = prereg_unadjusted_complete_coeftest[, 2],
  p_value = prereg_unadjusted_complete_coeftest[, 4],
  ci_low = prereg_unadjusted_complete_ci[, 1],
  ci_high = prereg_unadjusted_complete_ci[, 2],
  row.names = NULL
)

prereg_adjusted_treatment_results <- prereg_primary_results %>%
  mutate(model = "Adjusted, preregistered primary model", .before = term)

unadjusted_adjusted_treatment_terms <- c(
  "(Intercept)",
  "country_scenario",
  "autonomous_ai",
  "country_scenario:autonomous_ai"
)

prereg_unadjusted_adjusted_comparison <- bind_rows(
  prereg_unadjusted_full_results,
  prereg_unadjusted_complete_results,
  prereg_adjusted_treatment_results
) %>%
  filter(term %in% unadjusted_adjusted_treatment_terms)

kable(
  prereg_unadjusted_adjusted_comparison,
  digits = 3,
  caption = "Unadjusted and adjusted treatment estimates for perceived intentionality. All standard errors are HC2."
)
Unadjusted and adjusted treatment estimates for perceived intentionality. All standard errors are HC2.
model term estimate hc2_se p_value ci_low ci_high statistic
Unadjusted, full outcome sample (Intercept) 62.834 0.906 0.000 61.058 64.609 NA
Unadjusted, full outcome sample country_scenario 9.276 1.240 0.000 6.845 11.708 NA
Unadjusted, full outcome sample autonomous_ai -8.622 1.306 0.000 -11.182 -6.062 NA
Unadjusted, full outcome sample country_scenario:autonomous_ai -1.397 1.784 0.434 -4.894 2.100 NA
Unadjusted, covariate-complete sample (Intercept) 63.218 0.918 0.000 61.417 65.019 NA
Unadjusted, covariate-complete sample country_scenario 8.958 1.253 0.000 6.501 11.415 NA
Unadjusted, covariate-complete sample autonomous_ai -9.023 1.318 0.000 -11.608 -6.439 NA
Unadjusted, covariate-complete sample country_scenario:autonomous_ai -1.090 1.805 0.546 -4.629 2.449 NA
Adjusted, preregistered primary model (Intercept) 64.156 2.451 0.000 59.351 68.962 26.180
Adjusted, preregistered primary model country_scenario 9.039 1.253 0.000 6.582 11.496 7.214
Adjusted, preregistered primary model autonomous_ai -9.030 1.316 0.000 -11.609 -6.450 -6.864
Adjusted, preregistered primary model country_scenario:autonomous_ai -1.231 1.806 0.496 -4.773 2.311 -0.682

Planned Contrasts for H1-H4

prereg_contrast_weights <- matrix(
  0,
  nrow = 4,
  ncol = length(coef(prereg_primary_model))
)
colnames(prereg_contrast_weights) <- names(coef(prereg_primary_model))
rownames(prereg_contrast_weights) <- c(
  "H1: Chinese strike vs. U.S. strike among human-operated systems",
  "Autonomous AI effect in U.S.-strike / Chinese-victim condition",
  "Autonomous AI effect in Chinese-strike / U.S.-victim condition",
  "Country-scenario by autonomous-AI interaction"
)

prereg_contrast_weights["H1: Chinese strike vs. U.S. strike among human-operated systems", "country_scenario"] <- 1
prereg_contrast_weights["Autonomous AI effect in U.S.-strike / Chinese-victim condition", "autonomous_ai"] <- 1
prereg_contrast_weights["Autonomous AI effect in Chinese-strike / U.S.-victim condition", "autonomous_ai"] <- 1
prereg_contrast_weights["Autonomous AI effect in Chinese-strike / U.S.-victim condition", "country_scenario:autonomous_ai"] <- 1
prereg_contrast_weights["Country-scenario by autonomous-AI interaction", "country_scenario:autonomous_ai"] <- 1

prereg_contrast_estimate <- as.numeric(prereg_contrast_weights %*% coef(prereg_primary_model))
prereg_contrast_se <- sqrt(diag(prereg_contrast_weights %*% prereg_primary_vcov %*% t(prereg_contrast_weights)))
prereg_contrast_statistic <- prereg_contrast_estimate / prereg_contrast_se
prereg_contrast_p <- 2 * pt(abs(prereg_contrast_statistic), df = df.residual(prereg_primary_model), lower.tail = FALSE)
prereg_contrast_critical <- qt(0.975, df = df.residual(prereg_primary_model))

prereg_contrasts <- data.frame(
  contrast = rownames(prereg_contrast_weights),
  estimate = prereg_contrast_estimate,
  hc2_se = prereg_contrast_se,
  statistic = prereg_contrast_statistic,
  p_value = prereg_contrast_p,
  ci_low = prereg_contrast_estimate - prereg_contrast_critical * prereg_contrast_se,
  ci_high = prereg_contrast_estimate + prereg_contrast_critical * prereg_contrast_se,
  row.names = NULL
)

kable(
  prereg_contrasts,
  digits = 3,
  caption = "Planned treatment contrasts using HC2 standard errors."
)
Planned treatment contrasts using HC2 standard errors.
contrast estimate hc2_se statistic p_value ci_low ci_high
H1: Chinese strike vs. U.S. strike among human-operated systems 9.039 1.253 7.214 0.000 6.582 11.496
Autonomous AI effect in U.S.-strike / Chinese-victim condition -9.030 1.316 -6.864 0.000 -11.609 -6.450
Autonomous AI effect in Chinese-strike / U.S.-victim condition -10.261 1.237 -8.295 0.000 -12.686 -7.835
Country-scenario by autonomous-AI interaction -1.231 1.806 -0.682 0.496 -4.773 2.311
prereg_contrast_plot_data <- prereg_contrasts %>%
  mutate(
    contrast_label = case_when(
      grepl("^H1", contrast) ~ "H1: Chinese vs. U.S. strike\namong human-operated systems",
      grepl("U.S.-strike", contrast) ~ "Autonomous effect\nin U.S.-strike condition",
      grepl("Chinese-strike", contrast) ~ "Autonomous effect\nin Chinese-strike condition",
      TRUE ~ "Country scenario x\nautonomous AI interaction"
    ),
    contrast_label = factor(
      contrast_label,
      levels = c(
        "H1: Chinese vs. U.S. strike\namong human-operated systems",
        "Autonomous effect\nin U.S.-strike condition",
        "Autonomous effect\nin Chinese-strike condition",
        "Country scenario x\nautonomous AI interaction"
      )
    )
  )

ggplot(prereg_contrast_plot_data, aes(x = estimate, y = contrast_label)) +
  geom_vline(xintercept = 0, linewidth = 0.5, color = "gray55") +
  geom_errorbarh(aes(xmin = ci_low, xmax = ci_high), height = 0.14, linewidth = 0.8) +
  geom_point(size = 3) +
  scale_y_discrete(limits = rev(levels(prereg_contrast_plot_data$contrast_label))) +
  labs(
    title = "Preregistered Treatment Contrasts for Intentionality",
    x = "Estimated effect on perceived intentionality",
    y = NULL,
    caption = "Points are OLS estimates; error bars are 95% CIs using HC2 standard errors."
  ) +
  theme_minimal(base_size = 13) +
  theme(
    plot.title = element_text(face = "bold", hjust = 0.5),
    panel.grid.major.y = element_blank()
  )
## Warning: `geom_errobarh()` was deprecated in ggplot2 4.0.0.
## ℹ Please use the `orientation` argument of `geom_errorbar()` instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.
## `height` was translated to `width`.

Weighted Primary Model

Coding note: this is a robustness check using the appended respondent weight. It keeps the same outcome, treatment indicators, interaction, preregistered covariates, and demographic controls as the primary model. The unweighted covariate-adjusted model remains the primary preregistered analysis unless the preregistration explicitly says weights are primary.

prereg_weighted_data <- prereg_primary_data %>%
  filter(!is.na(survey_weight),
         survey_weight > 0)

prereg_weighted_primary_model <- lm(
  perceived_intentionality ~ country_scenario * autonomous_ai +
    ai_comfort_c + relative_china_us_ai_c + pid_republican +
    age_ordinal + gender_female + education_ordinal + income_ordinal +
    race_white + hispanic + metro,
  data = prereg_weighted_data,
  weights = survey_weight
)

prereg_weighted_primary_vcov <- vcovHC(prereg_weighted_primary_model, type = "HC2")
prereg_weighted_primary_coeftest <- coeftest(prereg_weighted_primary_model, vcov. = prereg_weighted_primary_vcov)
prereg_weighted_primary_ci <- coefci(prereg_weighted_primary_model, vcov. = prereg_weighted_primary_vcov)

prereg_weighted_primary_results <- data.frame(
  term = rownames(prereg_weighted_primary_coeftest),
  estimate = prereg_weighted_primary_coeftest[, 1],
  hc2_se = prereg_weighted_primary_coeftest[, 2],
  statistic = prereg_weighted_primary_coeftest[, 3],
  p_value = prereg_weighted_primary_coeftest[, 4],
  ci_low = prereg_weighted_primary_ci[, 1],
  ci_high = prereg_weighted_primary_ci[, 2],
  row.names = NULL
)

kable(
  prereg_weighted_primary_results,
  digits = 3,
  caption = paste0("Weighted version of the primary preregistered model using HC2 standard errors (N = ", nrow(prereg_weighted_data), ").")
)
Weighted version of the primary preregistered model using HC2 standard errors (N = 2833).
term estimate hc2_se statistic p_value ci_low ci_high
(Intercept) 60.343 2.962 20.375 0.000 54.536 66.150
country_scenario 8.639 1.527 5.658 0.000 5.645 11.633
autonomous_ai -9.394 1.638 -5.735 0.000 -12.606 -6.182
ai_comfort_c 1.278 0.570 2.244 0.025 0.161 2.395
relative_china_us_ai_c 0.029 0.024 1.187 0.235 -0.019 0.077
pid_republican 1.869 1.186 1.575 0.115 -0.457 4.195
age_ordinal 0.131 0.590 0.223 0.824 -1.025 1.288
gender_female -0.282 1.143 -0.247 0.805 -2.524 1.960
education_ordinal -0.141 0.433 -0.326 0.744 -0.991 0.708
income_ordinal 0.064 0.428 0.149 0.882 -0.775 0.903
race_whiteWhite -2.445 1.386 -1.764 0.078 -5.162 0.273
hispanicYes 3.827 1.551 2.467 0.014 0.786 6.869
metroMetropolitan 3.462 1.715 2.019 0.044 0.100 6.824
country_scenario:autonomous_ai 0.460 2.238 0.206 0.837 -3.928 4.848

Alternative Demographic Coding Robustness Check

The primary model treats age, education, and income as ordinal controls. This robustness check treats those variables as categorical factors instead. The purpose is to check whether the treatment estimates depend on the linearity assumption implied by ordinal coding.

prereg_categorical_demographics_model <- lm(
  perceived_intentionality ~ country_scenario * autonomous_ai +
    ai_comfort_c + relative_china_us_ai_c + pid_republican +
    age_group4 + gender_female + education + income +
    race_white + hispanic + metro,
  data = prereg_primary_data
)

prereg_categorical_demographics_vcov <- vcovHC(prereg_categorical_demographics_model, type = "HC2")
prereg_categorical_demographics_coeftest <- coeftest(prereg_categorical_demographics_model, vcov. = prereg_categorical_demographics_vcov)
prereg_categorical_demographics_ci <- coefci(prereg_categorical_demographics_model, vcov. = prereg_categorical_demographics_vcov)

prereg_categorical_demographics_results <- data.frame(
  term = rownames(prereg_categorical_demographics_coeftest),
  estimate = prereg_categorical_demographics_coeftest[, 1],
  hc2_se = prereg_categorical_demographics_coeftest[, 2],
  statistic = prereg_categorical_demographics_coeftest[, 3],
  p_value = prereg_categorical_demographics_coeftest[, 4],
  ci_low = prereg_categorical_demographics_ci[, 1],
  ci_high = prereg_categorical_demographics_ci[, 2],
  row.names = NULL
)

prereg_categorical_demographics_treatment_results <- prereg_categorical_demographics_results %>%
  filter(term %in% c("country_scenario", "autonomous_ai", "country_scenario:autonomous_ai"))

kable(
  prereg_categorical_demographics_treatment_results,
  digits = 3,
  caption = "Treatment coefficients from the robustness model with age, education, and income entered as categorical controls."
)
Treatment coefficients from the robustness model with age, education, and income entered as categorical controls.
term estimate hc2_se statistic p_value ci_low ci_high
country_scenario 9.007 1.254 7.183 0.000 6.548 11.466
autonomous_ai -9.066 1.314 -6.897 0.000 -11.644 -6.489
country_scenario:autonomous_ai -1.221 1.806 -0.676 0.499 -4.762 2.319

Treatment-Estimate Robustness Summary

This table gathers the focal treatment coefficients across the main specification checks above. It is intended as a compact audit of whether the main treatment pattern changes across unadjusted, adjusted, weighted, and categorical-demographic specifications.

treatment_robustness_results <- bind_rows(
  prereg_unadjusted_full_results %>%
    mutate(specification = "Unadjusted, full outcome sample", .before = term),
  prereg_unadjusted_complete_results %>%
    mutate(specification = "Unadjusted, covariate-complete sample", .before = term),
  prereg_primary_results %>%
    mutate(specification = "Adjusted primary model", .before = term),
  prereg_weighted_primary_results %>%
    mutate(specification = "Weighted adjusted primary model", .before = term),
  prereg_categorical_demographics_results %>%
    mutate(specification = "Adjusted model with categorical demographics", .before = term)
) %>%
  filter(term %in% c("country_scenario", "autonomous_ai", "country_scenario:autonomous_ai")) %>%
  mutate(
    term_label = case_when(
      term == "country_scenario" ~ "Chinese vs. U.S. strike among human-operated systems",
      term == "autonomous_ai" ~ "Autonomous effect in U.S.-strike condition",
      TRUE ~ "Country scenario x autonomous AI interaction"
    )
  ) %>%
  select(specification, term_label, estimate, hc2_se, p_value, ci_low, ci_high)

kable(
  treatment_robustness_results,
  digits = 3,
  caption = "Focal treatment coefficients across primary and robustness specifications."
)
Focal treatment coefficients across primary and robustness specifications.
specification term_label estimate hc2_se p_value ci_low ci_high
Unadjusted, full outcome sample Chinese vs. U.S. strike among human-operated systems 9.276 1.240 0.000 6.845 11.708
Unadjusted, full outcome sample Autonomous effect in U.S.-strike condition -8.622 1.306 0.000 -11.182 -6.062
Unadjusted, full outcome sample Country scenario x autonomous AI interaction -1.397 1.784 0.434 -4.894 2.100
Unadjusted, covariate-complete sample Chinese vs. U.S. strike among human-operated systems 8.958 1.253 0.000 6.501 11.415
Unadjusted, covariate-complete sample Autonomous effect in U.S.-strike condition -9.023 1.318 0.000 -11.608 -6.439
Unadjusted, covariate-complete sample Country scenario x autonomous AI interaction -1.090 1.805 0.546 -4.629 2.449
Adjusted primary model Chinese vs. U.S. strike among human-operated systems 9.039 1.253 0.000 6.582 11.496
Adjusted primary model Autonomous effect in U.S.-strike condition -9.030 1.316 0.000 -11.609 -6.450
Adjusted primary model Country scenario x autonomous AI interaction -1.231 1.806 0.496 -4.773 2.311
Weighted adjusted primary model Chinese vs. U.S. strike among human-operated systems 8.639 1.527 0.000 5.645 11.633
Weighted adjusted primary model Autonomous effect in U.S.-strike condition -9.394 1.638 0.000 -12.606 -6.182
Weighted adjusted primary model Country scenario x autonomous AI interaction 0.460 2.238 0.837 -3.928 4.848
Adjusted model with categorical demographics Chinese vs. U.S. strike among human-operated systems 9.007 1.254 0.000 6.548 11.466
Adjusted model with categorical demographics Autonomous effect in U.S.-strike condition -9.066 1.314 0.000 -11.644 -6.489
Adjusted model with categorical demographics Country scenario x autonomous AI interaction -1.221 1.806 0.499 -4.762 2.319

Equivalence-Style Tests for Negligible Effects

For the preregistered equivalence-style tests, a meaningful effect is defined as Cohen’s D = .20. The table converts that threshold into raw intentionality points using the standard deviation of the primary model sample. These tests are useful when the autonomous-AI coefficients are small: instead of only asking whether an effect is statistically distinguishable from zero, they ask whether the estimate is statistically contained inside the preregistered negligible-effect range.

sesoi_intentionality <- 0.20 * sd(prereg_primary_data$perceived_intentionality, na.rm = TRUE)

prereg_equivalence_weights <- prereg_contrast_weights[
  c(
    "Autonomous AI effect in U.S.-strike / Chinese-victim condition",
    "Autonomous AI effect in Chinese-strike / U.S.-victim condition"
  ),
  ,
  drop = FALSE
]

prereg_equivalence_estimate <- as.numeric(prereg_equivalence_weights %*% coef(prereg_primary_model))
prereg_equivalence_se <- sqrt(diag(prereg_equivalence_weights %*% prereg_primary_vcov %*% t(prereg_equivalence_weights)))
prereg_equivalence_p_lower <- pt(
  (prereg_equivalence_estimate - (-sesoi_intentionality)) / prereg_equivalence_se,
  df = df.residual(prereg_primary_model),
  lower.tail = FALSE
)
prereg_equivalence_p_upper <- pt(
  (prereg_equivalence_estimate - sesoi_intentionality) / prereg_equivalence_se,
  df = df.residual(prereg_primary_model),
  lower.tail = TRUE
)

prereg_equivalence_tests <- data.frame(
  contrast = rownames(prereg_equivalence_weights),
  estimate = prereg_equivalence_estimate,
  lower_bound = -sesoi_intentionality,
  upper_bound = sesoi_intentionality,
  hc2_se = prereg_equivalence_se,
  p_lower = prereg_equivalence_p_lower,
  p_upper = prereg_equivalence_p_upper,
  p_tost = pmax(prereg_equivalence_p_lower, prereg_equivalence_p_upper),
  equivalent_at_alpha_05 = pmax(prereg_equivalence_p_lower, prereg_equivalence_p_upper) < 0.05,
  row.names = NULL
)

kable(
  prereg_equivalence_tests,
  digits = 3,
  caption = paste0("Equivalence-style tests using +/- ", round(sesoi_intentionality, 2), " intentionality points as the meaningful-effect threshold.")
)
Equivalence-style tests using +/- 4.97 intentionality points as the meaningful-effect threshold.
contrast estimate lower_bound upper_bound hc2_se p_lower p_upper p_tost equivalent_at_alpha_05
Autonomous AI effect in U.S.-strike / Chinese-victim condition -9.030 -4.967 4.967 1.316 0.999 0 0.999 FALSE
Autonomous AI effect in Chinese-strike / U.S.-victim condition -10.261 -4.967 4.967 1.237 1.000 0 1.000 FALSE
equivalence_critical <- qt(0.95, df = df.residual(prereg_primary_model))

prereg_equivalence_plot_data <- prereg_equivalence_tests %>%
  mutate(
    ci90_low = estimate - equivalence_critical * hc2_se,
    ci90_high = estimate + equivalence_critical * hc2_se,
    contrast_label = case_when(
      grepl("U.S.-strike", contrast) ~ "Autonomous effect\nin U.S.-strike condition",
      TRUE ~ "Autonomous effect\nin Chinese-strike condition"
    ),
    contrast_label = factor(
      contrast_label,
      levels = c(
        "Autonomous effect\nin U.S.-strike condition",
        "Autonomous effect\nin Chinese-strike condition"
      )
    )
  )

ggplot(prereg_equivalence_plot_data, aes(x = estimate, y = contrast_label)) +
  annotate(
    "rect",
    xmin = -sesoi_intentionality,
    xmax = sesoi_intentionality,
    ymin = -Inf,
    ymax = Inf,
    alpha = 0.12,
    fill = "gray60"
  ) +
  geom_vline(xintercept = 0, linewidth = 0.5, color = "gray50") +
  geom_vline(xintercept = c(-sesoi_intentionality, sesoi_intentionality),
             linewidth = 0.6,
             linetype = "dashed",
             color = "gray40") +
  geom_errorbarh(aes(xmin = ci90_low, xmax = ci90_high), height = 0.14, linewidth = 0.8) +
  geom_point(size = 3) +
  scale_y_discrete(limits = rev(levels(prereg_equivalence_plot_data$contrast_label))) +
  labs(
    title = "Equivalence Tests for Negligible Autonomous-AI Effects",
    x = "Estimated autonomous-AI effect on perceived intentionality",
    y = NULL,
    caption = paste0("Shaded area is the preregistered negligible-effect region (+/- ", round(sesoi_intentionality, 2), " points). Error bars are 90% CIs for TOST.")
  ) +
  theme_minimal(base_size = 13) +
  theme(
    plot.title = element_text(face = "bold", hjust = 0.5),
    panel.grid.major.y = element_blank()
  )
## `height` was translated to `width`.

Manipulation Check Descriptives

The current Qualtrics export contains manipulation checks for the country whose weapons system launched the missiles and whether the system was autonomous or human-operated. It does not contain a separate victim-country manipulation check.

manipulation_check_overall <- prereg_analysis_data %>%
  summarize(
    n = n(),
    country_check_response_n = sum(!is.na(comp_country_correct)),
    country_check_correct = mean(comp_country_correct, na.rm = TRUE),
    autonomy_check_response_n = sum(!is.na(comp_autonomy_correct)),
    autonomy_check_correct = mean(comp_autonomy_correct, na.rm = TRUE),
    both_checks_correct = mean(comp_both_correct, na.rm = TRUE)
  )

manipulation_check_by_condition <- prereg_analysis_data %>%
  group_by(country_condition, weapons_condition) %>%
  summarize(
    n = n(),
    country_check_correct = mean(comp_country_correct, na.rm = TRUE),
    autonomy_check_correct = mean(comp_autonomy_correct, na.rm = TRUE),
    both_checks_correct = mean(comp_both_correct, na.rm = TRUE),
    .groups = "drop"
  )

kable(manipulation_check_overall, digits = 3, caption = "Overall manipulation-check pass rates.")
Overall manipulation-check pass rates.
n country_check_response_n country_check_correct autonomy_check_response_n autonomy_check_correct both_checks_correct
2935 2905 0.738 2935 0.808 0.66
kable(manipulation_check_by_condition, digits = 3, caption = "Manipulation-check pass rates by condition.")
Manipulation-check pass rates by condition.
country_condition weapons_condition n country_check_correct autonomy_check_correct both_checks_correct
Chinese strike / U.S. victim Autonomous AI 737 0.800 0.821 0.722
Chinese strike / U.S. victim Human-operated 737 0.749 0.775 0.644
U.S. strike / Chinese victim Autonomous AI 728 0.693 0.834 0.638
U.S. strike / Chinese victim Human-operated 733 0.709 0.802 0.634

Exploratory Model Among Manipulation-Check Passers

prereg_pass_data <- prereg_primary_data %>%
  filter(comp_both_correct)

prereg_pass_model <- lm(
  perceived_intentionality ~ country_scenario * autonomous_ai +
    ai_comfort_c + relative_china_us_ai_c + pid_republican +
    age_ordinal + gender_female + education_ordinal + income_ordinal +
    race_white + hispanic + metro,
  data = prereg_pass_data
)

prereg_pass_vcov <- vcovHC(prereg_pass_model, type = "HC2")
prereg_pass_coeftest <- coeftest(prereg_pass_model, vcov. = prereg_pass_vcov)
prereg_pass_ci <- coefci(prereg_pass_model, vcov. = prereg_pass_vcov)

prereg_pass_results <- data.frame(
  term = rownames(prereg_pass_coeftest),
  estimate = prereg_pass_coeftest[, 1],
  hc2_se = prereg_pass_coeftest[, 2],
  statistic = prereg_pass_coeftest[, 3],
  p_value = prereg_pass_coeftest[, 4],
  ci_low = prereg_pass_ci[, 1],
  ci_high = prereg_pass_ci[, 2],
  row.names = NULL
)

kable(
  prereg_pass_results,
  digits = 3,
  caption = paste0("Exploratory primary model among manipulation-check passers only (N = ", nrow(prereg_pass_data), ").")
)
Exploratory primary model among manipulation-check passers only (N = 1880).
term estimate hc2_se statistic p_value ci_low ci_high
(Intercept) 70.515 3.189 22.113 0.000 64.261 76.769
country_scenario 11.415 1.555 7.339 0.000 8.365 14.466
autonomous_ai -12.483 1.725 -7.235 0.000 -15.867 -9.100
ai_comfort_c 1.322 0.572 2.311 0.021 0.200 2.444
relative_china_us_ai_c 0.043 0.029 1.483 0.138 -0.014 0.099
pid_republican 1.735 1.195 1.452 0.147 -0.609 4.080
age_ordinal -1.031 0.617 -1.671 0.095 -2.242 0.179
gender_female -0.357 1.134 -0.315 0.753 -2.581 1.868
education_ordinal -0.780 0.436 -1.788 0.074 -1.635 0.075
income_ordinal -0.237 0.394 -0.602 0.548 -1.009 0.535
race_whiteWhite -0.797 1.416 -0.563 0.573 -3.574 1.980
hispanicYes 3.558 1.800 1.977 0.048 0.028 7.089
metroMetropolitan 0.848 1.776 0.478 0.633 -2.635 4.331
country_scenario:autonomous_ai -0.860 2.263 -0.380 0.704 -5.299 3.579

Secondary Outcome Models

The preregistration lists escalation likelihood, escalation justifiability, and responsibility attributions as exploratory dependent variables. The following models use the same treatment specification and preregistered covariates as the primary model.

Likelihood of Escalation
prereg_escalation_likelihood_data <- prereg_primary_data %>%
  filter(!is.na(escalation_likelihood))

prereg_escalation_likelihood_model <- lm(
  escalation_likelihood ~ country_scenario * autonomous_ai +
    ai_comfort_c + relative_china_us_ai_c + pid_republican +
    age_ordinal + gender_female + education_ordinal + income_ordinal +
    race_white + hispanic + metro,
  data = prereg_escalation_likelihood_data
)

prereg_escalation_likelihood_vcov <- vcovHC(prereg_escalation_likelihood_model, type = "HC2")
prereg_escalation_likelihood_coeftest <- coeftest(prereg_escalation_likelihood_model, vcov. = prereg_escalation_likelihood_vcov)
prereg_escalation_likelihood_ci <- coefci(prereg_escalation_likelihood_model, vcov. = prereg_escalation_likelihood_vcov)

prereg_escalation_likelihood_results <- data.frame(
  term = rownames(prereg_escalation_likelihood_coeftest),
  estimate = prereg_escalation_likelihood_coeftest[, 1],
  hc2_se = prereg_escalation_likelihood_coeftest[, 2],
  statistic = prereg_escalation_likelihood_coeftest[, 3],
  p_value = prereg_escalation_likelihood_coeftest[, 4],
  ci_low = prereg_escalation_likelihood_ci[, 1],
  ci_high = prereg_escalation_likelihood_ci[, 2],
  row.names = NULL
)

kable(
  prereg_escalation_likelihood_results,
  digits = 3,
  caption = "Likelihood of escalation model with HC2 standard errors."
)
Likelihood of escalation model with HC2 standard errors.
term estimate hc2_se statistic p_value ci_low ci_high
(Intercept) 65.757 2.232 29.464 0.000 61.381 70.133
country_scenario 1.081 1.137 0.950 0.342 -1.149 3.310
autonomous_ai 0.741 1.099 0.674 0.500 -1.415 2.897
ai_comfort_c 0.593 0.412 1.442 0.149 -0.214 1.400
relative_china_us_ai_c 0.006 0.020 0.327 0.744 -0.032 0.045
pid_republican -0.556 0.878 -0.633 0.527 -2.278 1.167
age_ordinal -0.673 0.447 -1.507 0.132 -1.549 0.203
gender_female -0.909 0.847 -1.074 0.283 -2.570 0.751
education_ordinal 0.005 0.320 0.017 0.986 -0.622 0.633
income_ordinal 0.179 0.282 0.634 0.526 -0.374 0.731
race_whiteWhite -1.035 1.026 -1.008 0.313 -3.047 0.977
hispanicYes -2.217 1.385 -1.600 0.110 -4.933 0.500
metroMetropolitan 0.510 1.281 0.399 0.690 -2.001 3.022
country_scenario:autonomous_ai -1.644 1.636 -1.005 0.315 -4.852 1.563
Justifiability of Escalation
prereg_escalation_justifiability_data <- prereg_primary_data %>%
  filter(!is.na(escalation_justifiability))

prereg_escalation_justifiability_model <- lm(
  escalation_justifiability ~ country_scenario * autonomous_ai +
    ai_comfort_c + relative_china_us_ai_c + pid_republican +
    age_ordinal + gender_female + education_ordinal + income_ordinal +
    race_white + hispanic + metro,
  data = prereg_escalation_justifiability_data
)

prereg_escalation_justifiability_vcov <- vcovHC(prereg_escalation_justifiability_model, type = "HC2")
prereg_escalation_justifiability_coeftest <- coeftest(prereg_escalation_justifiability_model, vcov. = prereg_escalation_justifiability_vcov)
prereg_escalation_justifiability_ci <- coefci(prereg_escalation_justifiability_model, vcov. = prereg_escalation_justifiability_vcov)

prereg_escalation_justifiability_results <- data.frame(
  term = rownames(prereg_escalation_justifiability_coeftest),
  estimate = prereg_escalation_justifiability_coeftest[, 1],
  hc2_se = prereg_escalation_justifiability_coeftest[, 2],
  statistic = prereg_escalation_justifiability_coeftest[, 3],
  p_value = prereg_escalation_justifiability_coeftest[, 4],
  ci_low = prereg_escalation_justifiability_ci[, 1],
  ci_high = prereg_escalation_justifiability_ci[, 2],
  row.names = NULL
)

kable(
  prereg_escalation_justifiability_results,
  digits = 3,
  caption = "Justifiability of escalation model with HC2 standard errors."
)
Justifiability of escalation model with HC2 standard errors.
term estimate hc2_se statistic p_value ci_low ci_high
(Intercept) 60.008 2.545 23.581 0.000 55.018 64.998
country_scenario 2.338 1.324 1.765 0.078 -0.259 4.934
autonomous_ai -2.403 1.316 -1.827 0.068 -4.983 0.177
ai_comfort_c 1.684 0.492 3.425 0.001 0.720 2.649
relative_china_us_ai_c 0.012 0.023 0.520 0.603 -0.033 0.057
pid_republican 4.471 1.029 4.344 0.000 2.453 6.489
age_ordinal 0.234 0.529 0.443 0.658 -0.803 1.271
gender_female -2.942 0.981 -2.998 0.003 -4.866 -1.018
education_ordinal 0.172 0.369 0.466 0.641 -0.551 0.895
income_ordinal -0.045 0.329 -0.136 0.892 -0.689 0.600
race_whiteWhite -2.575 1.123 -2.293 0.022 -4.776 -0.373
hispanicYes 1.205 1.460 0.825 0.410 -1.659 4.068
metroMetropolitan 1.977 1.515 1.305 0.192 -0.993 4.947
country_scenario:autonomous_ai 1.375 1.900 0.724 0.469 -2.350 5.100
prereg_secondary_treatment_results <- bind_rows(
  prereg_escalation_likelihood_results %>%
    filter(term %in% c("country_scenario", "autonomous_ai", "country_scenario:autonomous_ai")) %>%
    mutate(outcome = "Likelihood of escalation", .before = term),
  prereg_escalation_justifiability_results %>%
    filter(term %in% c("country_scenario", "autonomous_ai", "country_scenario:autonomous_ai")) %>%
    mutate(outcome = "Justifiability of escalation", .before = term)
) %>%
  mutate(
    term_label = case_when(
      term == "country_scenario" ~ "Chinese vs. U.S. strike\namong human-operated systems",
      term == "autonomous_ai" ~ "Autonomous effect\nin U.S.-strike condition",
      TRUE ~ "Country scenario x\nautonomous AI interaction"
    )
  )

prereg_secondary_treatment_results$term_label <- factor(
  prereg_secondary_treatment_results$term_label,
  levels = c(
    "Chinese vs. U.S. strike\namong human-operated systems",
    "Autonomous effect\nin U.S.-strike condition",
    "Country scenario x\nautonomous AI interaction"
  )
)

ggplot(prereg_secondary_treatment_results, aes(x = estimate, y = term_label)) +
  geom_vline(xintercept = 0, linewidth = 0.5, color = "gray55") +
  geom_errorbarh(aes(xmin = ci_low, xmax = ci_high), height = 0.14, linewidth = 0.8) +
  geom_point(size = 2.8) +
  facet_wrap(~ outcome) +
  scale_y_discrete(limits = rev(levels(prereg_secondary_treatment_results$term_label))) +
  labs(
    title = "Treatment Coefficients for Secondary Outcomes",
    x = "Estimated effect",
    y = NULL,
    caption = "Points are OLS estimates; error bars are 95% CIs using HC2 standard errors."
  ) +
  theme_minimal(base_size = 13) +
  theme(
    plot.title = element_text(face = "bold", hjust = 0.5),
    panel.grid.major.y = element_blank(),
    strip.text = element_text(face = "bold")
  )
## `height` was translated to `width`.

prereg_responsibility_data <- bind_rows(
  prereg_primary_data %>%
    transmute(country_scenario, autonomous_ai, ai_comfort_c, relative_china_us_ai_c, pid_republican,
              age_ordinal, gender_female, education_ordinal, income_ordinal, race_white, hispanic, metro,
              actor = "Senior political leaders", responsibility = as.numeric(resp_leader)),
  prereg_primary_data %>%
    transmute(country_scenario, autonomous_ai, ai_comfort_c, relative_china_us_ai_c, pid_republican,
              age_ordinal, gender_female, education_ordinal, income_ordinal, race_white, hispanic, metro,
              actor = "Military officials", responsibility = as.numeric(resp_military)),
  prereg_primary_data %>%
    transmute(country_scenario, autonomous_ai, ai_comfort_c, relative_china_us_ai_c, pid_republican,
              age_ordinal, gender_female, education_ordinal, income_ordinal, race_white, hispanic, metro,
              actor = "Human operator", responsibility = as.numeric(resp_pilot)),
  prereg_primary_data %>%
    transmute(country_scenario, autonomous_ai, ai_comfort_c, relative_china_us_ai_c, pid_republican,
              age_ordinal, gender_female, education_ordinal, income_ordinal, race_white, hispanic, metro,
              actor = "AI weapons system", responsibility = as.numeric(resp_ai))
) %>%
  filter(!is.na(responsibility))

responsibility_treatment_terms <- c(
  "country_scenario",
  "autonomous_ai",
  "country_scenario:autonomous_ai"
)
Responsibility: Senior Political Leaders
prereg_senior_political_leaders_data <- prereg_responsibility_data %>%
  filter(actor == "Senior political leaders")

prereg_senior_political_leaders_model <- lm(
  responsibility ~ country_scenario * autonomous_ai +
    ai_comfort_c + relative_china_us_ai_c + pid_republican +
    age_ordinal + gender_female + education_ordinal + income_ordinal +
    race_white + hispanic + metro,
  data = prereg_senior_political_leaders_data
)

prereg_senior_political_leaders_vcov <- vcovHC(prereg_senior_political_leaders_model, type = "HC2")
prereg_senior_political_leaders_coeftest <- coeftest(prereg_senior_political_leaders_model, vcov. = prereg_senior_political_leaders_vcov)
prereg_senior_political_leaders_ci <- coefci(prereg_senior_political_leaders_model, vcov. = prereg_senior_political_leaders_vcov)

prereg_senior_political_leaders_results <- data.frame(
  term = rownames(prereg_senior_political_leaders_coeftest),
  estimate = prereg_senior_political_leaders_coeftest[, 1],
  hc2_se = prereg_senior_political_leaders_coeftest[, 2],
  statistic = prereg_senior_political_leaders_coeftest[, 3],
  p_value = prereg_senior_political_leaders_coeftest[, 4],
  ci_low = prereg_senior_political_leaders_ci[, 1],
  ci_high = prereg_senior_political_leaders_ci[, 2],
  row.names = NULL
)

prereg_senior_political_leaders_treatment_results <- prereg_senior_political_leaders_results %>%
  filter(term %in% responsibility_treatment_terms) %>%
  mutate(actor = "Senior political leaders", .before = term)

kable(
  prereg_senior_political_leaders_treatment_results,
  digits = 3,
  caption = "Treatment coefficients for responsibility attributed to senior political leaders."
)
Treatment coefficients for responsibility attributed to senior political leaders.
actor term estimate hc2_se statistic p_value ci_low ci_high
Senior political leaders country_scenario 4.968 1.306 3.804 0.000 2.407 7.529
Senior political leaders autonomous_ai 1.514 1.355 1.117 0.264 -1.144 4.171
Senior political leaders country_scenario:autonomous_ai -1.419 1.862 -0.762 0.446 -5.070 2.232
Responsibility: Military Officials
prereg_military_officials_data <- prereg_responsibility_data %>%
  filter(actor == "Military officials")

prereg_military_officials_model <- lm(
  responsibility ~ country_scenario * autonomous_ai +
    ai_comfort_c + relative_china_us_ai_c + pid_republican +
    age_ordinal + gender_female + education_ordinal + income_ordinal +
    race_white + hispanic + metro,
  data = prereg_military_officials_data
)

prereg_military_officials_vcov <- vcovHC(prereg_military_officials_model, type = "HC2")
prereg_military_officials_coeftest <- coeftest(prereg_military_officials_model, vcov. = prereg_military_officials_vcov)
prereg_military_officials_ci <- coefci(prereg_military_officials_model, vcov. = prereg_military_officials_vcov)

prereg_military_officials_results <- data.frame(
  term = rownames(prereg_military_officials_coeftest),
  estimate = prereg_military_officials_coeftest[, 1],
  hc2_se = prereg_military_officials_coeftest[, 2],
  statistic = prereg_military_officials_coeftest[, 3],
  p_value = prereg_military_officials_coeftest[, 4],
  ci_low = prereg_military_officials_ci[, 1],
  ci_high = prereg_military_officials_ci[, 2],
  row.names = NULL
)

prereg_military_officials_treatment_results <- prereg_military_officials_results %>%
  filter(term %in% responsibility_treatment_terms) %>%
  mutate(actor = "Military officials", .before = term)

kable(
  prereg_military_officials_treatment_results,
  digits = 3,
  caption = "Treatment coefficients for responsibility attributed to military officials."
)
Treatment coefficients for responsibility attributed to military officials.
actor term estimate hc2_se statistic p_value ci_low ci_high
Military officials country_scenario 0.801 1.209 0.663 0.507 -1.569 3.171
Military officials autonomous_ai 0.462 1.221 0.378 0.705 -1.933 2.856
Military officials country_scenario:autonomous_ai 0.550 1.725 0.319 0.750 -2.833 3.933
Responsibility: Human Operator
prereg_human_operator_data <- prereg_responsibility_data %>%
  filter(actor == "Human operator")

prereg_human_operator_model <- lm(
  responsibility ~ country_scenario * autonomous_ai +
    ai_comfort_c + relative_china_us_ai_c + pid_republican +
    age_ordinal + gender_female + education_ordinal + income_ordinal +
    race_white + hispanic + metro,
  data = prereg_human_operator_data
)

prereg_human_operator_vcov <- vcovHC(prereg_human_operator_model, type = "HC2")
prereg_human_operator_coeftest <- coeftest(prereg_human_operator_model, vcov. = prereg_human_operator_vcov)
prereg_human_operator_ci <- coefci(prereg_human_operator_model, vcov. = prereg_human_operator_vcov)

prereg_human_operator_results <- data.frame(
  term = rownames(prereg_human_operator_coeftest),
  estimate = prereg_human_operator_coeftest[, 1],
  hc2_se = prereg_human_operator_coeftest[, 2],
  statistic = prereg_human_operator_coeftest[, 3],
  p_value = prereg_human_operator_coeftest[, 4],
  ci_low = prereg_human_operator_ci[, 1],
  ci_high = prereg_human_operator_ci[, 2],
  row.names = NULL
)

prereg_human_operator_treatment_results <- prereg_human_operator_results %>%
  filter(term %in% responsibility_treatment_terms) %>%
  mutate(actor = "Human operator", .before = term)

kable(
  prereg_human_operator_treatment_results,
  digits = 3,
  caption = "Treatment coefficients for responsibility attributed to the human operator."
)
Treatment coefficients for responsibility attributed to the human operator.
actor term estimate hc2_se statistic p_value ci_low ci_high
Human operator country_scenario 1.68 1.416 1.186 0.236 -1.098 4.458
Responsibility: AI Weapons System
prereg_ai_weapons_system_data <- prereg_responsibility_data %>%
  filter(actor == "AI weapons system")

prereg_ai_weapons_system_model <- lm(
  responsibility ~ country_scenario * autonomous_ai +
    ai_comfort_c + relative_china_us_ai_c + pid_republican +
    age_ordinal + gender_female + education_ordinal + income_ordinal +
    race_white + hispanic + metro,
  data = prereg_ai_weapons_system_data
)

prereg_ai_weapons_system_vcov <- vcovHC(prereg_ai_weapons_system_model, type = "HC2")
prereg_ai_weapons_system_coeftest <- coeftest(prereg_ai_weapons_system_model, vcov. = prereg_ai_weapons_system_vcov)
prereg_ai_weapons_system_ci <- coefci(prereg_ai_weapons_system_model, vcov. = prereg_ai_weapons_system_vcov)

prereg_ai_weapons_system_results <- data.frame(
  term = rownames(prereg_ai_weapons_system_coeftest),
  estimate = prereg_ai_weapons_system_coeftest[, 1],
  hc2_se = prereg_ai_weapons_system_coeftest[, 2],
  statistic = prereg_ai_weapons_system_coeftest[, 3],
  p_value = prereg_ai_weapons_system_coeftest[, 4],
  ci_low = prereg_ai_weapons_system_ci[, 1],
  ci_high = prereg_ai_weapons_system_ci[, 2],
  row.names = NULL
)

prereg_ai_weapons_system_treatment_results <- prereg_ai_weapons_system_results %>%
  filter(term %in% responsibility_treatment_terms) %>%
  mutate(actor = "AI weapons system", .before = term)

kable(
  prereg_ai_weapons_system_treatment_results,
  digits = 3,
  caption = "Treatment coefficients for responsibility attributed to the AI weapons system."
)
Treatment coefficients for responsibility attributed to the AI weapons system.
actor term estimate hc2_se statistic p_value ci_low ci_high
AI weapons system country_scenario -1.339 1.66 -0.807 0.42 -4.595 1.917
prereg_responsibility_treatment_results <- bind_rows(
  prereg_senior_political_leaders_treatment_results,
  prereg_military_officials_treatment_results,
  prereg_human_operator_treatment_results,
  prereg_ai_weapons_system_treatment_results
) %>%
  mutate(
    term_label = case_when(
      term == "country_scenario" ~ "Chinese vs. U.S. strike",
      term == "autonomous_ai" ~ "Autonomous vs. human",
      TRUE ~ "Interaction"
    )
  )

prereg_responsibility_treatment_results$term_label <- factor(
  prereg_responsibility_treatment_results$term_label,
  levels = c("Chinese vs. U.S. strike", "Autonomous vs. human", "Interaction")
)

ggplot(prereg_responsibility_treatment_results, aes(x = estimate, y = term_label)) +
  geom_vline(xintercept = 0, linewidth = 0.5, color = "gray55") +
  geom_errorbarh(aes(xmin = ci_low, xmax = ci_high), height = 0.14, linewidth = 0.8) +
  geom_point(size = 2.8) +
  facet_wrap(~ actor, ncol = 2) +
  scale_y_discrete(limits = rev(levels(prereg_responsibility_treatment_results$term_label))) +
  labs(
    title = "Treatment Coefficients for Responsibility Attributions",
    x = "Estimated effect on perceived responsibility",
    y = NULL,
    caption = "Error bars are 95% CIs using HC2 standard errors. Human-operator and AI-system responsibility were only asked in their corresponding weapons-system conditions."
  ) +
  theme_minimal(base_size = 13) +
  theme(
    plot.title = element_text(face = "bold", hjust = 0.5),
    panel.grid.major.y = element_blank(),
    strip.text = element_text(face = "bold")
  )
## `height` was translated to `width`.

Exploratory Responsibility Profiles by Actor

Coding note: senior political leaders and military officials are observed in all four treatment cells. Human-operator responsibility is only observed in the human-operated condition, and AI-system responsibility is only observed in the autonomous-AI condition. For that reason, this plot compares responsibility levels descriptively within each actor panel rather than treating all actor comparisons as if they were asked of the same respondents in every condition.

responsibility_profile_data <- bind_rows(
  prereg_analysis_data %>%
    transmute(country_condition, weapons_condition, actor = "Senior political leaders", responsibility = as.numeric(resp_leader)),
  prereg_analysis_data %>%
    transmute(country_condition, weapons_condition, actor = "Military officials", responsibility = as.numeric(resp_military)),
  prereg_analysis_data %>%
    transmute(country_condition, weapons_condition, actor = "Human operator", responsibility = as.numeric(resp_pilot)),
  prereg_analysis_data %>%
    transmute(country_condition, weapons_condition, actor = "AI weapons system", responsibility = as.numeric(resp_ai))
) %>%
  filter(!is.na(responsibility)) %>%
  mutate(
    country_condition = factor(
      country_condition,
      levels = c("Chinese strike / U.S. victim", "U.S. strike / Chinese victim"),
      labels = c("Chinese strike", "U.S. strike")
    ),
    weapons_condition = factor(weapons_condition, levels = c("Human-operated", "Autonomous AI")),
    actor = factor(
      actor,
      levels = c("Senior political leaders", "Military officials", "Human operator", "AI weapons system")
    )
  )

responsibility_profile_summary <- responsibility_profile_data %>%
  group_by(actor, country_condition, weapons_condition) %>%
  summarize(
    n = n(),
    mean = mean(responsibility),
    se = sd(responsibility) / sqrt(n),
    ci_low = mean - 1.96 * se,
    ci_high = mean + 1.96 * se,
    .groups = "drop"
  )

kable(
  responsibility_profile_summary,
  digits = 2,
  caption = "Descriptive responsibility means and 95% confidence intervals by actor and experimental condition."
)
Descriptive responsibility means and 95% confidence intervals by actor and experimental condition.
actor country_condition weapons_condition n mean se ci_low ci_high
Senior political leaders Chinese strike Human-operated 728 74.64 0.87 72.94 76.34
Senior political leaders Chinese strike Autonomous AI 729 74.83 0.91 73.04 76.62
Senior political leaders U.S. strike Human-operated 720 69.63 0.98 67.72 71.55
Senior political leaders U.S. strike Autonomous AI 720 71.28 1.00 69.32 73.23
Military officials Chinese strike Human-operated 728 74.82 0.86 73.14 76.50
Military officials Chinese strike Autonomous AI 728 76.09 0.87 74.38 77.79
Military officials U.S. strike Human-operated 721 74.14 0.85 72.47 75.81
Military officials U.S. strike Autonomous AI 722 74.45 0.90 72.68 76.22
Human operator Chinese strike Human-operated 728 70.74 0.99 68.80 72.68
Human operator U.S. strike Human-operated 718 68.67 0.98 66.75 70.60
AI weapons system Chinese strike Autonomous AI 723 65.41 1.17 63.12 67.70
AI weapons system U.S. strike Autonomous AI 712 66.94 1.16 64.67 69.21
ggplot(
  responsibility_profile_summary,
  aes(x = weapons_condition, y = mean, color = country_condition, group = country_condition)
) +
  geom_line(linewidth = 0.9, alpha = 0.45, na.rm = TRUE) +
  geom_point(size = 2.8) +
  geom_errorbar(aes(ymin = ci_low, ymax = ci_high), width = 0.08, linewidth = 0.7) +
  facet_wrap(~ actor, ncol = 2) +
  scale_color_manual(
    values = c("Chinese strike" = "#A63A33", "U.S. strike" = "#235A8C"),
    name = "Country scenario"
  ) +
  coord_cartesian(ylim = c(50, 85)) +
  labs(
    title = "Responsibility Profiles by Actor and Condition",
    x = "Weapons-system condition",
    y = "Perceived responsibility",
    caption = "Error bars are 95% confidence intervals. Human-operator and AI-system items are condition-specific."
  ) +
  theme_minimal(base_size = 12) +
  theme(
    plot.title = element_text(face = "bold", hjust = 0.5),
    legend.position = "top",
    panel.grid.major.x = element_blank(),
    strip.text = element_text(face = "bold")
  )
## `geom_line()`: Each group consists of only one observation.
## ℹ Do you need to adjust the group aesthetic?
## `geom_line()`: Each group consists of only one observation.
## ℹ Do you need to adjust the group aesthetic?

Exploratory Moderation Models

These models extend the primary specification by interacting the country-scenario indicator, autonomous-AI indicator, and moderator. Continuous moderators are demeaned. Models include Verasight demographic controls; moderation models not focused on party identification also control for pid_republican.

moderation_ai_comfort_model <- lm(
  perceived_intentionality ~ country_scenario * autonomous_ai * ai_comfort_c +
    pid_republican + age_ordinal + gender_female + education_ordinal + income_ordinal +
    race_white + hispanic + metro,
  data = prereg_primary_data
)

moderation_ai_comfort_vcov <- vcovHC(moderation_ai_comfort_model, type = "HC2")
moderation_ai_comfort_coeftest <- coeftest(moderation_ai_comfort_model, vcov. = moderation_ai_comfort_vcov)
moderation_ai_comfort_ci <- coefci(moderation_ai_comfort_model, vcov. = moderation_ai_comfort_vcov)

moderation_ai_comfort_results <- data.frame(
  term = rownames(moderation_ai_comfort_coeftest),
  estimate = moderation_ai_comfort_coeftest[, 1],
  hc2_se = moderation_ai_comfort_coeftest[, 2],
  statistic = moderation_ai_comfort_coeftest[, 3],
  p_value = moderation_ai_comfort_coeftest[, 4],
  ci_low = moderation_ai_comfort_ci[, 1],
  ci_high = moderation_ai_comfort_ci[, 2],
  row.names = NULL
) %>%
  filter(term == "country_scenario:autonomous_ai:ai_comfort_c") %>%
  mutate(moderator = "AI comfort", .before = term)

moderation_us_ai_effectiveness_model <- lm(
  perceived_intentionality ~ country_scenario * autonomous_ai * us_ai_effectiveness_c +
    pid_republican + age_ordinal + gender_female + education_ordinal + income_ordinal +
    race_white + hispanic + metro,
  data = prereg_primary_data
)

moderation_us_ai_effectiveness_vcov <- vcovHC(moderation_us_ai_effectiveness_model, type = "HC2")
moderation_us_ai_effectiveness_coeftest <- coeftest(moderation_us_ai_effectiveness_model, vcov. = moderation_us_ai_effectiveness_vcov)
moderation_us_ai_effectiveness_ci <- coefci(moderation_us_ai_effectiveness_model, vcov. = moderation_us_ai_effectiveness_vcov)

moderation_us_ai_effectiveness_results <- data.frame(
  term = rownames(moderation_us_ai_effectiveness_coeftest),
  estimate = moderation_us_ai_effectiveness_coeftest[, 1],
  hc2_se = moderation_us_ai_effectiveness_coeftest[, 2],
  statistic = moderation_us_ai_effectiveness_coeftest[, 3],
  p_value = moderation_us_ai_effectiveness_coeftest[, 4],
  ci_low = moderation_us_ai_effectiveness_ci[, 1],
  ci_high = moderation_us_ai_effectiveness_ci[, 2],
  row.names = NULL
) %>%
  filter(term == "country_scenario:autonomous_ai:us_ai_effectiveness_c") %>%
  mutate(moderator = "U.S. AI effectiveness", .before = term)

moderation_china_ai_effectiveness_model <- lm(
  perceived_intentionality ~ country_scenario * autonomous_ai * china_ai_effectiveness_c +
    pid_republican + age_ordinal + gender_female + education_ordinal + income_ordinal +
    race_white + hispanic + metro,
  data = prereg_primary_data
)

moderation_china_ai_effectiveness_vcov <- vcovHC(moderation_china_ai_effectiveness_model, type = "HC2")
moderation_china_ai_effectiveness_coeftest <- coeftest(moderation_china_ai_effectiveness_model, vcov. = moderation_china_ai_effectiveness_vcov)
moderation_china_ai_effectiveness_ci <- coefci(moderation_china_ai_effectiveness_model, vcov. = moderation_china_ai_effectiveness_vcov)

moderation_china_ai_effectiveness_results <- data.frame(
  term = rownames(moderation_china_ai_effectiveness_coeftest),
  estimate = moderation_china_ai_effectiveness_coeftest[, 1],
  hc2_se = moderation_china_ai_effectiveness_coeftest[, 2],
  statistic = moderation_china_ai_effectiveness_coeftest[, 3],
  p_value = moderation_china_ai_effectiveness_coeftest[, 4],
  ci_low = moderation_china_ai_effectiveness_ci[, 1],
  ci_high = moderation_china_ai_effectiveness_ci[, 2],
  row.names = NULL
) %>%
  filter(term == "country_scenario:autonomous_ai:china_ai_effectiveness_c") %>%
  mutate(moderator = "Chinese AI effectiveness", .before = term)

moderation_southkorea_ai_effectiveness_model <- lm(
  perceived_intentionality ~ country_scenario * autonomous_ai * southkorea_ai_effectiveness_c +
    pid_republican + age_ordinal + gender_female + education_ordinal + income_ordinal +
    race_white + hispanic + metro,
  data = prereg_primary_data
)

moderation_southkorea_ai_effectiveness_vcov <- vcovHC(moderation_southkorea_ai_effectiveness_model, type = "HC2")
moderation_southkorea_ai_effectiveness_coeftest <- coeftest(moderation_southkorea_ai_effectiveness_model, vcov. = moderation_southkorea_ai_effectiveness_vcov)
moderation_southkorea_ai_effectiveness_ci <- coefci(moderation_southkorea_ai_effectiveness_model, vcov. = moderation_southkorea_ai_effectiveness_vcov)

moderation_southkorea_ai_effectiveness_results <- data.frame(
  term = rownames(moderation_southkorea_ai_effectiveness_coeftest),
  estimate = moderation_southkorea_ai_effectiveness_coeftest[, 1],
  hc2_se = moderation_southkorea_ai_effectiveness_coeftest[, 2],
  statistic = moderation_southkorea_ai_effectiveness_coeftest[, 3],
  p_value = moderation_southkorea_ai_effectiveness_coeftest[, 4],
  ci_low = moderation_southkorea_ai_effectiveness_ci[, 1],
  ci_high = moderation_southkorea_ai_effectiveness_ci[, 2],
  row.names = NULL
) %>%
  filter(term == "country_scenario:autonomous_ai:southkorea_ai_effectiveness_c") %>%
  mutate(moderator = "South Korean AI effectiveness", .before = term)

moderation_relative_china_us_ai_model <- lm(
  perceived_intentionality ~ country_scenario * autonomous_ai * relative_china_us_ai_c +
    pid_republican + age_ordinal + gender_female + education_ordinal + income_ordinal +
    race_white + hispanic + metro,
  data = prereg_primary_data
)

moderation_relative_china_us_ai_vcov <- vcovHC(moderation_relative_china_us_ai_model, type = "HC2")
moderation_relative_china_us_ai_coeftest <- coeftest(moderation_relative_china_us_ai_model, vcov. = moderation_relative_china_us_ai_vcov)
moderation_relative_china_us_ai_ci <- coefci(moderation_relative_china_us_ai_model, vcov. = moderation_relative_china_us_ai_vcov)

moderation_relative_china_us_ai_results <- data.frame(
  term = rownames(moderation_relative_china_us_ai_coeftest),
  estimate = moderation_relative_china_us_ai_coeftest[, 1],
  hc2_se = moderation_relative_china_us_ai_coeftest[, 2],
  statistic = moderation_relative_china_us_ai_coeftest[, 3],
  p_value = moderation_relative_china_us_ai_coeftest[, 4],
  ci_low = moderation_relative_china_us_ai_ci[, 1],
  ci_high = moderation_relative_china_us_ai_ci[, 2],
  row.names = NULL
) %>%
  filter(term == "country_scenario:autonomous_ai:relative_china_us_ai_c") %>%
  mutate(moderator = "Chinese minus U.S. AI effectiveness", .before = term)

moderation_pid_model <- lm(
  perceived_intentionality ~ country_scenario * autonomous_ai * pid_republican +
    ai_comfort_c + relative_china_us_ai_c +
    age_ordinal + gender_female + education_ordinal + income_ordinal +
    race_white + hispanic + metro,
  data = prereg_primary_data
)

moderation_pid_vcov <- vcovHC(moderation_pid_model, type = "HC2")
moderation_pid_coeftest <- coeftest(moderation_pid_model, vcov. = moderation_pid_vcov)
moderation_pid_ci <- coefci(moderation_pid_model, vcov. = moderation_pid_vcov)

moderation_pid_results <- data.frame(
  term = rownames(moderation_pid_coeftest),
  estimate = moderation_pid_coeftest[, 1],
  hc2_se = moderation_pid_coeftest[, 2],
  statistic = moderation_pid_coeftest[, 3],
  p_value = moderation_pid_coeftest[, 4],
  ci_low = moderation_pid_ci[, 1],
  ci_high = moderation_pid_ci[, 2],
  row.names = NULL
) %>%
  filter(term == "country_scenario:autonomous_ai:pid_republican") %>%
  mutate(moderator = "Party identification", .before = term)

moderation_results <- bind_rows(
  moderation_ai_comfort_results,
  moderation_us_ai_effectiveness_results,
  moderation_china_ai_effectiveness_results,
  moderation_southkorea_ai_effectiveness_results,
  moderation_relative_china_us_ai_results,
  moderation_pid_results
)

kable(
  moderation_results,
  digits = 3,
  caption = "Focal three-way interaction terms from exploratory moderation models."
)
Focal three-way interaction terms from exploratory moderation models.
moderator term estimate hc2_se statistic p_value ci_low ci_high
AI comfort country_scenario:autonomous_ai:ai_comfort_c 4.428 1.773 2.498 0.013 0.952 7.904
U.S. AI effectiveness country_scenario:autonomous_ai:us_ai_effectiveness_c 0.086 0.081 1.063 0.288 -0.073 0.245
Chinese AI effectiveness country_scenario:autonomous_ai:china_ai_effectiveness_c 0.231 0.077 3.000 0.003 0.080 0.382
South Korean AI effectiveness country_scenario:autonomous_ai:southkorea_ai_effectiveness_c 0.223 0.083 2.677 0.007 0.060 0.386
Chinese minus U.S. AI effectiveness country_scenario:autonomous_ai:relative_china_us_ai_c 0.166 0.080 2.084 0.037 0.010 0.322
Party identification country_scenario:autonomous_ai:pid_republican -1.276 3.728 -0.342 0.732 -8.585 6.033
if (nrow(moderation_results) > 0) {
  moderation_plot_data <- moderation_results %>%
    mutate(
      moderator_term = ifelse(
        term == "country_scenario:autonomous_ai:pid_republican",
        "Republican vs. Democrat/Independent",
        moderator
      ),
      moderator_term = factor(moderator_term, levels = rev(unique(moderator_term)))
    )

  ggplot(moderation_plot_data, aes(x = estimate, y = moderator_term)) +
    geom_vline(xintercept = 0, linewidth = 0.5, color = "gray55") +
    geom_errorbarh(aes(xmin = ci_low, xmax = ci_high), height = 0.14, linewidth = 0.8) +
    geom_point(size = 2.8) +
    labs(
      title = "Exploratory Moderation of the Treatment Interaction",
      x = "Estimated three-way interaction",
      y = NULL,
      caption = "Points are focal three-way interaction estimates; error bars are 95% CIs using HC2 standard errors."
    ) +
    theme_minimal(base_size = 13) +
    theme(
      plot.title = element_text(face = "bold", hjust = 0.5),
      panel.grid.major.y = element_blank()
    )
}
## `height` was translated to `width`.

Exploratory Partisan Polarization

This exploratory analysis uses the three-category party identification variable to show whether perceived intentionality differs across Democrats, Independents, and Republicans within each experimental cell. The model is exploratory because the primary preregistered specification uses the binary pid_republican control rather than estimating separate treatment patterns for each party group.

polarization_data <- prereg_primary_data %>%
  mutate(
    pid_three_category = factor(pid, levels = c("Democrat", "Independent", "Republican")),
    country_condition = factor(
      country_condition,
      levels = c("Chinese strike / U.S. victim", "U.S. strike / Chinese victim"),
      labels = c("Chinese strike", "U.S. strike")
    ),
    weapons_condition = factor(weapons_condition, levels = c("Human-operated", "Autonomous AI"))
  ) %>%
  filter(!is.na(pid_three_category))

polarization_model <- lm(
  perceived_intentionality ~ country_scenario * autonomous_ai * pid_three_category +
    ai_comfort_c + relative_china_us_ai_c +
    age_ordinal + gender_female + education_ordinal + income_ordinal +
    race_white + hispanic + metro,
  data = polarization_data
)

polarization_vcov <- vcovHC(polarization_model, type = "HC2")
polarization_coeftest <- coeftest(polarization_model, vcov. = polarization_vcov)
polarization_ci <- coefci(polarization_model, vcov. = polarization_vcov)

polarization_results <- data.frame(
  term = rownames(polarization_coeftest),
  estimate = polarization_coeftest[, 1],
  hc2_se = polarization_coeftest[, 2],
  statistic = polarization_coeftest[, 3],
  p_value = polarization_coeftest[, 4],
  ci_low = polarization_ci[, 1],
  ci_high = polarization_ci[, 2],
  row.names = NULL
) %>%
  filter(grepl("pid_three_category", term))

polarization_summary <- polarization_data %>%
  group_by(pid_three_category, country_condition, weapons_condition) %>%
  summarize(
    n = n(),
    mean = mean(perceived_intentionality),
    se = sd(perceived_intentionality) / sqrt(n),
    ci_low = mean - 1.96 * se,
    ci_high = mean + 1.96 * se,
    .groups = "drop"
  )

kable(
  polarization_results,
  digits = 3,
  caption = "Exploratory party-identification terms from a three-category partisan moderation model with HC2 standard errors."
)
Exploratory party-identification terms from a three-category partisan moderation model with HC2 standard errors.
term estimate hc2_se statistic p_value ci_low ci_high
pid_three_categoryIndependent -0.127 2.769 -0.046 0.963 -5.556 5.302
pid_three_categoryRepublican -4.051 2.003 -2.022 0.043 -7.978 -0.123
country_scenario:pid_three_categoryIndependent -5.558 3.791 -1.466 0.143 -12.992 1.876
country_scenario:pid_three_categoryRepublican 7.589 2.733 2.776 0.006 2.229 12.948
autonomous_ai:pid_three_categoryIndependent -2.382 4.023 -0.592 0.554 -10.271 5.506
autonomous_ai:pid_three_categoryRepublican 3.263 2.849 1.145 0.252 -2.324 8.850
country_scenario:autonomous_ai:pid_three_categoryIndependent 11.439 5.418 2.111 0.035 0.815 22.062
country_scenario:autonomous_ai:pid_three_categoryRepublican 1.304 3.915 0.333 0.739 -6.373 8.981
ggplot(
  polarization_summary,
  aes(x = weapons_condition, y = mean, color = country_condition, group = country_condition)
) +
  geom_line(linewidth = 0.9, alpha = 0.45) +
  geom_point(size = 2.8) +
  geom_errorbar(aes(ymin = ci_low, ymax = ci_high), width = 0.08, linewidth = 0.7) +
  facet_wrap(~ pid_three_category) +
  scale_color_manual(
    values = c("Chinese strike" = "#A63A33", "U.S. strike" = "#235A8C"),
    name = "Country scenario"
  ) +
  coord_cartesian(ylim = c(45, 85)) +
  labs(
    title = "Perceived Intentionality by Party Identification",
    x = "Weapons-system condition",
    y = "Perceived intentionality",
    caption = "Exploratory descriptive means by party group. Error bars are 95% confidence intervals."
  ) +
  theme_minimal(base_size = 12) +
  theme(
    plot.title = element_text(face = "bold", hjust = 0.5),
    legend.position = "top",
    panel.grid.major.x = element_blank(),
    strip.text = element_text(face = "bold")
  )

Exploratory Downstream Association Models

The preregistration describes mediation-style analyses as exploratory and associational. The following models test whether perceived intentionality is associated with downstream escalation and responsibility judgments, controlling for treatment assignment, AI comfort, relative China-U.S. AI effectiveness, Republican identification, and Verasight demographic controls.

prereg_association_data <- prereg_primary_data %>%
  mutate(perceived_intentionality_c = perceived_intentionality - mean(perceived_intentionality, na.rm = TRUE))

downstream_escalation_likelihood_data <- prereg_association_data %>%
  filter(!is.na(escalation_likelihood))

downstream_escalation_likelihood_model <- lm(
  escalation_likelihood ~ country_scenario * autonomous_ai +
    perceived_intentionality_c + ai_comfort_c + relative_china_us_ai_c + pid_republican +
    age_ordinal + gender_female + education_ordinal + income_ordinal +
    race_white + hispanic + metro,
  data = downstream_escalation_likelihood_data
)

downstream_escalation_likelihood_vcov <- vcovHC(downstream_escalation_likelihood_model, type = "HC2")
downstream_escalation_likelihood_coeftest <- coeftest(downstream_escalation_likelihood_model, vcov. = downstream_escalation_likelihood_vcov)
downstream_escalation_likelihood_ci <- coefci(downstream_escalation_likelihood_model, vcov. = downstream_escalation_likelihood_vcov)

downstream_escalation_likelihood_results <- data.frame(
  term = rownames(downstream_escalation_likelihood_coeftest),
  estimate = downstream_escalation_likelihood_coeftest[, 1],
  hc2_se = downstream_escalation_likelihood_coeftest[, 2],
  statistic = downstream_escalation_likelihood_coeftest[, 3],
  p_value = downstream_escalation_likelihood_coeftest[, 4],
  ci_low = downstream_escalation_likelihood_ci[, 1],
  ci_high = downstream_escalation_likelihood_ci[, 2],
  row.names = NULL
) %>%
  filter(term == "perceived_intentionality_c") %>%
  mutate(outcome = "Likelihood of escalation", .before = term)

downstream_escalation_justifiability_data <- prereg_association_data %>%
  filter(!is.na(escalation_justifiability))

downstream_escalation_justifiability_model <- lm(
  escalation_justifiability ~ country_scenario * autonomous_ai +
    perceived_intentionality_c + ai_comfort_c + relative_china_us_ai_c + pid_republican +
    age_ordinal + gender_female + education_ordinal + income_ordinal +
    race_white + hispanic + metro,
  data = downstream_escalation_justifiability_data
)

downstream_escalation_justifiability_vcov <- vcovHC(downstream_escalation_justifiability_model, type = "HC2")
downstream_escalation_justifiability_coeftest <- coeftest(downstream_escalation_justifiability_model, vcov. = downstream_escalation_justifiability_vcov)
downstream_escalation_justifiability_ci <- coefci(downstream_escalation_justifiability_model, vcov. = downstream_escalation_justifiability_vcov)

downstream_escalation_justifiability_results <- data.frame(
  term = rownames(downstream_escalation_justifiability_coeftest),
  estimate = downstream_escalation_justifiability_coeftest[, 1],
  hc2_se = downstream_escalation_justifiability_coeftest[, 2],
  statistic = downstream_escalation_justifiability_coeftest[, 3],
  p_value = downstream_escalation_justifiability_coeftest[, 4],
  ci_low = downstream_escalation_justifiability_ci[, 1],
  ci_high = downstream_escalation_justifiability_ci[, 2],
  row.names = NULL
) %>%
  filter(term == "perceived_intentionality_c") %>%
  mutate(outcome = "Justifiability of escalation", .before = term)

downstream_results <- bind_rows(
  downstream_escalation_likelihood_results,
  downstream_escalation_justifiability_results
)

prereg_responsibility_association_data <- bind_rows(
  prereg_association_data %>%
    mutate(actor = "Senior political leaders", responsibility = as.numeric(resp_leader)),
  prereg_association_data %>%
    mutate(actor = "Military officials", responsibility = as.numeric(resp_military)),
  prereg_association_data %>%
    mutate(actor = "Human operator", responsibility = as.numeric(resp_pilot)),
  prereg_association_data %>%
    mutate(actor = "AI weapons system", responsibility = as.numeric(resp_ai))
) %>%
  filter(!is.na(responsibility))

responsibility_association_senior_political_leaders_data <- prereg_responsibility_association_data %>%
  filter(actor == "Senior political leaders",
         !is.na(perceived_intentionality_c))

responsibility_association_senior_political_leaders_model <- lm(
  responsibility ~ country_scenario * autonomous_ai +
    perceived_intentionality_c + ai_comfort_c + relative_china_us_ai_c + pid_republican +
    age_ordinal + gender_female + education_ordinal + income_ordinal +
    race_white + hispanic + metro,
  data = responsibility_association_senior_political_leaders_data
)

responsibility_association_senior_political_leaders_vcov <- vcovHC(responsibility_association_senior_political_leaders_model, type = "HC2")
responsibility_association_senior_political_leaders_coeftest <- coeftest(responsibility_association_senior_political_leaders_model, vcov. = responsibility_association_senior_political_leaders_vcov)
responsibility_association_senior_political_leaders_ci <- coefci(responsibility_association_senior_political_leaders_model, vcov. = responsibility_association_senior_political_leaders_vcov)

responsibility_association_senior_political_leaders_results <- data.frame(
  term = rownames(responsibility_association_senior_political_leaders_coeftest),
  estimate = responsibility_association_senior_political_leaders_coeftest[, 1],
  hc2_se = responsibility_association_senior_political_leaders_coeftest[, 2],
  statistic = responsibility_association_senior_political_leaders_coeftest[, 3],
  p_value = responsibility_association_senior_political_leaders_coeftest[, 4],
  ci_low = responsibility_association_senior_political_leaders_ci[, 1],
  ci_high = responsibility_association_senior_political_leaders_ci[, 2],
  row.names = NULL
) %>%
  filter(term == "perceived_intentionality_c") %>%
  mutate(outcome = "Responsibility: Senior political leaders", .before = term)

responsibility_association_military_officials_data <- prereg_responsibility_association_data %>%
  filter(actor == "Military officials",
         !is.na(perceived_intentionality_c))

responsibility_association_military_officials_model <- lm(
  responsibility ~ country_scenario * autonomous_ai +
    perceived_intentionality_c + ai_comfort_c + relative_china_us_ai_c + pid_republican +
    age_ordinal + gender_female + education_ordinal + income_ordinal +
    race_white + hispanic + metro,
  data = responsibility_association_military_officials_data
)

responsibility_association_military_officials_vcov <- vcovHC(responsibility_association_military_officials_model, type = "HC2")
responsibility_association_military_officials_coeftest <- coeftest(responsibility_association_military_officials_model, vcov. = responsibility_association_military_officials_vcov)
responsibility_association_military_officials_ci <- coefci(responsibility_association_military_officials_model, vcov. = responsibility_association_military_officials_vcov)

responsibility_association_military_officials_results <- data.frame(
  term = rownames(responsibility_association_military_officials_coeftest),
  estimate = responsibility_association_military_officials_coeftest[, 1],
  hc2_se = responsibility_association_military_officials_coeftest[, 2],
  statistic = responsibility_association_military_officials_coeftest[, 3],
  p_value = responsibility_association_military_officials_coeftest[, 4],
  ci_low = responsibility_association_military_officials_ci[, 1],
  ci_high = responsibility_association_military_officials_ci[, 2],
  row.names = NULL
) %>%
  filter(term == "perceived_intentionality_c") %>%
  mutate(outcome = "Responsibility: Military officials", .before = term)

responsibility_association_human_operator_data <- prereg_responsibility_association_data %>%
  filter(actor == "Human operator",
         !is.na(perceived_intentionality_c))

responsibility_association_human_operator_model <- lm(
  responsibility ~ country_scenario * autonomous_ai +
    perceived_intentionality_c + ai_comfort_c + relative_china_us_ai_c + pid_republican +
    age_ordinal + gender_female + education_ordinal + income_ordinal +
    race_white + hispanic + metro,
  data = responsibility_association_human_operator_data
)

responsibility_association_human_operator_vcov <- vcovHC(responsibility_association_human_operator_model, type = "HC2")
responsibility_association_human_operator_coeftest <- coeftest(responsibility_association_human_operator_model, vcov. = responsibility_association_human_operator_vcov)
responsibility_association_human_operator_ci <- coefci(responsibility_association_human_operator_model, vcov. = responsibility_association_human_operator_vcov)

responsibility_association_human_operator_results <- data.frame(
  term = rownames(responsibility_association_human_operator_coeftest),
  estimate = responsibility_association_human_operator_coeftest[, 1],
  hc2_se = responsibility_association_human_operator_coeftest[, 2],
  statistic = responsibility_association_human_operator_coeftest[, 3],
  p_value = responsibility_association_human_operator_coeftest[, 4],
  ci_low = responsibility_association_human_operator_ci[, 1],
  ci_high = responsibility_association_human_operator_ci[, 2],
  row.names = NULL
) %>%
  filter(term == "perceived_intentionality_c") %>%
  mutate(outcome = "Responsibility: Human operator", .before = term)

responsibility_association_ai_weapons_system_data <- prereg_responsibility_association_data %>%
  filter(actor == "AI weapons system",
         !is.na(perceived_intentionality_c))

responsibility_association_ai_weapons_system_model <- lm(
  responsibility ~ country_scenario * autonomous_ai +
    perceived_intentionality_c + ai_comfort_c + relative_china_us_ai_c + pid_republican +
    age_ordinal + gender_female + education_ordinal + income_ordinal +
    race_white + hispanic + metro,
  data = responsibility_association_ai_weapons_system_data
)

responsibility_association_ai_weapons_system_vcov <- vcovHC(responsibility_association_ai_weapons_system_model, type = "HC2")
responsibility_association_ai_weapons_system_coeftest <- coeftest(responsibility_association_ai_weapons_system_model, vcov. = responsibility_association_ai_weapons_system_vcov)
responsibility_association_ai_weapons_system_ci <- coefci(responsibility_association_ai_weapons_system_model, vcov. = responsibility_association_ai_weapons_system_vcov)

responsibility_association_ai_weapons_system_results <- data.frame(
  term = rownames(responsibility_association_ai_weapons_system_coeftest),
  estimate = responsibility_association_ai_weapons_system_coeftest[, 1],
  hc2_se = responsibility_association_ai_weapons_system_coeftest[, 2],
  statistic = responsibility_association_ai_weapons_system_coeftest[, 3],
  p_value = responsibility_association_ai_weapons_system_coeftest[, 4],
  ci_low = responsibility_association_ai_weapons_system_ci[, 1],
  ci_high = responsibility_association_ai_weapons_system_ci[, 2],
  row.names = NULL
) %>%
  filter(term == "perceived_intentionality_c") %>%
  mutate(outcome = "Responsibility: AI weapons system", .before = term)

responsibility_association_results <- bind_rows(
  responsibility_association_senior_political_leaders_results,
  responsibility_association_military_officials_results,
  responsibility_association_human_operator_results,
  responsibility_association_ai_weapons_system_results
)

kable(
  bind_rows(downstream_results, responsibility_association_results),
  digits = 3,
  caption = "Association between perceived intentionality and downstream judgments using HC2 standard errors."
)
Association between perceived intentionality and downstream judgments using HC2 standard errors.
outcome term estimate hc2_se statistic p_value ci_low ci_high
Likelihood of escalation perceived_intentionality_c 0.323 0.019 17.370 0.000 0.286 0.359
Justifiability of escalation perceived_intentionality_c 0.405 0.021 18.872 0.000 0.363 0.448
Responsibility: Senior political leaders perceived_intentionality_c 0.384 0.021 18.338 0.000 0.343 0.425
Responsibility: Military officials perceived_intentionality_c 0.273 0.020 13.330 0.000 0.233 0.313
Responsibility: Human operator perceived_intentionality_c 0.143 0.033 4.351 0.000 0.079 0.208
Responsibility: AI weapons system perceived_intentionality_c -0.012 0.037 -0.313 0.754 -0.085 0.062
prereg_downstream_plot_data <- bind_rows(downstream_results, responsibility_association_results) %>%
  mutate(outcome = factor(outcome, levels = rev(outcome)))

ggplot(prereg_downstream_plot_data, aes(x = estimate, y = outcome)) +
  geom_vline(xintercept = 0, linewidth = 0.5, color = "gray55") +
  geom_errorbarh(aes(xmin = ci_low, xmax = ci_high), height = 0.14, linewidth = 0.8) +
  geom_point(size = 2.8) +
  labs(
    title = "Association Between Intentionality and Downstream Judgments",
    x = "Estimated association with perceived intentionality",
    y = NULL,
    caption = "Associational models control for treatment assignment, preregistered covariates, and demographic controls. Error bars are 95% CIs using HC2 standard errors."
  ) +
  theme_minimal(base_size = 13) +
  theme(
    plot.title = element_text(face = "bold", hjust = 0.5),
    panel.grid.major.y = element_blank()
  )
## `height` was translated to `width`.

Exploratory Distributional Checks for Intentionality

The primary analyses focus on average treatment effects. These plots look at the full distribution of perceived intentionality by experimental condition. This helps diagnose whether differences in means reflect broad shifts across respondents or changes concentrated at particular parts of the 0-to-100 scale.

intentionality_distribution_data <- prereg_analysis_data %>%
  mutate(
    country_condition = factor(
      country_condition,
      levels = c("Chinese strike / U.S. victim", "U.S. strike / Chinese victim"),
      labels = c("Chinese strike", "U.S. strike")
    ),
    weapons_condition = factor(weapons_condition, levels = c("Human-operated", "Autonomous AI"))
  )

intentionality_quantiles <- intentionality_distribution_data %>%
  group_by(country_condition, weapons_condition) %>%
  summarize(
    n = n(),
    mean = mean(perceived_intentionality, na.rm = TRUE),
    p10 = quantile(perceived_intentionality, 0.10, na.rm = TRUE),
    p25 = quantile(perceived_intentionality, 0.25, na.rm = TRUE),
    median = median(perceived_intentionality, na.rm = TRUE),
    p75 = quantile(perceived_intentionality, 0.75, na.rm = TRUE),
    p90 = quantile(perceived_intentionality, 0.90, na.rm = TRUE),
    .groups = "drop"
  )

kable(
  intentionality_quantiles,
  digits = 2,
  caption = "Distributional summaries for perceived intentionality by experimental cell."
)
Distributional summaries for perceived intentionality by experimental cell.
country_condition weapons_condition n mean p10 p25 median p75 p90
Chinese strike Human-operated 737 72.11 46.0 51 75 94 100
Chinese strike Autonomous AI 737 62.09 33.6 50 59 80 99
U.S. strike Human-operated 733 62.83 36.2 50 59 84 99
U.S. strike Autonomous AI 728 54.21 17.0 43 51 71 92
ggplot(
  intentionality_distribution_data,
  aes(x = perceived_intentionality, fill = country_condition, color = country_condition)
) +
  geom_density(alpha = 0.20, linewidth = 0.8, adjust = 1.1, na.rm = TRUE) +
  facet_wrap(~ weapons_condition) +
  scale_fill_manual(
    values = c("Chinese strike" = "#A63A33", "U.S. strike" = "#235A8C"),
    name = "Country scenario"
  ) +
  scale_color_manual(
    values = c("Chinese strike" = "#A63A33", "U.S. strike" = "#235A8C"),
    name = "Country scenario"
  ) +
  scale_x_continuous(limits = c(0, 100), breaks = seq(0, 100, 20)) +
  labs(
    title = "Distribution of Perceived Intentionality by Condition",
    x = "Perceived intentionality",
    y = "Density",
    caption = "Density plots show the full outcome distribution rather than only the mean."
  ) +
  theme_minimal(base_size = 12) +
  theme(
    plot.title = element_text(face = "bold", hjust = 0.5),
    legend.position = "top",
    panel.grid.minor = element_blank(),
    strip.text = element_text(face = "bold")
  )

Main Outcome Plots

Intentionality

intentionality_summary <- survey %>%
  filter(!is.na(condition_agressor),
         !is.na(condition_autonomy),
         !is.na(intentionality)) %>%
  mutate(
    country = ifelse(condition_agressor == "US", "U.S.", condition_agressor),
    autonomy = ifelse(condition_autonomy == "human", "Human-operated", "Autonomous AI"),
    intentionality = as.numeric(intentionality)
  ) %>%
  group_by(country, autonomy) %>%
  summarize(
    n = n(),
    mean = mean(intentionality),
    se = sd(intentionality) / sqrt(n),
    ci_low = mean - 1.96 * se,
    ci_high = mean + 1.96 * se,
    .groups = "drop"
  )

intentionality_summary$country <- factor(intentionality_summary$country, levels = c("China", "U.S."))
intentionality_summary$autonomy <- factor(intentionality_summary$autonomy, levels = c("Human-operated", "Autonomous AI"))

ggplot(
  intentionality_summary,
  aes(x = autonomy, y = mean, color = country, group = country)
) +
  geom_line(linewidth = 1, alpha = 0.45) +
  geom_point(size = 3.5) +
  geom_errorbar(aes(ymin = ci_low, ymax = ci_high), width = 0.08, linewidth = 0.8) +
  geom_text(aes(label = paste0(round(mean, 1), "%")),
            vjust = -1.2,
            show.legend = FALSE) +
  scale_color_manual(
    values = c("China" = "#A63A33", "U.S." = "#235A8C"),
    name = "Country condition"
  ) +
  scale_y_continuous(
    limits = c(50, 80),
    breaks = seq(50, 80, 5),
    labels = function(x) paste0(x, "%")
  ) +
  labs(
    title = "Perceived Intentionality by Country and Weapons System",
    x = "Weapons-system condition",
    y = "Probability of intentionality",
    caption = "Error bars are 95% confidence intervals."
  ) +
  theme_minimal(base_size = 14) +
  theme(
    plot.title = element_text(face = "bold", hjust = 0.5),
    legend.position = "top",
    panel.grid.major.x = element_blank()
  )

Escalation

escalation_summary <- survey %>%
  filter(!is.na(condition_agressor),
         !is.na(condition_autonomy),
         !is.na(escalation)) %>%
  mutate(
    country = ifelse(condition_agressor == "US", "U.S.", condition_agressor),
    autonomy = ifelse(condition_autonomy == "human", "Human-operated", "Autonomous AI"),
    escalation = as.numeric(escalation)
  ) %>%
  group_by(country, autonomy) %>%
  summarize(
    n = n(),
    mean = mean(escalation),
    se = sd(escalation) / sqrt(n),
    ci_low = mean - 1.96 * se,
    ci_high = mean + 1.96 * se,
    .groups = "drop"
  ) %>%
  mutate(
    label_y = ifelse(country == "U.S.", ci_low - 2, ci_high + 2)
  )

escalation_summary$country <- factor(escalation_summary$country, levels = c("China", "U.S."))
escalation_summary$autonomy <- factor(escalation_summary$autonomy, levels = c("Human-operated", "Autonomous AI"))

ggplot(
  escalation_summary,
  aes(x = autonomy, y = mean, color = country, group = country)
) +
  geom_line(linewidth = 1, alpha = 0.45) +
  geom_point(size = 3.5) +
  geom_errorbar(aes(ymin = ci_low, ymax = ci_high), width = 0.08, linewidth = 0.8) +
  geom_text(aes(y = label_y, label = paste0(round(mean, 1), "%")),
            show.legend = FALSE) +
  scale_color_manual(
    values = c("China" = "#A63A33", "U.S." = "#235A8C"),
    name = "Country condition"
  ) +
  scale_y_continuous(
    limits = c(50, 70),
    breaks = seq(50, 70, 5),
    labels = function(x) paste0(x, "%")
  ) +
  labs(
    title = "Likelihood of Escalation by Country and Weapons System",
    x = "Weapons-system condition",
    y = "Likelihood of escalation",
    caption = "Error bars are 95% confidence intervals."
  ) +
  theme_minimal(base_size = 14) +
  theme(
    plot.title = element_text(face = "bold", hjust = 0.5),
    legend.position = "top",
    panel.grid.major.x = element_blank()
  )

Justification

justified_summary <- survey %>%
  filter(!is.na(condition_agressor),
         !is.na(condition_autonomy),
         !is.na(justified)) %>%
  mutate(
    country = ifelse(condition_agressor == "US", "U.S.", condition_agressor),
    autonomy = ifelse(condition_autonomy == "human", "Human-operated", "Autonomous AI"),
    justified = as.numeric(justified)
  ) %>%
  group_by(country, autonomy) %>%
  summarize(
    n = n(),
    mean = mean(justified),
    se = sd(justified) / sqrt(n),
    ci_low = mean - 1.96 * se,
    ci_high = mean + 1.96 * se,
    .groups = "drop"
  ) %>%
  mutate(
    label_y = ifelse(country == "U.S.", ci_low - 2, ci_high + 2)
  )

justified_summary$country <- factor(justified_summary$country, levels = c("China", "U.S."))
justified_summary$autonomy <- factor(justified_summary$autonomy, levels = c("Human-operated", "Autonomous AI"))

ggplot(
  justified_summary,
  aes(x = autonomy, y = mean, color = country, group = country)
) +
  geom_line(linewidth = 1, alpha = 0.45) +
  geom_point(size = 3.5) +
  geom_errorbar(aes(ymin = ci_low, ymax = ci_high), width = 0.08, linewidth = 0.8) +
  geom_text(aes(y = label_y, label = paste0(round(mean, 1))),
            show.legend = FALSE) +
  scale_color_manual(
    values = c("China" = "#A63A33", "U.S." = "#235A8C"),
    name = "Country condition"
  ) +
  scale_y_continuous(
    limits = c(50, 70),
    breaks = seq(50, 70, 5),
    labels = function(x) paste0(x)
  ) +
  labs(
    title = "Justifiability of Escalation by Country and Weapons System",
    x = "Weapons-system condition",
    y = "Level of Justification",
    caption = "Error bars are 95% confidence intervals."
  ) +
  theme_minimal(base_size = 14) +
  theme(
    plot.title = element_text(face = "bold", hjust = 0.5),
    legend.position = "top",
    panel.grid.major.x = element_blank()
  )

Responsibility

responsibility_data <- bind_rows(
  survey %>%
    transmute(
      condition_agressor,
      condition_autonomy,
      pid,
      actor = "Senior political leaders",
      responsibility = as.numeric(resp_leader)
    ),
  survey %>%
    transmute(
      condition_agressor,
      condition_autonomy,
      pid,
      actor = "Military officials",
      responsibility = as.numeric(resp_military)
    ),
  survey %>%
    transmute(
      condition_agressor,
      condition_autonomy,
      pid,
      actor = "Human operator",
      responsibility = as.numeric(resp_pilot)
    ),
  survey %>%
    transmute(
      condition_agressor,
      condition_autonomy,
      pid,
      actor = "AI weapons system",
      responsibility = as.numeric(resp_ai)
    )
)

responsibility_summary <- responsibility_data %>%
  filter(!is.na(condition_agressor),
         !is.na(condition_autonomy),
         !is.na(pid),
         !is.na(responsibility)) %>%
  mutate(
    country = ifelse(condition_agressor == "US", "U.S.", condition_agressor),
    autonomy = ifelse(condition_autonomy == "human", "Human-operated", "Autonomous AI"),
    pid = factor(pid, levels = c("Democrat", "Independent", "Republican"))
  ) %>%
  group_by(pid, actor, country, autonomy) %>%
  summarize(
    n = n(),
    mean = mean(responsibility),
    se = sd(responsibility) / sqrt(n),
    ci_low = mean - 1.96 * se,
    ci_high = mean + 1.96 * se,
    .groups = "drop"
  )

responsibility_summary$country <- factor(responsibility_summary$country, levels = c("China", "U.S."))
responsibility_summary$autonomy <- factor(responsibility_summary$autonomy, levels = c("Human-operated", "Autonomous AI"))
responsibility_summary$pid <- factor(
  responsibility_summary$pid,
  levels = c("Democrat", "Independent", "Republican")
)
responsibility_summary$actor <- factor(
  responsibility_summary$actor,
  levels = c("Senior political leaders", "Military officials", "Human operator", "AI weapons system")
)

ggplot(
  responsibility_summary,
  aes(x = autonomy, y = mean, color = country, group = country)
) +
  geom_line(linewidth = 1, alpha = 0.45) +
  geom_point(size = 2.5) +
  geom_errorbar(aes(ymin = ci_low, ymax = ci_high), width = 0.08, linewidth = 0.8) +
  facet_grid(pid ~ actor) +
  scale_color_manual(
    values = c("China" = "#A63A33", "U.S." = "#235A8C"),
    name = "Country condition"
  ) +
  scale_y_continuous(
    breaks = seq(50, 90, 10),
    labels = function(x) paste0(x)
  ) +
  coord_cartesian(ylim = c(50, 90)) +
  labs(
    title = "Perceived Responsibility by Party Identification",
    x = "Weapons-system condition",
    y = "Perceived responsibility",
    caption = "Error bars are 95% confidence intervals."
  ) +
  theme_minimal(base_size = 8) +
  theme(
    plot.title = element_text(face = "bold", hjust = 0.5),
    legend.position = "top",
    axis.text.x = element_text(size = 6),
    panel.grid.major.x = element_blank(),
    strip.text = element_text(face = "bold", size = 7)
  )
## `geom_line()`: Each group consists of only one observation.
## ℹ Do you need to adjust the group aesthetic?
## `geom_line()`: Each group consists of only one observation.
## ℹ Do you need to adjust the group aesthetic?
## `geom_line()`: Each group consists of only one observation.
## ℹ Do you need to adjust the group aesthetic?
## `geom_line()`: Each group consists of only one observation.
## ℹ Do you need to adjust the group aesthetic?
## `geom_line()`: Each group consists of only one observation.
## ℹ Do you need to adjust the group aesthetic?
## `geom_line()`: Each group consists of only one observation.
## ℹ Do you need to adjust the group aesthetic?

Covariate and Moderator Plots

Covariate Distributions

distribution_data <- bind_rows(
  intentionality_regression_data %>%
    transmute(variable = "Relative AI effectiveness", value = relative_ai),
  intentionality_regression_data %>%
    transmute(variable = "AI comfort", value = ai_comfort)
)

distribution_data$variable <- factor(
  distribution_data$variable,
  levels = c("Relative AI effectiveness", "AI comfort")
)

ggplot(distribution_data, aes(x = value)) +
  geom_histogram(bins = 30, fill = "#235A8C", color = "white") +
  facet_wrap(~ variable, scales = "free_x", ncol = 2) +
  labs(
    title = "Distributions of Relative AI Effectiveness and AI Comfort",
    x = "Value",
    y = "Number of respondents"
  ) +
  theme_minimal(base_size = 12) +
  theme(
    plot.title = element_text(face = "bold", hjust = 0.5),
    panel.grid.minor = element_blank(),
    strip.text = element_text(face = "bold")
  )

AI Comfort and Intentionality

comfort_intentionality_data <- intentionality_regression_data %>%
  mutate(
    country = ifelse(condition_agressor == "US", "U.S.", condition_agressor),
    autonomous = ifelse(condition_autonomy == "human", "Human-operated", "Autonomous AI")
  )

comfort_intentionality_data$country <- factor(comfort_intentionality_data$country, levels = c("China", "U.S."))
comfort_intentionality_data$autonomous <- factor(comfort_intentionality_data$autonomous, levels = c("Human-operated", "Autonomous AI"))

ggplot(
  comfort_intentionality_data,
  aes(x = ai_comfort, y = perceived_intentionality, color = country, shape = autonomous)
) +
  geom_jitter(width = 0.06, height = 0.4, alpha = 0.15, size = 1.2) +
  geom_smooth(aes(linetype = autonomous), method = "lm", se = TRUE, linewidth = 0.9) +
  scale_color_manual(
    values = c("China" = "#A63A33", "U.S." = "#235A8C"),
    name = "Country condition"
  ) +
  scale_shape_manual(
    values = c("Human-operated" = 16, "Autonomous AI" = 17),
    name = "Weapons-system condition"
  ) +
  scale_linetype_manual(
    values = c("Human-operated" = "solid", "Autonomous AI" = "dashed"),
    name = "Weapons-system condition"
  ) +
  scale_x_continuous(
    limits = c(-2, 2),
    breaks = seq(-2, 2, 1)
  ) +
  scale_y_continuous(
    limits = c(0, 100),
    breaks = seq(0, 100, 20),
    labels = function(x) paste0(x, "%")
  ) +
  labs(
    title = "Perceived Intentionality by AI Comfort",
    x = "AI comfort (-2 to 2 average)",
    y = "Perceived intentionality",
    caption = "Points are individual respondents; lines are linear fits by country and weapons-system condition."
  ) +
  guides(linetype = "none") +
  theme_minimal(base_size = 8) +
  theme(
    plot.title = element_text(face = "bold", hjust = 0.5),
    legend.position = "top",
    panel.grid.minor = element_blank()
  )
## `geom_smooth()` using formula = 'y ~ x'
## Warning: Removed 355 rows containing missing values or values outside the scale range
## (`geom_point()`).

Relative AI Effectiveness and Intentionality

relative_ai_intentionality_data <- intentionality_regression_data %>%
  mutate(
    country = ifelse(condition_agressor == "US", "U.S.", condition_agressor),
    autonomous = ifelse(condition_autonomy == "human", "Human-operated", "Autonomous AI")
  )

relative_ai_intentionality_data$country <- factor(relative_ai_intentionality_data$country, levels = c("China", "U.S."))
relative_ai_intentionality_data$autonomous <- factor(relative_ai_intentionality_data$autonomous, levels = c("Human-operated", "Autonomous AI"))

ggplot(
  relative_ai_intentionality_data,
  aes(x = relative_ai, y = perceived_intentionality, color = country, shape = autonomous)
) +
  geom_jitter(width = 0.6, height = 0.4, alpha = 0.15, size = 1.2) +
  geom_smooth(aes(linetype = autonomous), method = "lm", se = TRUE, linewidth = 0.9) +
  scale_color_manual(
    values = c("China" = "#A63A33", "U.S." = "#235A8C"),
    name = "Country condition"
  ) +
  scale_shape_manual(
    values = c("Human-operated" = 16, "Autonomous AI" = 17),
    name = "Weapons-system condition"
  ) +
  scale_linetype_manual(
    values = c("Human-operated" = "solid", "Autonomous AI" = "dashed"),
    name = "Weapons-system condition"
  ) +
  scale_x_continuous(
    limits = c(-100, 100),
    breaks = seq(-100, 100, 50)
  ) +
  scale_y_continuous(
    limits = c(0, 100),
    breaks = seq(0, 100, 20),
    labels = function(x) paste0(x, "%")
  ) +
  labs(
    title = "Perceived Intentionality by Relative AI Effectiveness",
    x = "Relative U.S.-China AI effectiveness",
    y = "Perceived intentionality",
    caption = "Points are individual respondents; lines are linear fits by country and weapons-system condition."
  ) +
  guides(linetype = "none") +
  theme_minimal(base_size = 8) +
  theme(
    plot.title = element_text(face = "bold", hjust = 0.5),
    legend.position = "top",
    panel.grid.minor = element_blank()
  )
## `geom_smooth()` using formula = 'y ~ x'
## Warning: Removed 151 rows containing missing values or values outside the scale range
## (`geom_point()`).

Party Identification and Intentionality

pid_intentionality_data <- intentionality_regression_data %>%
  filter(!is.na(pid)) %>%
  mutate(
    country = factor(ifelse(condition_agressor == "US", "U.S.", condition_agressor), levels = c("China", "U.S.")),
    autonomous = factor(ifelse(condition_autonomy == "human", "Human-operated", "Autonomous AI"), levels = c("Human-operated", "Autonomous AI")),
    pid = factor(pid, levels = c("Democrat", "Independent", "Republican")),
    plot_group = interaction(country, autonomous, drop = TRUE)
  )

pid_intentionality_summary <- pid_intentionality_data %>%
  group_by(pid, country, autonomous, plot_group) %>%
  summarize(
    n = n(),
    mean = mean(perceived_intentionality),
    se = sd(perceived_intentionality) / sqrt(n),
    ci_low = mean - 1.96 * se,
    ci_high = mean + 1.96 * se,
    .groups = "drop"
  )

ggplot(
  pid_intentionality_data,
  aes(x = pid, y = perceived_intentionality, color = country, shape = autonomous, group = plot_group)
) +
  geom_point(
    position = position_jitterdodge(jitter.width = 0.16, jitter.height = 0.4, dodge.width = 0.75),
    alpha = 0.12,
    size = 1.1
  ) +
  geom_errorbar(
    data = pid_intentionality_summary,
    aes(y = mean, ymin = ci_low, ymax = ci_high),
    position = position_dodge(width = 0.75),
    width = 0.18,
    linewidth = 0.7
  ) +
  geom_point(
    data = pid_intentionality_summary,
    aes(y = mean),
    position = position_dodge(width = 0.75),
    size = 2.8
  ) +
  scale_color_manual(
    values = c("China" = "#A63A33", "U.S." = "#235A8C"),
    name = "Country condition"
  ) +
  scale_shape_manual(
    values = c("Human-operated" = 16, "Autonomous AI" = 17),
    name = "Weapons-system condition"
  ) +
  scale_x_discrete(
    labels = c("Democrat", "Independent", "Republican", "Other/none")
  ) +
  scale_y_continuous(
    limits = c(0, 100),
    breaks = seq(0, 100, 20),
    labels = function(x) paste0(x, "%")
  ) +
  labs(
    title = "Perceived Intentionality by Party Identification",
    x = "Party identification",
    y = "Perceived intentionality",
    caption = "Faded points are individual respondents; larger points are group means with 95% confidence intervals."
  ) +
  theme_minimal(base_size = 8) +
  theme(
    plot.title = element_text(face = "bold", hjust = 0.5),
    legend.position = "top",
    panel.grid.minor = element_blank(),
    axis.text.x = element_text(size = 7)
  )
## Warning: Removed 155 rows containing missing values or values outside the scale range
## (`geom_point()`).

U.S. AI Effectiveness and Intentionality

us_ai_effectiveness_data <- intentionality_regression_data %>%
  filter(!is.na(ai_us)) %>%
  mutate(
    country = ifelse(condition_agressor == "US", "U.S.", condition_agressor),
    autonomous = ifelse(condition_autonomy == "human", "Human-operated", "Autonomous AI"),
    us_ai_effectiveness = as.numeric(ai_us)
  ) %>%
  filter(!is.na(us_ai_effectiveness))

us_ai_effectiveness_data$country <- factor(us_ai_effectiveness_data$country, levels = c("China", "U.S."))
us_ai_effectiveness_data$autonomous <- factor(us_ai_effectiveness_data$autonomous, levels = c("Human-operated", "Autonomous AI"))

ggplot(
  us_ai_effectiveness_data,
  aes(x = us_ai_effectiveness, y = perceived_intentionality, color = country, shape = autonomous)
) +
  geom_jitter(width = 0.6, height = 0.4, alpha = 0.15, size = 1.2) +
  geom_smooth(aes(linetype = autonomous), method = "lm", se = TRUE, linewidth = 0.9) +
  scale_color_manual(
    values = c("China" = "#A63A33", "U.S." = "#235A8C"),
    name = "Country condition"
  ) +
  scale_shape_manual(
    values = c("Human-operated" = 16, "Autonomous AI" = 17),
    name = "Weapons-system condition"
  ) +
  scale_linetype_manual(
    values = c("Human-operated" = "solid", "Autonomous AI" = "dashed"),
    name = "Weapons-system condition"
  ) +
  scale_x_continuous(
    limits = c(0, 100),
    breaks = seq(0, 100, 20)
  ) +
  scale_y_continuous(
    limits = c(0, 100),
    breaks = seq(0, 100, 20),
    labels = function(x) paste0(x, "%")
  ) +
  labs(
    title = "Perceived Intentionality by U.S. AI Effectiveness",
    x = "U.S. AI effectiveness rating",
    y = "Perceived intentionality",
    caption = "Points are individual respondents; lines are linear fits by country and weapons-system condition."
  ) +
  guides(linetype = "none") +
  theme_minimal(base_size = 8) +
  theme(
    plot.title = element_text(face = "bold", hjust = 0.5),
    legend.position = "top",
    panel.grid.minor = element_blank()
  )
## `geom_smooth()` using formula = 'y ~ x'
## Warning: Removed 265 rows containing missing values or values outside the scale range
## (`geom_point()`).

Chinese AI Effectiveness and Intentionality

chinese_ai_effectiveness_data <- intentionality_regression_data %>%
  filter(!is.na(ai_china)) %>%
  mutate(
    country = ifelse(condition_agressor == "US", "U.S.", condition_agressor),
    autonomous = ifelse(condition_autonomy == "human", "Human-operated", "Autonomous AI"),
    chinese_ai_effectiveness = as.numeric(ai_china)
  ) %>%
  filter(!is.na(chinese_ai_effectiveness))

chinese_ai_effectiveness_data$country <- factor(chinese_ai_effectiveness_data$country, levels = c("China", "U.S."))
chinese_ai_effectiveness_data$autonomous <- factor(chinese_ai_effectiveness_data$autonomous, levels = c("Human-operated", "Autonomous AI"))

ggplot(
  chinese_ai_effectiveness_data,
  aes(x = chinese_ai_effectiveness, y = perceived_intentionality, color = country, shape = autonomous)
) +
  geom_jitter(width = 0.6, height = 0.4, alpha = 0.15, size = 1.2) +
  geom_smooth(aes(linetype = autonomous), method = "lm", se = TRUE, linewidth = 0.9) +
  scale_color_manual(
    values = c("China" = "#A63A33", "U.S." = "#235A8C"),
    name = "Country condition"
  ) +
  scale_shape_manual(
    values = c("Human-operated" = 16, "Autonomous AI" = 17),
    name = "Weapons-system condition"
  ) +
  scale_linetype_manual(
    values = c("Human-operated" = "solid", "Autonomous AI" = "dashed"),
    name = "Weapons-system condition"
  ) +
  scale_x_continuous(
    limits = c(0, 100),
    breaks = seq(0, 100, 20)
  ) +
  scale_y_continuous(
    limits = c(0, 100),
    breaks = seq(0, 100, 20),
    labels = function(x) paste0(x, "%")
  ) +
  labs(
    title = "Perceived Intentionality by Chinese AI Effectiveness",
    x = "Chinese AI effectiveness rating",
    y = "Perceived intentionality",
    caption = "Points are individual respondents; lines are linear fits by country and weapons-system condition."
  ) +
  guides(linetype = "none") +
  theme_minimal(base_size = 8) +
  theme(
    plot.title = element_text(face = "bold", hjust = 0.5),
    legend.position = "top",
    panel.grid.minor = element_blank()
  )
## `geom_smooth()` using formula = 'y ~ x'
## Warning: Removed 234 rows containing missing values or values outside the scale range
## (`geom_point()`).

South Korean AI Effectiveness and Intentionality

southkorea_ai_effectiveness_data <- intentionality_regression_data %>%
  filter(!is.na(ai_sk)) %>%
  mutate(
    country = ifelse(condition_agressor == "US", "U.S.", condition_agressor),
    autonomous = ifelse(condition_autonomy == "human", "Human-operated", "Autonomous AI"),
    southkorea_ai_effectiveness = as.numeric(ai_sk)
  ) %>%
  filter(!is.na(southkorea_ai_effectiveness))

southkorea_ai_effectiveness_data$country <- factor(southkorea_ai_effectiveness_data$country, levels = c("China", "U.S."))
southkorea_ai_effectiveness_data$autonomous <- factor(southkorea_ai_effectiveness_data$autonomous, levels = c("Human-operated", "Autonomous AI"))

ggplot(
  southkorea_ai_effectiveness_data,
  aes(x = southkorea_ai_effectiveness, y = perceived_intentionality, color = country, shape = autonomous)
) +
  geom_jitter(width = 0.6, height = 0.4, alpha = 0.15, size = 1.2) +
  geom_smooth(aes(linetype = autonomous), method = "lm", se = TRUE, linewidth = 0.9) +
  scale_color_manual(
    values = c("China" = "#A63A33", "U.S." = "#235A8C"),
    name = "Country condition"
  ) +
  scale_shape_manual(
    values = c("Human-operated" = 16, "Autonomous AI" = 17),
    name = "Weapons-system condition"
  ) +
  scale_linetype_manual(
    values = c("Human-operated" = "solid", "Autonomous AI" = "dashed"),
    name = "Weapons-system condition"
  ) +
  scale_x_continuous(
    limits = c(0, 100),
    breaks = seq(0, 100, 20)
  ) +
  scale_y_continuous(
    limits = c(0, 100),
    breaks = seq(0, 100, 20),
    labels = function(x) paste0(x, "%")
  ) +
  labs(
    title = "Perceived Intentionality by South Korean AI Effectiveness",
    x = "South Korea AI effectiveness rating",
    y = "Perceived intentionality",
    caption = "Points are individual respondents; lines are linear fits by country and weapons-system condition."
  ) +
  guides(linetype = "none") +
  theme_minimal(base_size = 8) +
  theme(
    plot.title = element_text(face = "bold", hjust = 0.5),
    legend.position = "top",
    panel.grid.minor = element_blank()
  )
## `geom_smooth()` using formula = 'y ~ x'
## Warning: Removed 225 rows containing missing values or values outside the scale range
## (`geom_point()`).