Green · worked conjoint study

Design an auditable laptop choice experiment.

This tutorial turns a product question into a conjoint design, an inspectable EDSL Jobs package, normalized choice data, and preference estimates.

The example asks how laptop buyers trade off brand, price, memory, and screen size. Green owns the study and its provenance. EDSL executes only the generated package that the researcher has inspected and approved.

Install Green and EDSL

The managed tool installation supplies both the green and ep executables from their current main branches.

uv tool install --upgrade --force \
  --with-executables-from "edsl @ git+https://github.com/expectedparrot/edsl.git@main" \
  "green[fielding] @ git+https://github.com/expectedparrot/green.git@main"

green version
ep --help

Use green guide for the bundled workflow contract and green next for the current project action.

1. Start the project

green init --type generic-conjoint \
  --description "Laptop configurations for professional buyers"
green guide
green next
Show abbreviated next action
{
  "schema_version": "1.0",
  "status": "ok",
  "data": {
    "phase": "definition-empty",
    "action": {"command": "green dimension add brand --type categorical"}
  }
}

green next is state-aware. Run it after each completed stage and execute the returned action rather than reconstructing the workflow from memory.

2. Define attributes and levels

Categorical attributes use named levels. Price is continuous so later analysis can express other utility changes in dollars.

green dimension add brand --type categorical
green level add brand Dell HP Lenovo

green dimension add ram --type categorical
green level add ram 8GB 16GB 32GB

green dimension add screen --type categorical
green level add screen 13-inch 15-inch 17-inch

green dimension add price --type continuous --unit USD \
  --role price --range 799:1399 --grid-count 4
green validate
Show validation outcome
{
  "status": "ok",
  "data": {"valid": true, "dimensions": 4, "blocking_issues": []}
}

Stable dimension and level IDs become the columns and values in the saved design. Display metadata does not replace those analysis keys.

3. Define who answers

An AgentList records the intended respondent panel before fielding. Each agent can have a persona and structured traits used by EDSL.

green agent-list create panel
green agent add panel --id buyer_01 \
  --persona "IT manager replacing laptops for a 100-person company" \
  --trait segment=enterprise
green agent add panel --id buyer_02 \
  --persona "Independent developer buying a primary work laptop" \
  --trait segment=independent
Show panel summary
{"status":"ok","data":{"agent_list":"panel","agents":2}}

A real study would define the full sample and recruitment logic here. The example keeps two agents only to make the mechanics easy to inspect.

4. Generate the choice sets

green design generate --name main --method bayesian-d-optimal \
  --tasks-per-respondent 12 --alternatives 3 \
  --versions 1 --seed 42
green design inspect main
Show abbreviated design diagnostics
{
  "status": "ok",
  "data": {
    "design": {"name":"main","schema":"cbc-design-v1","rows":36,"method":"bayesian-d-optimal"},
    "identifiability": {"identifiable":true,"blocking_issues":[]}
  }
}

Twelve tasks with three alternatives produce 36 profile rows. Green greedily maximizes the mean log determinant of expected Fisher information, then checks within-task variation and design-matrix rank before recommending fielding. With no --priors file, coefficient means are zero.

5. Build the EDSL handoff

green field build --design main --job-name laptop \
  --agent-list panel
Show generated artifacts
{
  "status": "ok",
  "artifacts": {
    "survey": "survey.ep",
    "agent_list": "agent_list.ep",
    "jobs": "jobs.ep",
    "mapping": "question-map.json"
  }
}

survey.ep, agent_list.ep, and jobs.ep are native, git-backed EDSL objects. There is no generated Python script and no model embedded in the package.

6. Inspect and price before running

ep inspect jobs.ep
ep inspect agent_list.ep
ep surveys questions survey.ep
ep jobs cost jobs.ep --model gpt-5.4-mini
green plan field --design main --job-name laptop
Show what the field plan records
{
  "kind": "field",
  "requires_user_approval": true,
  "estimates": {
    "respondents": 2,
    "respondent_tasks": 24,
    "questions_per_task": 1,
    "response_rows": 72,
    "external_model_calls": 24
  }
}
Review boundary. Inspect the question wording, agents, model-call count, cost estimate, runtime range, and output path. Do not run the paid execution step until the user approves it.

ep inspect agent_list.ep exposes the exact personas and traits created in step 3. ep surveys questions survey.ep exposes the rendered laptop alternatives and question wording. Nothing about the sample or product profiles is added at model execution time.

7. Execute the reviewed package

# Run only after explicit approval
ep run jobs.ep --model gpt-5.4-mini \
  --output results.ep
Show the expected execution shape
{
  "completed": 2,
  "failed": 0,
  "output": "results.ep"
}

The Results package contains answers together with agent and model provenance. Preserve it alongside the Jobs package: one records what was asked; the other records what answered.

8. Normalize and validate choices

green data import-edsl results.ep \
  --design main --job-name laptop --name field_run_1
green data quality field_run_1
green next
Show a successful quality contract
{
  "valid": true,
  "counts": {
    "respondents": 2,
    "tasks_expected": 24,
    "tasks_complete": 24,
    "tasks_invalid": 0,
    "rows": 72
  }
}

Import maps selected option labels back to design alternative IDs. Quality requires exactly one choice per respondent-task and rejects malformed, duplicate, unknown, or incomplete task rows.

9. Fit the baseline model

green plan estimate --data field_run_1 --method mnl \
  --name mnl_main
green estimate mnl --data field_run_1 --name mnl_main --dry-run
green estimate mnl --data field_run_1 --name mnl_main
Show model diagnostics shape
{
  "method": "mnl-mle",
  "converged": true,
  "coverage": {"tasks_excluded":0,"quality_valid_before_fit":true},
  "identifiability": {"identifiable":true}
}

The pooled multinomial logit estimates part-worths from within-task choices. The dry run resolves data, coverage, and identifiability without registering a model artifact.

10. Interpret and preserve the outputs

green report partworths mnl_main
green report importance mnl_main
green report wtp mnl_main --price-dimension price --attribute ram
green next
Show report interpretation
partworths  Relative utility for each attribute level
importance  Utility range by attribute, normalized across attributes
wtp         Utility difference divided by the negative price coefficient

Part-worths are relative to the model’s coding and reference levels. Importance summarizes ranges within this design; it is not a universal property of an attribute. Willingness-to-pay is meaningful only when the price coefficient has the expected sign and adequate precision.

The full workflow

Green: define → design → validate → jobs.ep + manifest
EDSL: inspect → price → approved run → results.ep
Green: import → quality → estimate → report → verify

At each arrow, green next reports the current phase, blockers, and one executable next action. Durable provenance lives under .green/; public fielding artifacts are written to the current directory or the explicit --output-dir.