Auditable qualitative coding
Twenty letters between John and Abigail Adams go from raw text to a traceable codebook, exact excerpts, analytic memos, and an interactive evidence explorer — with the path from every claim back to its source preserved.
See the finished product first. The interactive HTML explorer on this site was generated by the exact run this tutorial walks through; the corpus ships inside the package (bewley example fetch adams-letters writes a local copy), so the whole page is reproducible.
01 The corpus and the question
The worked example is twenty letters exchanged by John and Abigail Adams between April 1775 and July 1776, selected from the public-domain Familiar Letters edition (Project Gutenberg ebook 34123) and bundled with the package. The research question the codebook must serve — stated here, and recorded in the project itself in chapter 4:
How did John and Abigail Adams negotiate public duty, household responsibility, political voice, danger, and emotional intimacy during the American Revolution?
This is the kind of material the codebook has to do justice to — Abigail Adams, writing from Braintree on 31 March 1776:
“…in the new code of laws which I suppose it will be necessary for you to make, I desire you would remember the ladies and be more generous and favorable to them than your ancestors. Do not put such unlimited power into the hands of the husbands. Remember, all men would be tyrants if they could. If particular care and attention is not paid to the ladies, we are determined to foment a rebellion, and will not hold ourselves bound by any laws in which we have no voice or representation.”
What qualitative coding is
If you have never done this before: qualitative coding is reading a body of text carefully and tagging passages with short labels that name what is going on in them. The labels are called codes — an unfortunate collision with both computer code and Abigail's "code of laws"; here a code is just a named idea, like political_voice, with a one-sentence definition of when it applies. The set of codes plus their definitions is the codebook. Each individual tag — this code, attached to this exact passage — is an annotation. The passage above will carry a political_voice annotation, because it is a claim to political standing by someone excluded from it.
Why bother? Because once passages are tagged, questions about twenty letters become answerable and checkable: which letters talk about both duty and affection? does the danger theme appear more in her letters or his? what exactly is the evidence for that claim? The discipline is that every code has a written definition and every claim traces back to exact quoted text — not to an impression left after reading.
The workflow this tutorial walks through, end to end:
Two terms worth knowing because the tradition uses them and this page inherits them: open coding is the first pass, where labels are invented from the material itself rather than chosen from a preset list — here a language model drafts that pass and you judge it. A memo is a dated analytic note recording why you interpreted something the way you did. Everything else is introduced where it appears.
Bewley's role is to make all of this accountable: every code has a written definition, every annotation is anchored to exact bytes of a specific document revision, and every change to either is an append-only event you can audit later.
Evidence stays exact
Annotations point to a whole document, an inclusive line range, or an exact byte span.
History is append-only
Edits and undo operations become events. Earlier analytic decisions remain inspectable.
Codes can evolve
Rename, merge, split, nest, and link codes as distinctions sharpen — merges absorb their sources without losing provenance.
Model work stays external
Bewley packages open-coding jobs as EDSL objects; the paid ep run step is yours to authorize.
If you follow along, expect small differences. Every output shown on this page was captured from one real working session, so you can see exactly what each command produces. If you run the same commands yourself, two things will look different: the machine-generated identifiers and timestamps (each project generates its own), and the labels the model proposes in chapter 5 — for that one step this page substitutes a saved copy of a model's answers in place of a live, paid model call, so the page can be rebuilt at no cost; the substitution is labeled where it happens. Everything else — the commands, what each one does, and the shape of every output — is what you will see.
02 The local data model
| Object | Role | Why it matters |
|---|---|---|
| Document | A versioned UTF-8 source | Preserves the material being interpreted. |
| Code | A named analytic category | Makes the codebook explicit and revisable. |
| Annotation | A code attached to evidence | Connects an interpretation to an exact passage. |
| Memo | An analytic note | Records comparisons, hypotheses, and exceptions. |
| Event | An append-only state change | Provides history, audit, and non-destructive undo. |
.bewley/. The SQLite index is derived state and can be rebuilt with bewley rebuild-index.03 Installation
Install Bewley and EDSL's ep executable into one isolated tool environment:
python -m pip install --user --upgrade uv
uv tool install --python 3.11 --upgrade --force \
--with-executables-from "edsl @ git+https://github.com/expectedparrot/edsl.git@02c9d1c8e273d9257f3f9d5f380b91a18747b8cb" \
"bewley @ git+https://github.com/expectedparrot/bewley.git@main"
Verify the build and its contract versions:
bewley version
Show command output
{
"schema_version": "2.0",
"status": "ok",
"command": "bewley version",
"argv": [
"bewley",
"version"
],
"data": {
"version": "0.5.0",
"package_path": "~/bewley/src/bewley",
"envelope_schema_version": "2.0",
"agent_schema_version": "1.0"
},
"warnings": [],
"errors": [],
"next_steps": []
}For local development, clone the repository, pip install -e ., and run PYTHONPATH=src python -m pytest tests/.
--cwd option. Run every project command from the directory containing .bewley/.04 Start the project
The twenty letters ship inside the package as a bundled example, so following along needs nothing beyond the install — no source checkout, no paths to guess. One command writes a local copy of the corpus (plus its README and Project Gutenberg license) into a fresh directory:
bewley example fetch adams-letters
Show command output
{
"schema_version": "2.0",
"status": "ok",
"command": "bewley example fetch",
"argv": [
"bewley",
"example",
"fetch",
"adams-letters"
],
"data": {
"example": "adams-letters",
"dest": "adams-letters",
"documents": 20,
"files_written": [
"PROJECT_GUTENBERG_LICENSE.txt",
"README.md",
"corpus/1775-april-30-john-adams.txt",
"corpus/1775-august-10-abigail-adams.txt",
"corpus/1775-december-03-john-adams.txt",
"corpus/1775-december-10-abigail-adams.txt",
"corpus/1775-june-11-john-adams.txt",
"corpus/1775-june-15-abigail-adams.txt",
"corpus/1775-june-18-john-adams.txt",
"corpus/1775-june-22-abigail-adams.txt",
"corpus/1775-may-04-abigail-adams.txt",
"corpus/1775-october-21-abigail-adams.txt",
"corpus/1775-october-23-john-adams.txt",
"corpus/1775-september-17-john-adams.txt",
"corpus/1776-april-14-john-adams.txt",
"corpus/1776-july-03-john-adams.txt",
"corpus/1776-july-11-john-adams.txt",
"corpus/1776-july-13-abigail-adams.txt",
"corpus/1776-june-03-abigail-adams.txt",
"corpus/1776-march-02-abigail-adams.txt",
"corpus/1776-march-17-john-adams.txt",
"corpus/1776-march-31-abigail-adams.txt"
]
},
"warnings": [],
"errors": [],
"next_steps": [
{
"id": "init-project",
"purpose": "Initialize the project inside adams-letters/ (cd there first).",
"command": [
"bewley",
"init"
],
"mutates_state": true,
"requires_network": false,
"requires_user_approval": false
}
]
}Step into that directory — every later command runs from here — and initialize the project:
cd adams-letters
bewley init
Show command output
{
"schema_version": "2.0",
"status": "ok",
"command": "bewley init",
"argv": [
"bewley",
"init"
],
"data": {
"status": "initialized"
},
"warnings": [],
"errors": [],
"next_steps": [
{
"id": "add-document",
"purpose": "Add the first source document.",
"command": [
"bewley",
"add",
"--help"
],
"mutates_state": false,
"requires_network": false,
"requires_user_approval": false,
"reason": "Supply the required values using this template: bewley add 'corpus/<filename>'"
}
]
}Before importing anything, record what the study is. Chapter 1 stated a research question in prose; these two commands make it part of the project, so the method and the question live in the same auditable record as the coding they govern — and bewley next can point at the gap when they are missing:
bewley study set --method grounded-theory --unit document
Show command output
{
"schema_version": "2.0",
"status": "ok",
"command": "bewley study set",
"argv": [
"bewley",
"study",
"set",
"--method",
"grounded-theory",
"--unit",
"document"
],
"data": {
"method": "grounded-theory",
"unit_of_analysis": "document",
"purpose": null,
"research_questions": []
},
"warnings": [],
"errors": [],
"next_steps": [
{
"id": "add-question",
"purpose": "Record the research question the coding will answer.",
"command": [
"bewley",
"question",
"add",
"--help"
],
"mutates_state": false,
"requires_network": false,
"requires_user_approval": false,
"reason": "Supply the required values using this template: bewley question add '<question>'"
}
]
}bewley question add "How did John and Abigail Adams negotiate public duty, household responsibility, political voice, danger, and emotional intimacy during the American Revolution?"
Show command output
{
"schema_version": "2.0",
"status": "ok",
"command": "bewley question add",
"argv": [
"bewley",
"question",
"add",
"How did John and Abigail Adams negotiate public duty, household responsibility, political voice, danger, and emotional intimacy during the American Revolution?"
],
"data": {
"question_id": "a3e82047a2a0454cb3ffb9a16b63463e",
"text": "How did John and Abigail Adams negotiate public duty, household responsibility, political voice, danger, and emotional intimacy during the American Revolution?",
"question_count": 1
},
"warnings": [],
"errors": [],
"next_steps": []
}$ bewley study show --human
╭─────────────────────────────────────────── Study ────────────────────────────────────────────╮
│ method grounded-theory │
│ unit of analysis document │
│ │
│ Research questions │
│ 1. How did John and Abigail Adams negotiate public duty, household responsibility, │
│ political voice, danger, and emotional intimacy during the American Revolution? │
│ │
╰──────────────────────────────────────────────────────────────────────────────────────────────╯bewley add corpus/1775-april-30-john-adams.txt
Show command output
{
"schema_version": "2.0",
"status": "ok",
"command": "bewley add",
"argv": [
"bewley",
"add",
"corpus/1775-april-30-john-adams.txt"
],
"data": {
"document_id": "23ca0efc6cf3460f95f03555863e2702"
},
"warnings": [],
"errors": [],
"next_steps": []
}Add the remaining nineteen letters the same way (an agent loops; a person tabs). Then confirm what the project tracks. Agents read the JSON envelope; for a person, --human renders the same answer as a table:
bewley list documents
Show command output
{
"schema_version": "2.0",
"status": "ok",
"command": "bewley list documents",
"argv": [
"bewley",
"list",
"documents"
],
"data": [
{
"document_id": "23ca0efc6cf3460f95f03555863e2702",
"current_path": "corpus/1775-april-30-john-adams.txt",
"revision_count": 1
},
{
"document_id": "888b7ecae251432cac96273b078fd295",
"current_path": "corpus/1775-august-10-abigail-adams.txt",
"revision_count": 1
},
{
"document_id": "dcc32fb8cc4948c382842eba75b45176",
"current_path": "corpus/1775-december-03-john-adams.txt",
"revision_count": 1
}
… 17 further entries elided …
],
"warnings": [],
"errors": [],
"next_steps": []
}$ bewley list documents --human
20 documents
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━┓
┃ Path ┃ Document ID ┃ Revisions ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━╇━━━━━━━━━━━┩
│ corpus/1775-april-30-john-adams.txt │ 23ca0efc6cf3 │ 1 │
│ corpus/1775-august-10-abigail-adams.txt │ 888b7ecae251 │ 1 │
│ corpus/1775-december-03-john-adams.txt │ dcc32fb8cc49 │ 1 │
│ corpus/1775-december-10-abigail-adams.txt │ 78c15de0775b │ 1 │
│ corpus/1775-june-11-john-adams.txt │ d3c62ccfbdac │ 1 │
│ corpus/1775-june-15-abigail-adams.txt │ 39e399df6f53 │ 1 │
│ corpus/1775-june-18-john-adams.txt │ d2937d6951ed │ 1 │
│ corpus/1775-june-22-abigail-adams.txt │ 3dcfe187fd2c │ 1 │
│ corpus/1775-may-04-abigail-adams.txt │ 65213f191333 │ 1 │
│ corpus/1775-october-21-abigail-adams.txt │ 4377752113d8 │ 1 │
│ corpus/1775-october-23-john-adams.txt │ a26df297590d │ 1 │
│ corpus/1775-september-17-john-adams.txt │ d87f4b0e1979 │ 1 │
│ corpus/1776-april-14-john-adams.txt │ aa74b4d3b802 │ 1 │
│ corpus/1776-july-03-john-adams.txt │ c84736ce2542 │ 1 │
│ corpus/1776-july-11-john-adams.txt │ fcde3dcf4f0e │ 1 │
│ corpus/1776-july-13-abigail-adams.txt │ 30241aeab3f2 │ 1 │
│ corpus/1776-june-03-abigail-adams.txt │ 87c5c4e3fb1a │ 1 │
│ corpus/1776-march-02-abigail-adams.txt │ cc8437d06d05 │ 1 │
│ corpus/1776-march-17-john-adams.txt │ a4d59341367c │ 1 │
│ corpus/1776-march-31-abigail-adams.txt │ 70c270391b15 │ 1 │
└───────────────────────────────────────────┴──────────────┴───────────┘--human: the twenty tracked letters.Documents are the what; cases are the who. A case is a person (or organization, or site) the study is about, and it is what cross-corpus comparison hangs on later — "her letters against his" only works if the project knows which letters are hers. Create one case per correspondent, give each a typed attribute that matters to the research question, and link every letter to its author. Nothing here is inferred from file names: each link is an explicit, recorded decision.
bewley case create "Abigail Adams" --type person
Show command output
{
"schema_version": "2.0",
"status": "ok",
"command": "bewley case create",
"argv": [
"bewley",
"case",
"create",
"Abigail Adams",
"--type",
"person"
],
"data": {
"case_id": "6d7e9766138f4dc78f8b464b3d26f5ef",
"name": "Abigail Adams",
"case_type": "person"
},
"warnings": [],
"errors": [],
"next_steps": [
{
"id": "link-documents",
"purpose": "Link the documents this case authored or appears in.",
"command": [
"bewley",
"case",
"link",
"--help"
],
"mutates_state": false,
"requires_network": false,
"requires_user_approval": false,
"reason": "Supply the required values using this template: bewley case link 'Abigail Adams' 'corpus/<file>' --as author"
}
]
}The same for John. Attributes are defined once, project-wide, with a declared type — here a categorical role whose two values are the study's central contrast — and then set per case (bewley case set "Abigail Adams" role home-front). An attribute can also be set to an explicit special state (missing, unknown, not_applicable, confidential) so a blank never has to be guessed at:
bewley attribute define role --type categorical --values "home-front,congressional-delegate"
Show command output
{
"schema_version": "2.0",
"status": "ok",
"command": "bewley attribute define",
"argv": [
"bewley",
"attribute",
"define",
"role",
"--type",
"categorical",
"--values",
"home-front,congressional-delegate"
],
"data": {
"attribute_id": "9d3736c428d6433298f8d4058aa572d7",
"name": "role",
"value_type": "categorical",
"allowed_values": [
"home-front",
"congressional-delegate"
]
},
"warnings": [],
"errors": [],
"next_steps": []
}Linking is a typed relationship — author here, but a focus-group participant or an organization a document merely discusses would be participant or subject:
bewley case link "Abigail Adams" corpus/1775-may-04-abigail-adams.txt --as author
Show command output
{
"schema_version": "2.0",
"status": "ok",
"command": "bewley case link",
"argv": [
"bewley",
"case",
"link",
"Abigail Adams",
"corpus/1775-may-04-abigail-adams.txt",
"--as",
"author"
],
"data": {
"link_id": "63f519fd397c483599b1d416660c0926",
"relationship": "author"
},
"warnings": [],
"errors": [],
"next_steps": []
}$ bewley case list --human
2 cases
┏━━━━━━━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━━━┓
┃ name ┃ type ┃ documents ┃ attributes ┃
┡━━━━━━━━━━━━━━━╇━━━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━━━━┩
│ Abigail Adams │ person │ 10 │ 1 │
│ John Adams │ person │ 10 │ 1 │
└───────────────┴────────┴───────────┴────────────┘bewley status
Show command output
{
"schema_version": "2.0",
"status": "ok",
"command": "bewley status",
"argv": [
"bewley",
"status"
],
"data": {
"documents": 20,
"revisions": 20,
"codes": 0,
"active_annotations": 0,
"conflicted_annotations": 0
},
"warnings": [],
"errors": [],
"next_steps": []
}bewley next reads the state on disk — not conversational memory — and recommends the single next action. With documents but no codes, it points at open coding:
bewley next
Show command output
{
"schema_version": "2.0",
"status": "ok",
"command": "bewley next",
"argv": [
"bewley",
"next"
],
"data": {
"schema_version": "1.0",
"ready": true,
"stage": "open_coding",
"phase": "open_coding",
"project_exists": true,
"counts": {
"documents": 20,
"codes": 0,
"active_annotations": 0
},
"study": {
"method": "grounded-theory",
"unit_of_analysis": "document",
"research_questions": 1,
"cases": 2,
"unlinked_documents": 0,
"unassigned_speaker_labels": []
},
"checklist": [
"Read all documents and write qualitative-analysis/corpus_summary.md.",
"Run `bewley open-coding jobs --output jobs.ep --model <model-name>` to package the corpus.",
"Run `ep run jobs.ep --model_list models.ep --output results.ep` (external; requires approval).",
"Run `bewley open-coding ingest results.ep --jobs jobs.ep`.",
"Review the queue: `bewley open-coding candidates`, then record decisions with `bewley open-coding review`."
],
"primary_doc": "workflow",
"blockers": []
},
"warnings": [],
"errors": [],
"next_steps": [
{
"id": "open-coding-jobs",
"purpose": "Package EDSL open-coding jobs",
"command": [
"bewley",
"open-coding",
"jobs",
"--output",
"jobs.ep"
],
"mutates_state": true,
"requires_network": false,
"requires_user_approval": false
}
]
}05 Model-assisted open coding
Open coding — the first, label-inventing pass over the corpus — is the tedious part, and it is what a language model drafts here: for every letter, propose a handful of candidate codes, each with a one-sentence definition and a verbatim supporting quotation. Proposals are all they are; nothing enters the codebook until a person has judged them in the next chapter.
The mechanics use EDSL, Expected Parrot's library for structured model work. One command bundles each letter's current text together with the coding prompt into a portable Jobs file (one "scenario" per letter) — and, because the run command should be executable exactly as suggested, --model also writes a models.ep file naming the model with an adequate answer-length budget:
bewley open-coding jobs --output jobs.ep --model gpt-4.1-mini
Show command output
{
"schema_version": "2.0",
"status": "ok",
"command": "bewley open-coding jobs",
"argv": [
"bewley",
"open-coding",
"jobs",
"--output",
"jobs.ep",
"--model",
"gpt-4.1-mini"
],
"data": {
"object_type": "Jobs",
"output": "jobs.ep",
"question": "open_coding",
"codebook_release": null,
"scenario_count": 20,
"expected_model_calls": 20,
"pilot": false,
"from_failures": null,
"failed_documents": null,
"saved": {
"status": "ok",
"path": "jobs.ep",
"commit": "f4942bc41256b048a5161015418c3d53992694b2",
"branch": "main",
"message": "Save Jobs"
},
"expected_results": "results.ep",
"models": {
"output": "models.ep",
"model": "gpt-4.1-mini",
"max_tokens": 4000
},
"inference": "external",
"answer_contract": {
"type": "json_array",
"item_required_keys": [
"code",
"description",
"quote"
],
"quote_policy": "exact_verbatim"
}
},
"warnings": [],
"errors": [],
"next_steps": [
{
"id": "run-open-coding-jobs",
"purpose": "Run the packaged jobs with the external ep CLI",
"command": [
"ep",
"run",
"jobs.ep",
"--model_list",
"models.ep",
"--output",
"results.ep"
],
"mutates_state": true,
"requires_network": true,
"requires_user_approval": true
}
]
}Note the envelope's shape: twenty scenarios, one expected model call each, inference: external, and a next_steps action that is runnable verbatim and flagged as requiring network access and your approval. Bewley never executes the packaged work itself.
ep auth login # once per workspace; then ep check before paid runs
ep run jobs.ep --model_list models.ep --output results.ep
results.ep in this run is a deterministic fixture Results object (built with the same EDSL API the tests use) containing plausible answers for all twenty scenarios. Everything before and after this point is the real machinery; in a real run only the answers' content differs.The run's answers come back as a Results file. Ingestion audits it against the originating Jobs — every letter × every model, with the Jobs as the denominator so a letter that never returned cannot hide — locates every quoted passage at its exact position in the letter, and writes the proposals to a review file. It does not create codes or annotations:
bewley open-coding ingest results.ep --jobs jobs.ep
Show command output
{
"schema_version": "2.0",
"status": "ok",
"command": "bewley open-coding ingest",
"argv": [
"bewley",
"open-coding",
"ingest",
"results.ep",
"--jobs",
"jobs.ep"
],
"data": {
"object_type": "CandidateCodes",
"output": "qualitative-analysis/candidate_codes.csv",
"ingest_log": "qualitative-analysis/ingest_log.jsonl",
"results": [
"results.ep"
],
"result_count": 20,
"candidate_count": 24,
"scenario_count": 20,
"models": [
"gpt-4.1-mini"
],
"retained_by_source": {
"results.ep": 20
},
"expected_scenarios": 20,
"expected_answers": 20,
"missing_answers": 0,
"duplicate_scenarios": 0,
"superseded_answers": 0,
"failed_scenarios": 0,
"stale_scenarios": 0,
"unresolved_quotes": 0,
"unresolved_details": [],
"partial": false
},
"warnings": [],
"errors": [],
"next_steps": []
}Twenty results yielded 24 candidates, every quotation resolved exactly, and nothing was missing or duplicated. Had any quote failed to resolve, it would be itemized in unresolved_details, never silently dropped.
06 Review, then apply
The candidate CSV is the human checkpoint, and open-coding candidates is how you read it — the full review queue of everything the model proposed, before any judgment:
bewley open-coding candidates
Show command output
{
"schema_version": "2.0",
"status": "ok",
"command": "bewley open-coding candidates",
"argv": [
"bewley",
"open-coding",
"candidates"
],
"data": {
"input": "qualitative-analysis/candidate_codes.csv",
"candidate_count": 24,
"undecided_count": 24,
"proposed_codes": [
{
"code_name": "daily_minutiae",
"candidates": 4
},
{
"code_name": "health_and_scarcity",
"candidates": 2
},
{
"code_name": "household_responsibility",
"candidates": 1
},
{
"code_name": "information_and_delay",
"candidates": 2
},
{
"code_name": "political_voice",
"candidates": 3
},
{
"code_name": "public_duty",
"candidates": 2
},
{
"code_name": "separation_and_affection",
"candidates": 2
},
{
"code_name": "travel_logistics",
"candidates": 3
},
{
"code_name": "war_and_danger",
"candidates": 2
},
{
"code_name": "weather_report",
"candidates": 3
}
],
"candidates": [
{
"candidate_id": "ac2282bbbe2457f1",
"code_name": "daily_minutiae",
"description": "Routine detail without analytic weight.",
"quote": "New York has appointed an ample representation in our Congress, and has appointed a Provincial Congress.",
"source_document_id": "23ca0efc6cf3460f95f03555863e2702",
"source_document_path": "corpus/1775-april-30-john-adams.txt",
"source_revision_id": "2d5f436e597a4d0a85b0923747ef05a9",
"byte_start": "162",
"byte_end": "266",
"resolve_status": "exact",
"source_results": "results.ep",
"source_model": "gpt-4.1-mini",
"source_code_id": "",
"codebook_release_id": "",
"decision": "",
"decision_reason": ""
},
{
"candidate_id": "6682b50095e4a011",
"code_name": "travel_logistics",
"description": "Routine detail without analytic weight.",
"quote": "Dearest Friend,--'T is with a sad heart I take my pen to write to you, because I must be the bearer of what will greatly afflict and distress you.",
"source_document_id": "888b7ecae251432cac96273b078fd295",
"source_document_path": "corpus/1775-august-10-abigail-adams.txt",
"source_revision_id": "bb11fd938c5840538c950fdd51e73b65",
"byte_start": "166",
"byte_end": "312",
"resolve_status": "exact",
"source_results": "results.ep",
"source_model": "gpt-4.1-mini",
"source_code_id": "",
"codebook_release_id": "",
"decision": "",
"decision_reason": ""
},
{
"candidate_id": "b4f0cad1bcc20174",
"code_name": "weather_report",
"description": "Routine detail without analytic weight.",
"quote": "My best Friend,--Yours of November 12th is before me.",
"source_document_id": "dcc32fb8cc4948c382842eba75b45176",
"source_document_path": "corpus/1775-december-03-john-adams.txt",
"source_revision_id": "94b61154b0c74ad2b7c34d68b29dcacb",
"byte_start": "174",
"byte_end": "227",
"resolve_status": "exact",
"source_results": "results.ep",
"source_model": "gpt-4.1-mini",
"source_code_id": "",
"codebook_release_id": "",
"decision": "",
"decision_reason": ""
},
{
"candidate_id": "1a0092787f61e687",
"code_name": "political_voice",
"description": "Claims to political standing or representation by or for those excluded from it.",
"quote": "But I cannot conclude without telling you we are all very angry with your House of Assembly for their instructions.",
"source_document_id": "78c15de0775b4c3a866fb99400cf1ee2",
"source_document_path": "corpus/1775-december-10-abigail-adams.txt",
"source_revision_id": "740108283c364427bf13e09d06643520",
"byte_start": "5290",
"byte_end": "5405",
"resolve_status": "exact",
"source_results": "results.ep",
"source_model": "gpt-4.1-mini",
"source_code_id": "",
"codebook_release_id": "",
"decision": "",
"decision_reason": ""
},
{
"candidate_id": "ba88b86dff4a0f0d",
"code_name": "daily_minutiae",
"description": "Routine detail without analytic weight.",
"quote": "I have been this morning to hear Mr.",
"source_document_id": "d3c62ccfbdac4f54bd8a864e6471e13b",
"source_document_path": "corpus/1775-june-11-john-adams.txt",
"source_revision_id": "c4865d9197364bb0bb71929991d90a52",
"byte_start": "168",
"byte_end": "204",
"resolve_status": "exact",
"source_results": "results.ep",
"source_model": "gpt-4.1-mini",
"source_code_id": "",
"codebook_release_id": "",
"decision": "",
"decision_reason": ""
},
{
"candidate_id": "65cd6f29e84d1090",
"code_name": "war_and_danger",
"description": "Direct experience or anticipation of military violence.",
"quote": "We live in continual expectation of alarms.",
"source_document_id": "39e399df6f53455495e1b6d120cd3ea4",
"source_document_path": "corpus/1775-june-15-abigail-adams.txt",
"source_revision_id": "53cf977e89884d8fbd6b6037cce02000",
"byte_start": "1768",
"byte_end": "1811",
"resolve_status": "exact",
"source_results": "results.ep",
"source_model": "gpt-4.1-mini",
"source_code_id": "",
"codebook_release_id": "",
"decision": "",
"decision_reason": ""
},
{
"candidate_id": "21e2b7e7bf477f55",
"code_name": "health_and_scarcity",
"description": "Illness, inoculation, or shortages of goods and money.",
"quote": "Bass, and purchase me a bundle of pins and put them in your trunk for me.",
"source_document_id": "39e399df6f53455495e1b6d120cd3ea4",
"source_document_path": "corpus/1775-june-15-abigail-adams.txt",
"source_revision_id": "53cf977e89884d8fbd6b6037cce02000",
"byte_start": "4644",
"byte_end": "4717",
"resolve_status": "exact",
"source_results": "results.ep",
"source_model": "gpt-4.1-mini",
"source_code_id": "",
"codebook_release_id": "",
"decision": "",
"decision_reason": ""
},
{
"candidate_id": "6369cc4de9d01f8d",
"code_name": "travel_logistics",
"description": "Routine detail without analytic weight.",
"quote": "This letter, I presume, will go by the brave and amiable General Washington.",
"source_document_id": "d2937d6951ed423e892dc4f444684684",
"source_document_path": "corpus/1775-june-18-john-adams.txt",
"source_revision_id": "a4a0f25a985e44c785082d98fdbd28c8",
"byte_start": "140",
"byte_end": "216",
"resolve_status": "exact",
"source_results": "results.ep",
"source_model": "gpt-4.1-mini",
"source_code_id": "",
"codebook_release_id": "",
"decision": "",
"decision_reason": ""
},
{
"candidate_id": "622b0f7f52e16065",
"code_name": "weather_report",
"description": "Routine detail without analytic weight.",
"quote": "I received yours of June 10th, for which I thank you.",
"source_document_id": "3dcfe187fd2c43c99f5943c6b806dd65",
"source_document_path": "corpus/1775-june-22-abigail-adams.txt",
"source_revision_id": "766ecd10ebe041ba930359bde2e2e292",
"byte_start": "140",
"byte_end": "193",
"resolve_status": "exact",
"source_results": "results.ep",
"source_model": "gpt-4.1-mini",
"source_code_id": "",
"codebook_release_id": "",
"decision": "",
"decision_reason": ""
},
{
"candidate_id": "1dcfdcb327d5a54e",
"code_name": "information_and_delay",
"description": "The struggle to get timely news and letters.",
"quote": "I want very much to hear from you, how you stood your journey, and in what state you find yourself now.",
"source_document_id": "65213f1913334ec69132b54907907dc2",
"source_document_path": "corpus/1775-may-04-abigail-adams.txt",
"source_revision_id": "64bc625fa54f4c34bc652d423ee30306",
"byte_start": "1513",
"byte_end": "1616",
"resolve_status": "exact",
"source_results": "results.ep",
"source_model": "gpt-4.1-mini",
"source_code_id": "",
"codebook_release_id": "",
"decision": "",
"decision_reason": ""
},
{
"candidate_id": "acdc1a8e18aae7a0",
"code_name": "separation_and_affection",
"description": "Longing, tenderness, and the cost of being apart.",
"quote": "I felt very anxious about you; though I endeavored to be very insensible and heroic, yet my heart felt like a heart of lead.",
"source_document_id": "65213f1913334ec69132b54907907dc2",
"source_document_path": "corpus/1775-may-04-abigail-adams.txt",
"source_revision_id": "64bc625fa54f4c34bc652d423ee30306",
"byte_start": "1617",
"byte_end": "1741",
"resolve_status": "exact",
"source_results": "results.ep",
"source_model": "gpt-4.1-mini",
"source_code_id": "",
"codebook_release_id": "",
"decision": "",
"decision_reason": ""
},
{
"candidate_id": "78aec605dbb30a0a",
"code_name": "daily_minutiae",
"description": "Routine detail without analytic weight.",
"quote": "The sickness has abated here and in the neighboring towns.",
"source_document_id": "4377752113d84a3d9dc2b5593a0350dc",
"source_document_path": "corpus/1775-october-21-abigail-adams.txt",
"source_revision_id": "b2ce583186b445a48f26b7066758fc9a",
"byte_start": "168",
"byte_end": "226",
"resolve_status": "exact",
"source_results": "results.ep",
"source_model": "gpt-4.1-mini",
"source_code_id": "",
"codebook_release_id": "",
"decision": "",
"decision_reason": ""
},
{
"candidate_id": "f36aa5f0b9db970b",
"code_name": "travel_logistics",
"description": "Routine detail without analytic weight.",
"quote": "Yesterday yours of October 9th came to hand.",
"source_document_id": "a26df297590d448bb4861df65db73acf",
"source_document_path": "corpus/1775-october-23-john-adams.txt",
"source_revision_id": "229a960a5b48435c8df2c7af75e5e302",
"byte_start": "174",
"byte_end": "218",
"resolve_status": "exact",
"source_results": "results.ep",
"source_model": "gpt-4.1-mini",
"source_code_id": "",
"codebook_release_id": "",
"decision": "",
"decision_reason": ""
},
{
"candidate_id": "3c7e7b451330f608",
"code_name": "public_duty",
"description": "Framing congressional or military service as obligation to the country.",
"quote": "Henry is made a General in Virginia, and therefore could not come.",
"source_document_id": "d87f4b0e197943ff8b82716c24381bae",
"source_document_path": "corpus/1775-september-17-john-adams.txt",
"source_revision_id": "e754c3c870d848d6aa6132fcb3fc1aa8",
"byte_start": "1833",
"byte_end": "1899",
"resolve_status": "exact",
"source_results": "results.ep",
"source_model": "gpt-4.1-mini",
"source_code_id": "",
"codebook_release_id": "",
"decision": "",
"decision_reason": ""
},
{
"candidate_id": "83377f4b36f04e8b",
"code_name": "separation_and_affection",
"description": "Longing, tenderness, and the cost of being apart.",
"quote": "Remember me in the tenderest language to all our little folks.",
"source_document_id": "d87f4b0e197943ff8b82716c24381bae",
"source_document_path": "corpus/1775-september-17-john-adams.txt",
"source_revision_id": "e754c3c870d848d6aa6132fcb3fc1aa8",
"byte_start": "2232",
"byte_end": "2294",
"resolve_status": "exact",
"source_results": "results.ep",
"source_model": "gpt-4.1-mini",
"source_code_id": "",
"codebook_release_id": "",
"decision": "",
"decision_reason": ""
},
{
"candidate_id": "f3123476f04beae8",
"code_name": "political_voice",
"description": "Claims to political standing or representation by or for those excluded from it.",
"quote": "As to your extraordinary code of laws, I cannot but laugh.",
"source_document_id": "aa74b4d3b80246e3adba8085d9c844d9",
"source_document_path": "corpus/1776-april-14-john-adams.txt",
"source_revision_id": "0d137db83a524d7ab094e21980458b3f",
"byte_start": "3117",
"byte_end": "3175",
"resolve_status": "exact",
"source_results": "results.ep",
"source_model": "gpt-4.1-mini",
"source_code_id": "",
"codebook_release_id": "",
"decision": "",
"decision_reason": ""
},
{
"candidate_id": "cfd0b0dbf0e136ca",
"code_name": "public_duty",
"description": "Framing congressional or military service as obligation to the country.",
"quote": "The second day of July, 1776, will be the most memorable epocha in the history of America.",
"source_document_id": "c84736ce25424513be12e1eca0d747d0",
"source_document_path": "corpus/1776-july-03-john-adams.txt",
"source_revision_id": "9fc3474a95524eb7b44880b8eba38244",
"byte_start": "2892",
"byte_end": "2982",
"resolve_status": "exact",
"source_results": "results.ep",
"source_model": "gpt-4.1-mini",
"source_code_id": "",
"codebook_release_id": "",
"decision": "",
"decision_reason": ""
},
{
"candidate_id": "a9f437214a33f2d2",
"code_name": "weather_report",
"description": "Routine detail without analytic weight.",
"quote": "You seem to be situated in the place of greatest tranquillity and security of any upon the continent.",
"source_document_id": "fcde3dcf4f0e4966a8bbd2f62de155cf",
"source_document_path": "corpus/1776-july-11-john-adams.txt",
"source_revision_id": "4508a6a1fbb74bf2a95607f347266232",
"byte_start": "169",
"byte_end": "270",
"resolve_status": "exact",
"source_results": "results.ep",
"source_model": "gpt-4.1-mini",
"source_code_id": "",
"codebook_release_id": "",
"decision": "",
"decision_reason": ""
},
{
"candidate_id": "417d1c255ac68f7f",
"code_name": "health_and_scarcity",
"description": "Illness, inoculation, or shortages of goods and money.",
"quote": "I now date from Boston, where I yesterday arrived and was with all of our little ones inoculated for the small-pox.",
"source_document_id": "30241aeab3f248fcb13a1aee5cb44a41",
"source_document_path": "corpus/1776-july-13-abigail-adams.txt",
"source_revision_id": "e5628783b2c04940972e438cd338de85",
"byte_start": "362",
"byte_end": "477",
"resolve_status": "exact",
"source_results": "results.ep",
"source_model": "gpt-4.1-mini",
"source_code_id": "",
"codebook_release_id": "",
"decision": "",
"decision_reason": ""
},
{
"candidate_id": "4a0bd18b6c250d69",
"code_name": "household_responsibility",
"description": "Managing the farm, prices, and family economy alone.",
"quote": "The merchant complains of the farmer and the farmer of the merchant--both are extravagant.",
"source_document_id": "87c5c4e3fb1a4dc1b1f67421ae2d0375",
"source_document_path": "corpus/1776-june-03-abigail-adams.txt",
"source_revision_id": "3af5ecfa57274d83bda24aa5d28f8ea6",
"byte_start": "2223",
"byte_end": "2313",
"resolve_status": "exact",
"source_results": "results.ep",
"source_model": "gpt-4.1-mini",
"source_code_id": "",
"codebook_release_id": "",
"decision": "",
"decision_reason": ""
},
{
"candidate_id": "18b58cd4451375d7",
"code_name": "daily_minutiae",
"description": "Routine detail without analytic weight.",
"quote": "I was greatly rejoiced at the return of your servant, to find you had safely arrived, and that you were well.",
"source_document_id": "cc8437d06d054265bf9244f4bae3a521",
"source_document_path": "corpus/1776-march-02-abigail-adams.txt",
"source_revision_id": "45a16503ecc342efa0c38404a2fa05a0",
"byte_start": "176",
"byte_end": "285",
"resolve_status": "exact",
"source_results": "results.ep",
"source_model": "gpt-4.1-mini",
"source_code_id": "",
"codebook_release_id": "",
"decision": "",
"decision_reason": ""
},
{
"candidate_id": "1e30ed3fb28dfe25",
"code_name": "war_and_danger",
"description": "Direct experience or anticipation of military violence.",
"quote": "By a vessel from Salem a cannonade was heard from dark till nine o'clock, last night was a week ago.",
"source_document_id": "a4d59341367c49ca97f20d8dcf459f75",
"source_document_path": "corpus/1776-march-17-john-adams.txt",
"source_revision_id": "2bd0dbfd645a4043a48d529ef2a207dd",
"byte_start": "1433",
"byte_end": "1533",
"resolve_status": "exact",
"source_results": "results.ep",
"source_model": "gpt-4.1-mini",
"source_code_id": "",
"codebook_release_id": "",
"decision": "",
"decision_reason": ""
},
{
"candidate_id": "18b171b82c81a38d",
"code_name": "political_voice",
"description": "Claims to political standing or representation by or for those excluded from it.",
"quote": "And, by the way, in the new code of laws which I suppose it will be necessary for you to make, I desire you would remember the ladies and be more generous and favorable to them than your ancestors.",
"source_document_id": "70c270391b154ea28bc1ca79d01e5416",
"source_document_path": "corpus/1776-march-31-abigail-adams.txt",
"source_revision_id": "3c1661073b344382a5a4fbc7c0e2192c",
"byte_start": "3114",
"byte_end": "3311",
"resolve_status": "exact",
"source_results": "results.ep",
"source_model": "gpt-4.1-mini",
"source_code_id": "",
"codebook_release_id": "",
"decision": "",
"decision_reason": ""
},
{
"candidate_id": "7492d0e97fb321f7",
"code_name": "information_and_delay",
"description": "The struggle to get timely news and letters.",
"quote": "I wish you would ever write me a letter half as long as I write you, and tell me, if you may, where your fleet are gone; what sort of defense Virginia can make against our common enemy; whether it is so situated as to make an able defense.",
"source_document_id": "70c270391b154ea28bc1ca79d01e5416",
"source_document_path": "corpus/1776-march-31-abigail-adams.txt",
"source_revision_id": "3c1661073b344382a5a4fbc7c0e2192c",
"byte_start": "164",
"byte_end": "403",
"resolve_status": "exact",
"source_results": "results.ep",
"source_model": "gpt-4.1-mini",
"source_code_id": "",
"codebook_release_id": "",
"decision": "",
"decision_reason": ""
}
]
},
"warnings": [],
"errors": [],
"next_steps": [
{
"id": "review-candidates",
"purpose": "Record accept/reject/map/adjust decisions for the queue",
"command": [
"bewley",
"open-coding",
"review",
"--help"
],
"mutates_state": false,
"requires_network": false,
"requires_user_approval": false,
"reason": "Supply the required values using this template: bewley open-coding review '<candidate-id>' --decision '<decision>'"
}
]
}$ bewley open-coding candidates --human
24 candidates across 10 proposed codes
┏━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━┓
┃ Code ┃ Description ┃ Quote ┃ Document ┃ Decision ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━┩
│ daily_minutiae │ Routine │ New York has │ 1775-april-30-john │ - │
│ │ detail │ appointed an │ │ │
│ │ without │ ample │ │ │
│ │ analytic │ representati │ │ │
│ │ weight. │ on in our │ │ │
│ │ │ Congress, │ │ │
│ │ │ and has │ │ │
│ │ │ appointed a │ │ │
│ │ │ Provincial │ │ │
│ │ │ Congress. │ │ │
│ daily_minutiae │ Routine │ I have been │ 1775-june-11-john │ - │
│ │ detail │ this morning │ │ │
│ │ without │ to hear Mr. │ │ │
│ │ analytic │ │ │ │
│ │ weight. │ │ │ │
│ daily_minutiae │ Routine │ The sickness │ 1775-october-21-abigail │ - │
│ │ detail │ has abated │ │ │
│ │ without │ here and in │ │ │
│ │ analytic │ the │ │ │
│ │ weight. │ neighboring │ │ │
│ │ │ towns. │ │ │
│ daily_minutiae │ Routine │ I was │ 1776-march-02-abigail │ - │
│ │ detail │ greatly │ │ │
│ │ without │ rejoiced at │ │ │
│ │ analytic │ the return │ │ │
│ │ weight. │ of your │ │ │
│ │ │ servant, to │ │ │
│ │ │ find you had │ │ │
│ │ │ safely │ │ │
│ │ │ arrived, and │ │ │
│ │ │ that you │ │ │
│ │ │ were well. │ │ │
│ health_and_scarcity │ Illness, │ Bass, and │ 1775-june-15-abigail │ - │
│ │ inoculation, │ purchase me │ │ │
│ │ or shortages │ a bundle of │ │ │
│ │ of goods and │ pins and put │ │ │
│ │ money. │ them in your │ │ │
│ │ │ trunk for │ │ │
│ │ │ me. │ │ │
│ health_and_scarcity │ Illness, │ I now date │ 1776-july-13-abigail │ - │
│ │ inoculation, │ from Boston, │ │ │
│ │ or shortages │ where I │ │ │
│ │ of goods and │ yesterday │ │ │
│ │ money. │ arrived and │ │ │
│ │ │ was with all │ │ │
│ │ │ of our │ │ │
│ │ │ little ones │ │ │
│ │ │ inoculated │ │ │
│ │ │ for the … │ │ │
│ household_responsibility │ Managing the │ The merchant │ 1776-june-03-abigail │ - │
│ │ farm, │ complains of │ │ │
│ │ prices, and │ the farmer │ │ │
│ │ family │ and the │ │ │
│ │ economy │ farmer of │ │ │
│ │ alone. │ the │ │ │
│ │ │ merchant--bo │ │ │
│ │ │ th are │ │ │
│ │ │ extravagant. │ │ │
│ information_and_delay │ The struggle │ I want very │ 1775-may-04-abigail │ - │
│ │ to get │ much to hear │ │ │
│ │ timely news │ from you, │ │ │
│ │ and letters. │ how you │ │ │
│ │ │ stood your │ │ │
│ │ │ journey, and │ │ │
│ │ │ in what │ │ │
│ │ │ state you │ │ │
│ │ │ find │ │ │
│ │ │ yourself │ │ │
│ │ │ now. │ │ │
│ information_and_delay │ The struggle │ I wish you │ 1776-march-31-abigail │ - │
│ │ to get │ would ever │ │ │
│ │ timely news │ write me a │ │ │
│ │ and letters. │ letter half │ │ │
│ │ │ as long as I │ │ │
│ │ │ write you, │ │ │
│ │ │ and tell me, │ │ │
│ │ │ if you may, │ │ │
│ │ │ where your … │ │ │
│ political_voice │ Claims to │ But I cannot │ 1775-december-10-abigail │ - │
│ │ political │ conclude │ │ │
│ │ standing or │ without │ │ │
│ │ representati │ telling you │ │ │
│ │ on by or for │ we are all │ │ │
│ │ those │ very angry │ │ │
│ │ excluded │ with your │ │ │
│ │ from it. │ House of │ │ │
│ │ │ Assembly for │ │ │
│ │ │ their … │ │ │
│ political_voice │ Claims to │ As to your │ 1776-april-14-john │ - │
│ │ political │ extraordinar │ │ │
│ │ standing or │ y code of │ │ │
│ │ representati │ laws, I │ │ │
│ │ on by or for │ cannot but │ │ │
│ │ those │ laugh. │ │ │
│ │ excluded │ │ │ │
│ │ from it. │ │ │ │
│ political_voice │ Claims to │ And, by the │ 1776-march-31-abigail │ - │
│ │ political │ way, in the │ │ │
│ │ standing or │ new code of │ │ │
│ │ representati │ laws which I │ │ │
│ │ on by or for │ suppose it │ │ │
│ │ those │ will be │ │ │
│ │ excluded │ necessary │ │ │
│ │ from it. │ for you to │ │ │
│ │ │ make, I │ │ │
│ │ │ desire you … │ │ │
│ public_duty │ Framing │ Henry is │ 1775-september-17-john │ - │
│ │ congressiona │ made a │ │ │
│ │ l or │ General in │ │ │
│ │ military │ Virginia, │ │ │
│ │ service as │ and │ │ │
│ │ obligation │ therefore │ │ │
│ │ to the │ could not │ │ │
│ │ country. │ come. │ │ │
│ public_duty │ Framing │ The second │ 1776-july-03-john │ - │
│ │ congressiona │ day of July, │ │ │
│ │ l or │ 1776, will │ │ │
│ │ military │ be the most │ │ │
│ │ service as │ memorable │ │ │
│ │ obligation │ epocha in │ │ │
│ │ to the │ the history │ │ │
│ │ country. │ of America. │ │ │
│ separation_and_affection │ Longing, │ I felt very │ 1775-may-04-abigail │ - │
│ │ tenderness, │ anxious │ │ │
│ │ and the cost │ about you; │ │ │
│ │ of being │ though I │ │ │
│ │ apart. │ endeavored │ │ │
│ │ │ to be very │ │ │
│ │ │ insensible │ │ │
│ │ │ and heroic, │ │ │
│ │ │ yet my heart │ │ │
│ │ │ felt like a │ │ │
│ │ │ … │ │ │
│ separation_and_affection │ Longing, │ Remember me │ 1775-september-17-john │ - │
│ │ tenderness, │ in the │ │ │
│ │ and the cost │ tenderest │ │ │
│ │ of being │ language to │ │ │
│ │ apart. │ all our │ │ │
│ │ │ little │ │ │
│ │ │ folks. │ │ │
│ travel_logistics │ Routine │ Dearest │ 1775-august-10-abigail │ - │
│ │ detail │ Friend,--'T │ │ │
│ │ without │ is with a │ │ │
│ │ analytic │ sad heart I │ │ │
│ │ weight. │ take my pen │ │ │
│ │ │ to write to │ │ │
│ │ │ you, because │ │ │
│ │ │ I must be │ │ │
│ │ │ the bearer │ │ │
│ │ │ of what … │ │ │
│ travel_logistics │ Routine │ This letter, │ 1775-june-18-john │ - │
│ │ detail │ I presume, │ │ │
│ │ without │ will go by │ │ │
│ │ analytic │ the brave │ │ │
│ │ weight. │ and amiable │ │ │
│ │ │ General │ │ │
│ │ │ Washington. │ │ │
│ travel_logistics │ Routine │ Yesterday │ 1775-october-23-john │ - │
│ │ detail │ yours of │ │ │
│ │ without │ October 9th │ │ │
│ │ analytic │ came to │ │ │
│ │ weight. │ hand. │ │ │
│ war_and_danger │ Direct │ We live in │ 1775-june-15-abigail │ - │
│ │ experience │ continual │ │ │
│ │ or │ expectation │ │ │
│ │ anticipation │ of alarms. │ │ │
│ │ of military │ │ │ │
│ │ violence. │ │ │ │
│ war_and_danger │ Direct │ By a vessel │ 1776-march-17-john │ - │
│ │ experience │ from Salem a │ │ │
│ │ or │ cannonade │ │ │
│ │ anticipation │ was heard │ │ │
│ │ of military │ from dark │ │ │
│ │ violence. │ till nine │ │ │
│ │ │ o'clock, │ │ │
│ │ │ last night │ │ │
│ │ │ was a week │ │ │
│ │ │ ago. │ │ │
│ weather_report │ Routine │ My best │ 1775-december-03-john │ - │
│ │ detail │ Friend,--You │ │ │
│ │ without │ rs of │ │ │
│ │ analytic │ November │ │ │
│ │ weight. │ 12th is │ │ │
│ │ │ before me. │ │ │
│ weather_report │ Routine │ I received │ 1775-june-22-abigail │ - │
│ │ detail │ yours of │ │ │
│ │ without │ June 10th, │ │ │
│ │ analytic │ for which I │ │ │
│ │ weight. │ thank you. │ │ │
│ weather_report │ Routine │ You seem to │ 1776-july-11-john │ - │
│ │ detail │ be situated │ │ │
│ │ without │ in the place │ │ │
│ │ analytic │ of greatest │ │ │
│ │ weight. │ tranquillity │ │ │
│ │ │ and security │ │ │
│ │ │ of any upon │ │ │
│ │ │ the │ │ │
│ │ │ continent. │ │ │
└──────────────────────────┴──────────────┴──────────────┴──────────────────────────┴──────────┘daily_minutiae, travel_logistics, and weather_report are topic labels without analytic weight.Review means recording a judgment about each candidate — and the judgment itself is part of the analysis, so it is recorded as an event: who decided, what, and why. Reject the topic labels with their reasons (one shown; the other nine rejections use the same form):
bewley open-coding review ac2282bbbe2457f1 --decision reject --reason 'Routine detail without analytic weight.'
Show command output
{
"schema_version": "2.0",
"status": "ok",
"command": "bewley open-coding review",
"argv": [
"bewley",
"open-coding",
"review",
"ac2282bbbe2457f1",
"--decision",
"reject",
"--reason",
"Routine detail without analytic weight."
],
"data": {
"decision": "reject",
"recorded": [
"ac2282bbbe2457f1"
],
"recorded_count": 1,
"undecided_remaining": 23
},
"warnings": [],
"errors": [],
"next_steps": [
{
"id": "continue-review",
"purpose": "Decide the remaining 23 candidate(s)",
"command": [
"bewley",
"open-coding",
"candidates"
],
"mutates_state": false,
"requires_network": false,
"requires_user_approval": false
}
]
}Then accept everything still undecided in one stroke:
bewley open-coding review --all-remaining --decision accept
Show command output
{
"schema_version": "2.0",
"status": "ok",
"command": "bewley open-coding review",
"argv": [
"bewley",
"open-coding",
"review",
"--all-remaining",
"--decision",
"accept"
],
"data": {
"decision": "accept",
"recorded": [
"1a0092787f61e687",
"65cd6f29e84d1090",
"21e2b7e7bf477f55",
"1dcfdcb327d5a54e",
"acdc1a8e18aae7a0",
"3c7e7b451330f608",
"83377f4b36f04e8b",
"f3123476f04beae8",
"cfd0b0dbf0e136ca",
"417d1c255ac68f7f",
"4a0bd18b6c250d69",
"1e30ed3fb28dfe25",
"18b171b82c81a38d",
"7492d0e97fb321f7"
],
"recorded_count": 14,
"undecided_remaining": 0
},
"warnings": [],
"errors": [],
"next_steps": [
{
"id": "apply-decisions",
"purpose": "Preview applying the recorded decisions",
"command": [
"bewley",
"open-coding",
"apply",
"--dry-run"
],
"mutates_state": false,
"requires_network": false,
"requires_user_approval": false
}
]
}Two other decisions exist for the in-between cases: --decision map --to <code> applies a candidate under a different (perhaps existing) code instead of rejecting a near-duplicate, and --decision adjust --bytes S:E corrects a proposal's span — including repairing one whose quote failed to resolve. The queue now shows every verdict:
$ bewley open-coding candidates --human
24 candidates across 10 proposed codes
┏━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━┓
┃ Code ┃ Description ┃ Quote ┃ Document ┃ Decision ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━┩
│ daily_minutiae │ Routine │ New York has │ 1775-april-30-john │ reject │
│ │ detail │ appointed an │ │ │
│ │ without │ ample │ │ │
│ │ analytic │ representati │ │ │
│ │ weight. │ on in our │ │ │
│ │ │ Congress, │ │ │
│ │ │ and has │ │ │
│ │ │ appointed a │ │ │
│ │ │ Provincial │ │ │
│ │ │ Congress. │ │ │
│ daily_minutiae │ Routine │ I have been │ 1775-june-11-john │ reject │
│ │ detail │ this morning │ │ │
│ │ without │ to hear Mr. │ │ │
│ │ analytic │ │ │ │
│ │ weight. │ │ │ │
│ daily_minutiae │ Routine │ The sickness │ 1775-october-21-abigail │ reject │
│ │ detail │ has abated │ │ │
│ │ without │ here and in │ │ │
│ │ analytic │ the │ │ │
│ │ weight. │ neighboring │ │ │
│ │ │ towns. │ │ │
│ daily_minutiae │ Routine │ I was │ 1776-march-02-abigail │ reject │
│ │ detail │ greatly │ │ │
│ │ without │ rejoiced at │ │ │
│ │ analytic │ the return │ │ │
│ │ weight. │ of your │ │ │
│ │ │ servant, to │ │ │
│ │ │ find you had │ │ │
│ │ │ safely │ │ │
│ │ │ arrived, and │ │ │
│ │ │ that you │ │ │
│ │ │ were well. │ │ │
│ health_and_scarcity │ Illness, │ Bass, and │ 1775-june-15-abigail │ accept │
│ │ inoculation, │ purchase me │ │ │
│ │ or shortages │ a bundle of │ │ │
│ │ of goods and │ pins and put │ │ │
│ │ money. │ them in your │ │ │
│ │ │ trunk for │ │ │
│ │ │ me. │ │ │
│ health_and_scarcity │ Illness, │ I now date │ 1776-july-13-abigail │ accept │
│ │ inoculation, │ from Boston, │ │ │
│ │ or shortages │ where I │ │ │
│ │ of goods and │ yesterday │ │ │
│ │ money. │ arrived and │ │ │
│ │ │ was with all │ │ │
│ │ │ of our │ │ │
│ │ │ little ones │ │ │
│ │ │ inoculated │ │ │
│ │ │ for the … │ │ │
│ household_responsibility │ Managing the │ The merchant │ 1776-june-03-abigail │ accept │
│ │ farm, │ complains of │ │ │
│ │ prices, and │ the farmer │ │ │
│ │ family │ and the │ │ │
│ │ economy │ farmer of │ │ │
│ │ alone. │ the │ │ │
│ │ │ merchant--bo │ │ │
│ │ │ th are │ │ │
│ │ │ extravagant. │ │ │
│ information_and_delay │ The struggle │ I want very │ 1775-may-04-abigail │ accept │
│ │ to get │ much to hear │ │ │
│ │ timely news │ from you, │ │ │
│ │ and letters. │ how you │ │ │
│ │ │ stood your │ │ │
│ │ │ journey, and │ │ │
│ │ │ in what │ │ │
│ │ │ state you │ │ │
│ │ │ find │ │ │
│ │ │ yourself │ │ │
│ │ │ now. │ │ │
│ information_and_delay │ The struggle │ I wish you │ 1776-march-31-abigail │ accept │
│ │ to get │ would ever │ │ │
│ │ timely news │ write me a │ │ │
│ │ and letters. │ letter half │ │ │
│ │ │ as long as I │ │ │
│ │ │ write you, │ │ │
│ │ │ and tell me, │ │ │
│ │ │ if you may, │ │ │
│ │ │ where your … │ │ │
│ political_voice │ Claims to │ But I cannot │ 1775-december-10-abigail │ accept │
│ │ political │ conclude │ │ │
│ │ standing or │ without │ │ │
│ │ representati │ telling you │ │ │
│ │ on by or for │ we are all │ │ │
│ │ those │ very angry │ │ │
│ │ excluded │ with your │ │ │
│ │ from it. │ House of │ │ │
│ │ │ Assembly for │ │ │
│ │ │ their … │ │ │
│ political_voice │ Claims to │ As to your │ 1776-april-14-john │ accept │
│ │ political │ extraordinar │ │ │
│ │ standing or │ y code of │ │ │
│ │ representati │ laws, I │ │ │
│ │ on by or for │ cannot but │ │ │
│ │ those │ laugh. │ │ │
│ │ excluded │ │ │ │
│ │ from it. │ │ │ │
│ political_voice │ Claims to │ And, by the │ 1776-march-31-abigail │ accept │
│ │ political │ way, in the │ │ │
│ │ standing or │ new code of │ │ │
│ │ representati │ laws which I │ │ │
│ │ on by or for │ suppose it │ │ │
│ │ those │ will be │ │ │
│ │ excluded │ necessary │ │ │
│ │ from it. │ for you to │ │ │
│ │ │ make, I │ │ │
│ │ │ desire you … │ │ │
│ public_duty │ Framing │ Henry is │ 1775-september-17-john │ accept │
│ │ congressiona │ made a │ │ │
│ │ l or │ General in │ │ │
│ │ military │ Virginia, │ │ │
│ │ service as │ and │ │ │
│ │ obligation │ therefore │ │ │
│ │ to the │ could not │ │ │
│ │ country. │ come. │ │ │
│ public_duty │ Framing │ The second │ 1776-july-03-john │ accept │
│ │ congressiona │ day of July, │ │ │
│ │ l or │ 1776, will │ │ │
│ │ military │ be the most │ │ │
│ │ service as │ memorable │ │ │
│ │ obligation │ epocha in │ │ │
│ │ to the │ the history │ │ │
│ │ country. │ of America. │ │ │
│ separation_and_affection │ Longing, │ I felt very │ 1775-may-04-abigail │ accept │
│ │ tenderness, │ anxious │ │ │
│ │ and the cost │ about you; │ │ │
│ │ of being │ though I │ │ │
│ │ apart. │ endeavored │ │ │
│ │ │ to be very │ │ │
│ │ │ insensible │ │ │
│ │ │ and heroic, │ │ │
│ │ │ yet my heart │ │ │
│ │ │ felt like a │ │ │
│ │ │ … │ │ │
│ separation_and_affection │ Longing, │ Remember me │ 1775-september-17-john │ accept │
│ │ tenderness, │ in the │ │ │
│ │ and the cost │ tenderest │ │ │
│ │ of being │ language to │ │ │
│ │ apart. │ all our │ │ │
│ │ │ little │ │ │
│ │ │ folks. │ │ │
│ travel_logistics │ Routine │ Dearest │ 1775-august-10-abigail │ reject │
│ │ detail │ Friend,--'T │ │ │
│ │ without │ is with a │ │ │
│ │ analytic │ sad heart I │ │ │
│ │ weight. │ take my pen │ │ │
│ │ │ to write to │ │ │
│ │ │ you, because │ │ │
│ │ │ I must be │ │ │
│ │ │ the bearer │ │ │
│ │ │ of what … │ │ │
│ travel_logistics │ Routine │ This letter, │ 1775-june-18-john │ reject │
│ │ detail │ I presume, │ │ │
│ │ without │ will go by │ │ │
│ │ analytic │ the brave │ │ │
│ │ weight. │ and amiable │ │ │
│ │ │ General │ │ │
│ │ │ Washington. │ │ │
│ travel_logistics │ Routine │ Yesterday │ 1775-october-23-john │ reject │
│ │ detail │ yours of │ │ │
│ │ without │ October 9th │ │ │
│ │ analytic │ came to │ │ │
│ │ weight. │ hand. │ │ │
│ war_and_danger │ Direct │ We live in │ 1775-june-15-abigail │ accept │
│ │ experience │ continual │ │ │
│ │ or │ expectation │ │ │
│ │ anticipation │ of alarms. │ │ │
│ │ of military │ │ │ │
│ │ violence. │ │ │ │
│ war_and_danger │ Direct │ By a vessel │ 1776-march-17-john │ accept │
│ │ experience │ from Salem a │ │ │
│ │ or │ cannonade │ │ │
│ │ anticipation │ was heard │ │ │
│ │ of military │ from dark │ │ │
│ │ violence. │ till nine │ │ │
│ │ │ o'clock, │ │ │
│ │ │ last night │ │ │
│ │ │ was a week │ │ │
│ │ │ ago. │ │ │
│ weather_report │ Routine │ My best │ 1775-december-03-john │ reject │
│ │ detail │ Friend,--You │ │ │
│ │ without │ rs of │ │ │
│ │ analytic │ November │ │ │
│ │ weight. │ 12th is │ │ │
│ │ │ before me. │ │ │
│ weather_report │ Routine │ I received │ 1775-june-22-abigail │ reject │
│ │ detail │ yours of │ │ │
│ │ without │ June 10th, │ │ │
│ │ analytic │ for which I │ │ │
│ │ weight. │ thank you. │ │ │
│ weather_report │ Routine │ You seem to │ 1776-july-11-john │ reject │
│ │ detail │ be situated │ │ │
│ │ without │ in the place │ │ │
│ │ analytic │ of greatest │ │ │
│ │ weight. │ tranquillity │ │ │
│ │ │ and security │ │ │
│ │ │ of any upon │ │ │
│ │ │ the │ │ │
│ │ │ continent. │ │ │
└──────────────────────────┴──────────────┴──────────────┴──────────────────────────┴──────────┘apply executes the decisions: accepted candidates become codes (carrying their definitions into the codebook) and exact-span annotations; rejected ones are skipped with their recorded reasons; anything undecided would be itemized and skipped, fail-closed. Preview first:
bewley open-coding apply --dry-run
Show command output
{
"schema_version": "2.0",
"status": "ok",
"command": "bewley open-coding apply",
"argv": [
"bewley",
"open-coding",
"apply",
"--dry-run"
],
"data": {
"input": "qualitative-analysis/candidate_codes.csv",
"rows": 24,
"dry_run": true,
"codes_to_create": [
"health_and_scarcity",
"household_responsibility",
"information_and_delay",
"political_voice",
"public_duty",
"separation_and_affection",
"war_and_danger"
],
"annotations_planned": 14,
"annotations_applied": 0,
"skipped": 10,
"skipped_details": [
{
"candidate_id": "ac2282bbbe2457f1",
"code_name": "daily_minutiae",
"reason": "rejected",
"review_reason": "Routine detail without analytic weight."
},
{
"candidate_id": "6682b50095e4a011",
"code_name": "travel_logistics",
"reason": "rejected",
"review_reason": "Topic label; says nothing about the research question."
},
{
"candidate_id": "b4f0cad1bcc20174",
"code_name": "weather_report",
"reason": "rejected",
"review_reason": "Topic label; says nothing about the research question."
},
{
"candidate_id": "ba88b86dff4a0f0d",
"code_name": "daily_minutiae",
"reason": "rejected",
"review_reason": "Routine detail without analytic weight."
},
{
"candidate_id": "6369cc4de9d01f8d",
"code_name": "travel_logistics",
"reason": "rejected",
"review_reason": "Topic label; says nothing about the research question."
},
{
"candidate_id": "622b0f7f52e16065",
"code_name": "weather_report",
"reason": "rejected",
"review_reason": "Topic label; says nothing about the research question."
},
{
"candidate_id": "78aec605dbb30a0a",
"code_name": "daily_minutiae",
"reason": "rejected",
"review_reason": "Routine detail without analytic weight."
},
{
"candidate_id": "f36aa5f0b9db970b",
"code_name": "travel_logistics",
"reason": "rejected",
"review_reason": "Topic label; says nothing about the research question."
},
{
"candidate_id": "a9f437214a33f2d2",
"code_name": "weather_report",
"reason": "rejected",
"review_reason": "Topic label; says nothing about the research question."
},
{
"candidate_id": "18b58cd4451375d7",
"code_name": "daily_minutiae",
"reason": "rejected",
"review_reason": "Routine detail without analytic weight."
}
],
"review_mode": "decisions",
"decisions": {
"accepted": 14,
"rejected": 10,
"mapped": 0,
"adjusted": 0,
"undecided": 0
}
},
"warnings": [],
"errors": [],
"next_steps": [
{
"id": "apply-for-real",
"purpose": "Apply the reviewed plan",
"command": [
"bewley",
"open-coding",
"apply",
"--input",
"qualitative-analysis/candidate_codes.csv"
],
"mutates_state": true,
"requires_network": false,
"requires_user_approval": false
}
]
}bewley open-coding apply
Show command output
{
"schema_version": "2.0",
"status": "ok",
"command": "bewley open-coding apply",
"argv": [
"bewley",
"open-coding",
"apply"
],
"data": {
"input": "qualitative-analysis/candidate_codes.csv",
"rows": 24,
"dry_run": false,
"codes_to_create": [
"political_voice",
"war_and_danger",
"health_and_scarcity",
"information_and_delay",
"separation_and_affection",
"public_duty",
"household_responsibility"
],
"annotations_planned": 14,
"annotations_applied": 14,
"skipped": 10,
"skipped_details": [
{
"candidate_id": "ac2282bbbe2457f1",
"code_name": "daily_minutiae",
"reason": "rejected",
"review_reason": "Routine detail without analytic weight."
},
{
"candidate_id": "6682b50095e4a011",
"code_name": "travel_logistics",
"reason": "rejected",
"review_reason": "Topic label; says nothing about the research question."
},
{
"candidate_id": "b4f0cad1bcc20174",
"code_name": "weather_report",
"reason": "rejected",
"review_reason": "Topic label; says nothing about the research question."
},
{
"candidate_id": "ba88b86dff4a0f0d",
"code_name": "daily_minutiae",
"reason": "rejected",
"review_reason": "Routine detail without analytic weight."
},
{
"candidate_id": "6369cc4de9d01f8d",
"code_name": "travel_logistics",
"reason": "rejected",
"review_reason": "Topic label; says nothing about the research question."
},
{
"candidate_id": "622b0f7f52e16065",
"code_name": "weather_report",
"reason": "rejected",
"review_reason": "Topic label; says nothing about the research question."
},
{
"candidate_id": "78aec605dbb30a0a",
"code_name": "daily_minutiae",
"reason": "rejected",
"review_reason": "Routine detail without analytic weight."
},
{
"candidate_id": "f36aa5f0b9db970b",
"code_name": "travel_logistics",
"reason": "rejected",
"review_reason": "Topic label; says nothing about the research question."
},
{
"candidate_id": "a9f437214a33f2d2",
"code_name": "weather_report",
"reason": "rejected",
"review_reason": "Topic label; says nothing about the research question."
},
{
"candidate_id": "18b58cd4451375d7",
"code_name": "daily_minutiae",
"reason": "rejected",
"review_reason": "Routine detail without analytic weight."
}
],
"review_mode": "decisions",
"decisions": {
"accepted": 14,
"rejected": 10,
"mapped": 0,
"adjusted": 0,
"undecided": 0
}
},
"warnings": [],
"errors": [],
"next_steps": [
{
"id": "review-applied-coding",
"purpose": "Inspect the applied codes and evidence",
"command": [
"bewley",
"show",
"snippets",
"--help"
],
"mutates_state": false,
"requires_network": false,
"requires_user_approval": false,
"reason": "Supply the required values using this template: bewley show snippets --code '<code-name>'"
}
]
}Only rows whose quotation resolved to exactly one location are applied; anything skipped is itemized with a reason — an unresolved quote, a stale revision, an already-applied row — never guessed. Re-running is idempotent. Freeze this first codebook as a named, immutable release, so after refinement there is something exact to diff against:
bewley codebook release first-pass
Show command output
{
"schema_version": "2.0",
"status": "ok",
"command": "bewley codebook release",
"argv": [
"bewley",
"codebook",
"release",
"first-pass"
],
"data": {
"name": "first-pass",
"release_id": "4179a73be9154e86b0b4961aab9b0136",
"codes": 7
},
"warnings": [],
"errors": [],
"next_steps": []
}The evidence is now queryable:
bewley show snippets --code political_voice
Show command output
{
"schema_version": "2.0",
"status": "ok",
"command": "bewley show snippets",
"argv": [
"bewley",
"show",
"snippets",
"--code",
"political_voice"
],
"data": [
{
"annotation_id": "9f5d3d2765b54b5499d945b5e9fed54e",
"code_name": "political_voice",
"document_path": "corpus/1775-december-10-abigail-adams.txt",
"start_line": 19,
"end_line": 19,
"anchor_status": "clean",
"text": "But I cannot conclude without telling you we are all very angry with your House of Assembly for their instructions."
},
{
"annotation_id": "6a17f6e726ac4fca8b8a17a83f229459",
"code_name": "political_voice",
"document_path": "corpus/1776-april-14-john-adams.txt",
"start_line": 13,
"end_line": 13,
"anchor_status": "clean",
"text": "As to your extraordinary code of laws, I cannot but laugh."
}
… 1 further entries elided …
],
"warnings": [],
"errors": [],
"next_steps": []
}And this is what the coded evidence actually looks like — the point of the whole exercise. With --human, each snippet is the verbatim passage under its code:
$ bewley show snippets --code political_voice --human
3 snippet(s)
╭────────────────────────────────────── political_voice ───────────────────────────────────────╮
│ But I cannot conclude without telling you we are all very angry with your House of Assembly │
│ for their instructions. │
╰────────────────── corpus/1775-december-10-abigail-adams.txt · lines 19–19 ───────────────────╯
╭────────────────────────────────────── political_voice ───────────────────────────────────────╮
│ As to your extraordinary code of laws, I cannot but laugh. │
╰───────────────────── corpus/1776-april-14-john-adams.txt · lines 13–13 ──────────────────────╯
╭────────────────────────────────────── political_voice ───────────────────────────────────────╮
│ And, by the way, in the new code of laws which I suppose it will be necessary for you to │
│ make, I desire you would remember the ladies and be more generous and favorable to them than │
│ your ancestors. │
╰──────────────────── corpus/1776-march-31-abigail-adams.txt · lines 19–19 ────────────────────╯political_voice evidence: Abigail's demand, John's laughing dismissal, and her anger at the Assembly — each anchored to exact lines.$ bewley code list --human
7 codes
┏━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━┳━━━━━━━━━━━┓
┃ Code ┃ Description ┃ Annotations ┃ Documents ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━╇━━━━━━━━━━━┩
│ health_and_scarcity │ Illness, inoculation, or shortages of │ 2 │ 2 │
│ │ goods and money. │ │ │
│ household_responsibility │ Managing the farm, prices, and family │ 1 │ 1 │
│ │ economy alone. │ │ │
│ information_and_delay │ The struggle to get timely news and │ 2 │ 2 │
│ │ letters. │ │ │
│ political_voice │ Claims to political standing or │ 3 │ 3 │
│ │ representation by or for those excluded │ │ │
│ │ from it. │ │ │
│ public_duty │ Framing congressional or military │ 2 │ 2 │
│ │ service as obligation to the country. │ │ │
│ separation_and_affection │ Longing, tenderness, and the cost of │ 2 │ 2 │
│ │ being apart. │ │ │
│ war_and_danger │ Direct experience or anticipation of │ 2 │ 2 │
│ │ military violence. │ │ │
└──────────────────────────┴─────────────────────────────────────────┴─────────────┴───────────┘07 When a run fails
Model runs fail: answers truncate, calls error, scenarios go missing. Bewley fails closed — and recovers with retry-only spend rather than a full re-run. To show the mechanics, this run packages a two-document pilot and hands it a Results file in which one answer is truncated prose:
bewley open-coding jobs --output pilot.jobs.ep --pilot 2 --model gpt-4.1-mini --force
Show command output
{
"schema_version": "2.0",
"status": "ok",
"command": "bewley open-coding jobs",
"argv": [
"bewley",
"open-coding",
"jobs",
"--output",
"pilot.jobs.ep",
"--pilot",
"2",
"--model",
"gpt-4.1-mini",
"--force"
],
"data": {
"object_type": "Jobs",
"output": "pilot.jobs.ep",
"question": "open_coding",
"codebook_release": null,
"scenario_count": 2,
"expected_model_calls": 2,
"pilot": true,
"from_failures": null,
"failed_documents": null,
"saved": {
"status": "ok",
"path": "pilot.jobs.ep",
"commit": "7666f828a22bc71cfec9657a7e24fe81057e6ca3",
"branch": "main",
"message": "Save Jobs"
},
"expected_results": "results.ep",
"models": {
"output": "models.ep",
"model": "gpt-4.1-mini",
"max_tokens": 4000
},
"inference": "external",
"answer_contract": {
"type": "json_array",
"item_required_keys": [
"code",
"description",
"quote"
],
"quote_policy": "exact_verbatim"
}
},
"warnings": [],
"errors": [],
"next_steps": [
{
"id": "run-open-coding-jobs",
"purpose": "Run the packaged jobs with the external ep CLI",
"command": [
"ep",
"run",
"pilot.jobs.ep",
"--model_list",
"models.ep",
"--output",
"results.ep"
],
"mutates_state": true,
"requires_network": true,
"requires_user_approval": true
}
]
}bewley open-coding ingest run1.results.ep --jobs pilot.jobs.ep --output retry-demo.csv
Show command output
{
"schema_version": "2.0",
"status": "error",
"command": "bewley open-coding ingest",
"argv": [
"bewley",
"open-coding",
"ingest",
"run1.results.ep",
"--jobs",
"pilot.jobs.ep",
"--output",
"retry-demo.csv"
],
"data": {},
"warnings": [],
"errors": [
{
"code": "INCOMPLETE_RESULTS",
"message": "Results failed validation; no candidate CSV was written.",
"context": {
"failures": [
{
"document_id": "888b7ecae251432cac96273b078fd295",
"model": "gpt-4.1-mini",
"error": "answer does not contain a JSON array"
}
],
"missing_answers": 0,
"duplicate_answers": 0
},
"hint": "Rebuild a retry package with `bewley open-coding jobs --from-failures run1.results.ep` and pass both results files to ingest, or use --allow-partial."
}
],
"next_steps": []
}No CSV was written; the error names the failing document and suggests the recovery. --from-failures repackages only the scenarios lacking a valid answer, using the originating Jobs as the denominator:
bewley open-coding jobs --from-failures run1.results.ep --jobs pilot.jobs.ep --output retry.jobs.ep --model gpt-4.1-mini --force
Show command output
{
"schema_version": "2.0",
"status": "ok",
"command": "bewley open-coding jobs",
"argv": [
"bewley",
"open-coding",
"jobs",
"--from-failures",
"run1.results.ep",
"--jobs",
"pilot.jobs.ep",
"--output",
"retry.jobs.ep",
"--model",
"gpt-4.1-mini",
"--force"
],
"data": {
"object_type": "Jobs",
"output": "retry.jobs.ep",
"question": "open_coding",
"codebook_release": null,
"scenario_count": 1,
"expected_model_calls": 1,
"pilot": false,
"from_failures": "run1.results.ep",
"failed_documents": 1,
"saved": {
"status": "ok",
"path": "retry.jobs.ep",
"commit": "7a3ef7e937781e41f54b2c5f9b78bf1db33af1e1",
"branch": "main",
"message": "Save Jobs"
},
"expected_results": "results.ep",
"models": {
"output": "models.ep",
"model": "gpt-4.1-mini",
"max_tokens": 4000
},
"inference": "external",
"answer_contract": {
"type": "json_array",
"item_required_keys": [
"code",
"description",
"quote"
],
"quote_policy": "exact_verbatim"
}
},
"warnings": [],
"errors": [],
"next_steps": [
{
"id": "run-open-coding-jobs",
"purpose": "Run the packaged jobs with the external ep CLI",
"command": [
"ep",
"run",
"retry.jobs.ep",
"--model_list",
"models.ep",
"--output",
"results.ep"
],
"mutates_state": true,
"requires_network": true,
"requires_user_approval": true
}
]
}After the retry runs externally, pass both Results files to ingest. Rows merge by stable (scenario, model) identity — first valid answer wins — and every retained candidate is attributed to the file that supplied it:
bewley open-coding ingest run1.results.ep run2.results.ep --jobs pilot.jobs.ep --output retry-demo.csv
Show command output
{
"schema_version": "2.0",
"status": "ok",
"command": "bewley open-coding ingest",
"argv": [
"bewley",
"open-coding",
"ingest",
"run1.results.ep",
"run2.results.ep",
"--jobs",
"pilot.jobs.ep",
"--output",
"retry-demo.csv"
],
"data": {
"object_type": "CandidateCodes",
"output": "retry-demo.csv",
"ingest_log": "ingest_log.jsonl",
"results": [
"run1.results.ep",
"run2.results.ep"
],
"result_count": 3,
"candidate_count": 2,
"scenario_count": 2,
"models": [
"gpt-4.1-mini"
],
"retained_by_source": {
"run1.results.ep": 1,
"run2.results.ep": 1
},
"expected_scenarios": 2,
"expected_answers": 2,
"missing_answers": 0,
"duplicate_scenarios": 0,
"superseded_answers": 0,
"failed_scenarios": 0,
"stale_scenarios": 0,
"unresolved_quotes": 0,
"unresolved_details": [],
"partial": false
},
"warnings": [],
"errors": [],
"next_steps": []
}retained_by_source shows one candidate from each file: the retry is separately attributable and never masquerades as a clean first run.
08 Refine the codebook
A first-pass codebook is never the final one: rereading tagged evidence changes your mind about the categories themselves, and the tool has to make changing your mind cheap and traceable. Suppose reading the delay excerpts suggests a distinction worth testing — waiting as its own idea, separate from slow information. Create the candidate code and tag Abigail's plea by hand. The least error-prone way to say where is to quote the passage itself: --quote matches the text verbatim or fails loudly (a mistyped word, a straightened curly quote — anything inexact is an error, never a wrong span), an ambiguous quote lists its occurrences for --occurrence N to pick from, and the envelope echoes exactly what was tagged so you can verify it. Line ranges (--lines 9:9, 1-based and inclusive) and byte spans remain available for coordinate-precise work:
bewley code create waiting_for_news \
--description "Waiting anxiously for letters that do not come"
bewley annotate apply waiting_for_news corpus/1775-may-04-abigail-adams.txt \
--quote "I want very much to hear from you, how you stood your journey, and in what state you find yourself now. I felt very anxious about you; though I endeavored to be very insensible and heroic, yet my heart felt like a heart of lead."
Show command output
{
"schema_version": "2.0",
"status": "ok",
"command": "bewley annotate apply",
"argv": [
"bewley",
"annotate",
"apply",
"waiting_for_news",
"corpus/1775-may-04-abigail-adams.txt",
"--quote",
"I want very much to hear from you, how you stood your journey, and in what state you find yourself now. I felt very anxious about you; though I endeavored to be very insensible and heroic, yet my heart felt like a heart of lead."
],
"data": {
"annotation_id": "965ea8d06d0c4ad2b53a91d5267fc4a2",
"scope_type": "span",
"start_line": 9,
"end_line": 9,
"speaker_scope": null,
"annotated_text": "I want very much to hear from you, how you stood your journey, and in what state you find yourself now. I felt very anxious about you; though I endeavored to be very insensible and heroic, yet my heart felt like a heart of lead."
},
"warnings": [],
"errors": [],
"next_steps": []
}Comparison shows the distinction never earns its keep: waiting is the delay theme. Merge it away — the target absorbs the source's evidence, while the absorbed annotation keeps its original code for provenance:
bewley code merge waiting_for_news --into information_and_delay
Show command output
{
"schema_version": "2.0",
"status": "ok",
"command": "bewley code merge",
"argv": [
"bewley",
"code",
"merge",
"waiting_for_news",
"--into",
"information_and_delay"
],
"data": {
"event_id": "1b6005bede4041c8a48f352c262a5c7b"
},
"warnings": [],
"errors": [],
"next_steps": []
}bewley code show information_and_delay
Show command output
{
"schema_version": "2.0",
"status": "ok",
"command": "bewley code show",
"argv": [
"bewley",
"code",
"show",
"information_and_delay"
],
"data": {
"code_id": "f28fa5aeda0441f98e2f5fe8861fc4b6",
"name": "information_and_delay",
"status": "active",
"description": "The struggle to get timely news and letters.",
"inclusion_criteria": null,
"exclusion_criteria": null,
"active_annotations": 3,
"aliases": [],
"absorbs": [
"waiting_for_news"
]
},
"warnings": [],
"errors": [],
"next_steps": []
}The count includes the absorbed annotation and absorbs names the merged source. Two more structuring moves as the account takes shape: parent-child grouping for "is a kind of" relations, and typed links for analytic claims like X is intensified by Y. Finally, choosing one core code declares what the whole account is organized around (grounded theorists call this the core category):
bewley code create home_front --description "The war as lived at home"
bewley code set-parent household_responsibility home_front
bewley code set-parent health_and_scarcity home_front
bewley code link separation_and_affection information_and_delay intensified_by \
--memo "Delayed letters repeatedly sharpen the pain of separation."
bewley code set-core separation_and_affection
bewley code list --tree
Show command output
{
"schema_version": "2.0",
"status": "ok",
"command": "bewley code list",
"argv": [
"bewley",
"code",
"list",
"--tree"
],
"data": [
{
"canonical_name": "home_front",
"code_id": "f2a0665b3c33452bbd4f421b6bf99f8f",
"description": "The war as lived at home",
"annotations": 0,
"children": [
{
"canonical_name": "health_and_scarcity",
"code_id": "a938067e0d9f4e0e81f14a0bf7507907",
"description": "Illness, inoculation, or shortages of goods and money.",
"annotations": 2
},
{
"canonical_name": "household_responsibility",
"code_id": "b5a2abb762a544ae87a7da5a98e3d9a7",
"description": "Managing the farm, prices, and family economy alone.",
"annotations": 1
}
]
},
{
"canonical_name": "information_and_delay",
"code_id": "f28fa5aeda0441f98e2f5fe8861fc4b6",
"description": "The struggle to get timely news and letters.",
"annotations": 2
},
{
"canonical_name": "political_voice",
"code_id": "aea0c42d21904efebaf5c6f4ac0dc652",
"description": "Claims to political standing or representation by or for those excluded from it.",
"annotations": 3
},
{
"canonical_name": "public_duty",
"code_id": "a166a1afa2ba4a2a971898d2a2ab7b90",
"description": "Framing congressional or military service as obligation to the country.",
"annotations": 2
},
{
"canonical_name": "separation_and_affection",
"code_id": "75085ac917754b4387201d51799a6f7a",
"description": "Longing, tenderness, and the cost of being apart.",
"annotations": 2
},
{
"canonical_name": "war_and_danger",
"code_id": "630c1a95e08f4587a562db72bd6eda99",
"description": "Direct experience or anticipation of military violence.",
"annotations": 2
}
],
"warnings": [],
"errors": [],
"next_steps": []
}$ bewley code list --tree --human
codebook
├── home_front
│ The war as lived at home
│ ├── health_and_scarcity (2 annotations)
│ │ Illness, inoculation, or shortages of goods and money.
│ └── household_responsibility (1 annotations)
│ Managing the farm, prices, and family economy alone.
├── information_and_delay (2 annotations)
│ The struggle to get timely news and letters.
├── political_voice (3 annotations)
│ Claims to political standing or representation by or for those excluded from it.
├── public_duty (2 annotations)
│ Framing congressional or military service as obligation to the country.
├── separation_and_affection (2 annotations)
│ Longing, tenderness, and the cost of being apart.
└── war_and_danger (2 annotations)
Direct experience or anticipation of military violence.home_front now parents the household and scarcity codes; every code keeps its definition and counts.coverage answers "how much of the corpus does this category actually touch" — with --breakdown so a parent's inclusive rollup cannot hide divergent children:
bewley code coverage home_front --breakdown
Show command output
{
"schema_version": "2.0",
"status": "ok",
"command": "bewley code coverage",
"argv": [
"bewley",
"code",
"coverage",
"home_front",
"--breakdown"
],
"data": {
"code": "home_front",
"total_respondents": 20,
"direct": 0,
"inclusive": 3,
"descendants": [
"health_and_scarcity",
"household_responsibility"
],
"breakdown": [
{
"code": "health_and_scarcity",
"is_target": false,
"respondents": 2
},
{
"code": "household_responsibility",
"is_target": false,
"respondents": 1
},
{
"code": "home_front",
"is_target": true,
"respondents": 0
}
]
},
"warnings": [],
"errors": [],
"next_steps": []
}From labels to a codebook others could use
A one-sentence definition is enough for the coder who invented the code, and not enough for anyone else — a second coder, a future model run, or you in six months. Structured criteria say when the code applies and when it does not, including what to use instead at the boundary:
bewley code update political_voice \
--inclusion "A claim about who is entitled to speak, decide, or be represented, made in the writer's own voice." \
--exclusion "War or government news without a claim about entitlement to a voice; use public_duty for the duty itself."
Show command output
{
"schema_version": "2.0",
"status": "ok",
"command": "bewley code update",
"argv": [
"bewley",
"code",
"update",
"political_voice",
"--inclusion",
"A claim about who is entitled to speak, decide, or be represented, made in the writer's own voice.",
"--exclusion",
"War or government news without a claim about entitlement to a voice; use public_duty for the duty itself."
],
"data": {
"code_id": "aea0c42d21904efebaf5c6f4ac0dc652",
"name": "political_voice",
"status": "active",
"description": "Claims to political standing or representation by or for those excluded from it.",
"inclusion_criteria": "A claim about who is entitled to speak, decide, or be represented, made in the writer's own voice.",
"exclusion_criteria": "War or government news without a claim about entitlement to a voice; use public_duty for the duty itself.",
"active_annotations": 3,
"aliases": []
},
"warnings": [],
"errors": [],
"next_steps": []
}code lint reads the codebook the way a skeptical colleague would — and reports the honest gap between the one code that now has criteria and the six that do not. It flags; it never fixes:
$ bewley code lint --human
6 codebook findings
┏━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃ code ┃ check ┃ detail ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
│ health_and_scarcity │ missing_criteria │ 2 annotation(s) but no inclusion criteria │
│ │ │ recorded. │
│ household_responsibility │ missing_criteria │ 1 annotation(s) but no inclusion criteria │
│ │ │ recorded. │
│ information_and_delay │ missing_criteria │ 2 annotation(s) but no inclusion criteria │
│ │ │ recorded. │
│ public_duty │ missing_criteria │ 2 annotation(s) but no inclusion criteria │
│ │ │ recorded. │
│ separation_and_affection │ missing_criteria │ 2 annotation(s) but no inclusion criteria │
│ │ │ recorded. │
│ war_and_danger │ missing_criteria │ 2 annotation(s) but no inclusion criteria │
│ │ │ recorded. │
└──────────────────────────┴──────────────────┴────────────────────────────────────────────────┘Release the refined codebook and diff it against first-pass from chapter 6 — the refinement chapter, summarized exactly: one code added, two re-parented, one given criteria:
bewley codebook release refined
Show command output
{
"schema_version": "2.0",
"status": "ok",
"command": "bewley codebook release",
"argv": [
"bewley",
"codebook",
"release",
"refined"
],
"data": {
"name": "refined",
"release_id": "38ee0b2c58964e0cb0ab8802d5ee816a",
"codes": 8
},
"warnings": [],
"errors": [],
"next_steps": []
}bewley codebook diff first-pass refined
Show command output
{
"schema_version": "2.0",
"status": "ok",
"command": "bewley codebook diff",
"argv": [
"bewley",
"codebook",
"diff",
"first-pass",
"refined"
],
"data": {
"from": "first-pass",
"to": "refined",
"added": [
"home_front"
],
"removed": [],
"changed": [
{
"code_name": "health_and_scarcity",
"changes": {
"parent": {
"from": null,
"to": "home_front"
}
}
},
{
"code_name": "household_responsibility",
"changes": {
"parent": {
"from": null,
"to": "home_front"
}
}
},
{
"code_name": "political_voice",
"changes": {
"inclusion_criteria": {
"from": null,
"to": "A claim about who is entitled to speak, decide, or be represented, made in the writer's own voice."
},
"exclusion_criteria": {
"from": null,
"to": "War or government news without a claim about entitlement to a voice; use public_duty for the duty itself."
}
}
}
]
},
"warnings": [],
"errors": [],
"next_steps": []
}09 Compare across the corpus, then write it down
The analytic engine of qualitative work is comparison: set passages that share a tag beside each other, look at where two themes coincide and where one appears without the other, and let the differences sharpen the categories (the tradition calls this constant comparison). The cases from chapter 4 supply one axis of comparison — whose letters carry a theme. case show gathers a correspondent's side of the corpus in one place:
$ bewley case show 'Abigail Adams' --human
╭─────────────────────────────────────── Abigail Adams ────────────────────────────────────────╮
│ type person │
│ │
│ Attributes │
│ role: home-front │
│ │
│ Documents (10) │
│ author corpus/1775-august-10-abigail-adams.txt │
│ author corpus/1775-december-10-abigail-adams.txt │
│ author corpus/1775-june-15-abigail-adams.txt │
│ author corpus/1775-june-22-abigail-adams.txt │
│ author corpus/1775-may-04-abigail-adams.txt │
│ author corpus/1775-october-21-abigail-adams.txt │
│ author corpus/1776-july-13-abigail-adams.txt │
│ author corpus/1776-june-03-abigail-adams.txt │
│ author corpus/1776-march-02-abigail-adams.txt │
│ author corpus/1776-march-31-abigail-adams.txt │
│ │
╰──────────────────────────────────────────────────────────────────────────────────────────────╯Boolean queries retrieve evidence across the whole corpus. Both keyword (AND OR NOT) and symbolic (& | !) operators work; quote the expression so the shell does not interpret them. Document mode asks which letters contain both themes anywhere:
bewley query "public_duty & separation_and_affection"
Show command output
{
"schema_version": "2.0",
"status": "ok",
"command": "bewley query",
"argv": [
"bewley",
"query",
"public_duty & separation_and_affection"
],
"data": [
{
"document_id": "d87f4b0e197943ff8b82716c24381bae",
"current_path": "corpus/1775-september-17-john-adams.txt"
}
],
"warnings": [],
"errors": [],
"next_steps": []
}Annotation mode is stricter: terms must be satisfied by overlapping spans. The 4 May 1775 letter matches because the passage quoted for waiting_for_news overlaps the heart of lead sentence — note the absorbed annotation still reports its original code name:
bewley query "information_and_delay & separation_and_affection" --mode annotation
Show command output
{
"schema_version": "2.0",
"status": "ok",
"command": "bewley query",
"argv": [
"bewley",
"query",
"information_and_delay & separation_and_affection",
"--mode",
"annotation"
],
"data": [
{
"annotation_id": "965ea8d06d0c4ad2b53a91d5267fc4a2",
"canonical_name": "waiting_for_news",
"current_path": "corpus/1775-may-04-abigail-adams.txt",
"start_line": 9,
"end_line": 9,
"anchor_status": "clean",
"text": "I want very much to hear from you, how you stood your journey, and in what state you find yourself now. I felt very anxious about you; though I endeavored to be very insensible and heroic, yet my heart felt like a heart of lead."
},
{
"annotation_id": "010749199e3b4b37973eac6c4791ca28",
"canonical_name": "separation_and_affection",
"current_path": "corpus/1775-may-04-abigail-adams.txt",
"start_line": 9,
"end_line": 9,
"anchor_status": "clean",
"text": "I felt very anxious about you; though I endeavored to be very insensible and heroic, yet my heart felt like a heart of lead."
}
],
"warnings": [],
"errors": [],
"next_steps": []
}$ bewley query 'information_and_delay & separation_and_affection' --mode annotation --human
2 matching annotation(s)
╭────────────────────────────────────── waiting_for_news ──────────────────────────────────────╮
│ I want very much to hear from you, how you stood your journey, and in what state you find │
│ yourself now. I felt very anxious about you; though I endeavored to be very insensible and │
│ heroic, yet my heart felt like a heart of lead. │
╰────────────────────── corpus/1775-may-04-abigail-adams.txt · lines 9–9 ──────────────────────╯
╭────────────────────────────────── separation_and_affection ──────────────────────────────────╮
│ I felt very anxious about you; though I endeavored to be very insensible and heroic, yet my │
│ heart felt like a heart of lead. │
╰────────────────────── corpus/1775-may-04-abigail-adams.txt · lines 9–9 ──────────────────────╯Queries return evidence, not conclusions. The interpretation belongs in a memo:
bewley memo add --code separation_and_affection \
"Affection and complaint travel together: nearly every tender passage sits beside a demand for more letters."
Show command output
{
"schema_version": "2.0",
"status": "ok",
"command": "bewley memo add",
"argv": [
"bewley",
"memo",
"add",
"--code",
"separation_and_affection",
"Affection and complaint travel together: nearly every tender passage sits beside a demand for more letters."
],
"data": {
"memo_id": "32c0061c9d7e41668173e35a3eb28fb2"
},
"warnings": [],
"errors": [],
"next_steps": []
}10 Export evidence and theory
Exports are regenerated views of the ledger. Quote tables carry document, revision, byte range, and exact text, so downstream reports can verify every excerpt:
bewley export quotes --code political_voice --format jsonl
Show command output
{
"schema_version": "2.0",
"status": "ok",
"command": "bewley export quotes",
"argv": [
"bewley",
"export",
"quotes",
"--code",
"political_voice",
"--format",
"jsonl"
],
"data": [
{
"code_name": "political_voice",
"code_id": "aea0c42d21904efebaf5c6f4ac0dc652",
"document_id": "78c15de0775b4c3a866fb99400cf1ee2",
"document_path": "corpus/1775-december-10-abigail-adams.txt",
"revision_id": "740108283c364427bf13e09d06643520",
"annotation_id": "9f5d3d2765b54b5499d945b5e9fed54e",
"start_byte": 5290,
"end_byte": 5405,
"start_line": 19,
"end_line": 19,
"exact_text": "But I cannot conclude without telling you we are all very angry with your House of Assembly for their instructions.",
"anchor_status": "clean",
"source_lineage": {
"document_id": "78c15de0775b4c3a866fb99400cf1ee2",
"revision_id": "740108283c364427bf13e09d06643520",
"lineage": [],
"metadata": [],
"lineage_status": "unrecorded"
}
}
… 2 further entries elided …
],
"warnings": [],
"errors": [],
"next_steps": []
}bewley export html --output adams-report.html --title "Adams letters — coded corpus"
Show command output
{
"schema_version": "2.0",
"status": "ok",
"command": "bewley export html",
"argv": [
"bewley",
"export",
"html",
"--output",
"adams-report.html",
"--title",
"Adams letters — coded corpus"
],
"data": {
"output_path": "adams-report.html"
},
"warnings": [],
"errors": [],
"next_steps": []
}That command produced the explorer linked from the header — search and filter every coded excerpt in this run. Theory structure exports as JSON or a Mermaid diagram, and bewley codegen theory-explorer generates an interactive D3 renderer:
bewley export theory --format mermaid --output theory.mmd
Show command output
{
"schema_version": "2.0",
"status": "ok",
"command": "bewley export theory",
"argv": [
"bewley",
"export",
"theory",
"--format",
"mermaid",
"--output",
"theory.mmd"
],
"data": {
"output_path": "theory.mmd"
},
"warnings": [],
"errors": [],
"next_steps": []
}bewley export plots --output-dir plots
Show command output
{
"schema_version": "2.0",
"status": "ok",
"command": "bewley export plots",
"argv": [
"bewley",
"export",
"plots",
"--output-dir",
"plots"
],
"data": {
"output_dir": "plots",
"plots": {
"code_prevalence": "plots/code-prevalence.svg",
"document_density": "plots/document-density.svg",
"code_cooccurrence": "plots/code-cooccurrence.svg",
"code_document_matrix": "plots/code-document-matrix.svg",
"code_discovery": "plots/code-discovery.svg",
"review_funnel": "plots/review-funnel.svg",
"annotation_positions": "plots/annotation-positions.svg",
"codebook_evolution": "plots/codebook-evolution.svg"
},
"manifest": "plots/plots.json",
"code_count": 8,
"document_count": 20
},
"warnings": [],
"errors": [],
"next_steps": []
}Descriptive plots, not findings
Plots audit the shape of the coding work — which codes dominate, which documents received disproportionate attention, which codes co-occur. They are prompts to return to the evidence, not statistical tests. These are the plots from this run:
daily_minutiae, travel_logistics, weather_report — are the proposals rejected in chapter 6, kept visible instead of vanishing.waiting_for_news, and a flat tail means recent material introduced no new codes. Whether that amounts to saturation is a methodological judgment about the sample, the questions, and conceptual depth; the plot informs that judgment but cannot make it.waiting_for_news into information_and_delay.Before reporting a finding, read the export and confirm its excerpts are non-empty, correctly scoped, and representative. A generated narrative is an inventory to interpret, not a substitute for reading the evidence.
11 Interviews and speakers
Everything so far treated a document as one voice. Interviews are not: part of the text is the interviewer and part is the response, and coding the interviewer's question as if the respondent said it is the classic transcript error — a theme gets attributed to a participant that was actually introduced by the question's wording. Bewley's answer is to make the document's voices explicit and then let the annotation machinery respect them.
FICTIONAL in every file header. The exchanges never took place; their themes echo the genuine letters so the codes carry over.Fetch it and set up a separate small project (same commands as chapter 4):
bewley example fetch adams-interviews
Show command output
{
"schema_version": "2.0",
"status": "ok",
"command": "bewley example fetch",
"argv": [
"bewley",
"example",
"fetch",
"adams-interviews"
],
"data": {
"example": "adams-interviews",
"dest": "adams-interviews",
"documents": 3,
"files_written": [
"README.md",
"corpus/interview-01-abigail-adams.txt",
"corpus/interview-02-john-adams.txt",
"corpus/interview-03-joint.txt"
]
},
"warnings": [],
"errors": [],
"next_steps": [
{
"id": "init-project",
"purpose": "Initialize the project inside adams-interviews/ (cd there first).",
"command": [
"bewley",
"init"
],
"mutates_state": true,
"requires_network": false,
"requires_user_approval": false
}
]
}cd adams-interviews && bewley init && bewley add corpus/interview-01-abigail-adams.txt # …and the other two
Segmentation is an explicit, recorded step — nothing is inferred silently. The default rule matches ALL-CAPS labels at line starts (INTERVIEWER:, ABIGAIL ADAMS:), so mixed-case header lines like Title: never become speakers; for transcripts with mixed-case labels, pass each one with --label:
bewley speakers detect corpus/interview-01-abigail-adams.txt
Show command output
{
"schema_version": "2.0",
"status": "ok",
"command": "bewley speakers detect",
"argv": [
"bewley",
"speakers",
"detect",
"corpus/interview-01-abigail-adams.txt"
],
"data": {
"document_id": "5293cd77863a46c090d3fc7561c3456a",
"revision_id": "a77459555f424887b4cf1bb9d62bbe9b",
"rule": "caps-labels",
"turn_count": 8,
"labels": [
"ABIGAIL ADAMS",
"INTERVIEWER"
]
},
"warnings": [],
"errors": [],
"next_steps": [
{
"id": "assign-roles",
"purpose": "Assign each detected label a role so coding can respect the interviewer boundary.",
"command": [
"bewley",
"speakers",
"set-role",
"--help"
],
"mutates_state": false,
"requires_network": false,
"requires_user_approval": false,
"reason": "Supply the required values using this template: bewley speakers set-role '<label>' '<interviewer|participant|other>'"
}
]
}Detected labels are only labels; whether a voice should be coded is a judgment, so it is a separate recorded decision. Until roles are assigned, bewley next treats the gap as the top unresolved state:
bewley next
Show command output
{
"schema_version": "2.0",
"status": "ok",
"command": "bewley next",
"argv": [
"bewley",
"next"
],
"data": {
"schema_version": "1.0",
"ready": true,
"stage": "open_coding",
"phase": "open_coding",
"project_exists": true,
"counts": {
"documents": 3,
"codes": 0,
"active_annotations": 0
},
"study": {
"method": null,
"unit_of_analysis": null,
"research_questions": 0,
"cases": 0,
"unlinked_documents": 3,
"unassigned_speaker_labels": [
"ABIGAIL ADAMS",
"INTERVIEWER",
"JOHN ADAMS"
]
},
"checklist": [
"Read all documents and write qualitative-analysis/corpus_summary.md.",
"Run `bewley open-coding jobs --output jobs.ep --model <model-name>` to package the corpus.",
"Run `ep run jobs.ep --model_list models.ep --output results.ep` (external; requires approval).",
"Run `bewley open-coding ingest results.ep --jobs jobs.ep`.",
"Review the queue: `bewley open-coding candidates`, then record decisions with `bewley open-coding review`."
],
"primary_doc": "workflow",
"blockers": []
},
"warnings": [],
"errors": [],
"next_steps": [
{
"id": "speakers-set-role",
"purpose": "Assign roles to detected speaker labels (ABIGAIL ADAMS, INTERVIEWER, JOHN ADAMS)",
"command": [
"bewley",
"speakers",
"set-role",
"--help"
],
"mutates_state": false,
"requires_network": false,
"requires_user_approval": false,
"reason": "Supply the required values using this template: bewley speakers set-role '<label>' '<interviewer|participant|other>'"
}
]
}bewley speakers set-role INTERVIEWER interviewer
Show command output
{
"schema_version": "2.0",
"status": "ok",
"command": "bewley speakers set-role",
"argv": [
"bewley",
"speakers",
"set-role",
"INTERVIEWER",
"interviewer"
],
"data": {
"label": "INTERVIEWER",
"role": "interviewer"
},
"warnings": [],
"errors": [],
"next_steps": []
}The participants get participant the same way. A speaker is scoped to its document — the same person can appear in many transcripts — and whose voice it is links to the cases from chapter 4's machinery:
bewley speakers link-case corpus/interview-03-joint.txt "ABIGAIL ADAMS" "Abigail Adams"
Show command output
{
"schema_version": "2.0",
"status": "ok",
"command": "bewley speakers link-case",
"argv": [
"bewley",
"speakers",
"link-case",
"corpus/interview-03-joint.txt",
"ABIGAIL ADAMS",
"Abigail Adams"
],
"data": {
"link_id": "8941570d9aa3466a8e4531d90e015f16"
},
"warnings": [],
"errors": [],
"next_steps": []
}$ bewley speakers list corpus/interview-03-joint.txt --human
corpus/interview-03-joint.txt — 10 turns
┏━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━┳━━━━━━━┳━━━━━━━┳━━━━━━━━━━━━━━━┓
┃ label ┃ role ┃ turns ┃ share ┃ case ┃
┡━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━╇━━━━━━━╇━━━━━━━╇━━━━━━━━━━━━━━━┩
│ ABIGAIL ADAMS │ participant │ 4 │ 42% │ Abigail Adams │
│ JOHN ADAMS │ participant │ 3 │ 40% │ John Adams │
│ INTERVIEWER │ interviewer │ 3 │ 18% │ - │
└───────────────┴─────────────┴───────┴───────┴───────────────┘Now the boundary does real work. Try to code a phrase that appears only in the interviewer's question — the span resolves, but bewley refuses it, because every span in a segmented document carries a speaker_scope:
bewley annotate apply political_voice corpus/interview-01-abigail-adams.txt --quote 'you asked him to "remember the ladies"'
Show command output
{
"schema_version": "2.0",
"status": "error",
"command": "bewley annotate apply",
"argv": [
"bewley",
"annotate",
"apply",
"political_voice",
"corpus/interview-01-abigail-adams.txt",
"--quote",
"you asked him to \"remember the ladies\""
],
"data": {},
"warnings": [],
"errors": [
{
"code": "INTERVIEWER_TEXT",
"message": "span lies entirely within interviewer turns",
"context": {
"speaker_scope": "interviewer"
},
"hint": "Codes normally anchor in participant answers, not the questions. Pass --allow-interviewer to annotate interviewer text deliberately."
}
],
"next_steps": []
}(--allow-interviewer exists for the deliberate case.) Code the participant's answer instead and the envelope records that the evidence is participant speech:
bewley annotate apply political_voice corpus/interview-01-abigail-adams.txt --quote "I expected exactly what I received"
Show command output
{
"schema_version": "2.0",
"status": "ok",
"command": "bewley annotate apply",
"argv": [
"bewley",
"annotate",
"apply",
"political_voice",
"corpus/interview-01-abigail-adams.txt",
"--quote",
"I expected exactly what I received"
],
"data": {
"annotation_id": "259760c1cc9a476baee00867717fcb80",
"scope_type": "span",
"start_line": 34,
"end_line": 34,
"speaker_scope": "participant",
"annotated_text": "I expected exactly what I received"
},
"warnings": [],
"errors": [],
"next_steps": []
}For interviews, the most natural unit is often the whole turn — robust to re-wrapping, no quote needed. --turn anchors the Nth turn of the segmentation:
bewley annotate apply war_and_danger corpus/interview-01-abigail-adams.txt --turn 8
Show command output
{
"schema_version": "2.0",
"status": "ok",
"command": "bewley annotate apply",
"argv": [
"bewley",
"annotate",
"apply",
"war_and_danger",
"corpus/interview-01-abigail-adams.txt",
"--turn",
"8"
],
"data": {
"annotation_id": "cbcaf03fa3524fa3b44d214132c618ba",
"scope_type": "span",
"start_line": 43,
"end_line": 46,
"speaker_scope": "participant",
"annotated_text": "ABIGAIL ADAMS: We lived in continual expectation of alarms. I heard the\ncannon at Boston from my own hill. Fear was a boarder in every house on\nthat coast; the question was only whether you let it govern the\nhousehold. I did not."
},
"warnings": [],
"errors": [],
"next_steps": []
}A span that crosses an interviewer question and a participant answer is legitimate sometimes (coding an exchange); it is recorded as speaker_scope: mixed with a warning rather than refused. After bewley update changes a transcript, rerun speakers detect — segmentation is tied to the exact revision it parsed.
The model pipeline respects the same boundary
Model-assisted open coding (chapter 5) works unchanged on transcripts — the packaged prompt additionally instructs the model to anchor quotes in participant turns — but instructions are not enforcement. Enforcement happens at ingest: a proposed quote that resolves inside interviewer turns is located, then marked interviewer_text — itemized like an unresolved quote, visible in the review queue, and skipped by apply. Never silently dropped, never silently applied:
bewley open-coding jobs --output jobs.ep --model gpt-4.1-mini
Show command output
{
"schema_version": "2.0",
"status": "ok",
"command": "bewley open-coding jobs",
"argv": [
"bewley",
"open-coding",
"jobs",
"--output",
"jobs.ep",
"--model",
"gpt-4.1-mini"
],
"data": {
"object_type": "Jobs",
"output": "jobs.ep",
"question": "open_coding",
"codebook_release": null,
"scenario_count": 3,
"expected_model_calls": 3,
"pilot": false,
"from_failures": null,
"failed_documents": null,
"saved": {
"status": "ok",
"path": "jobs.ep",
"commit": "3c7564f73344f7fb730ec38b8176bf553adeb635",
"branch": "main",
"message": "Save Jobs"
},
"expected_results": "results.ep",
"models": {
"output": "models.ep",
"model": "gpt-4.1-mini",
"max_tokens": 4000
},
"inference": "external",
"answer_contract": {
"type": "json_array",
"item_required_keys": [
"code",
"description",
"quote"
],
"quote_policy": "exact_verbatim"
}
},
"warnings": [],
"errors": [],
"next_steps": [
{
"id": "run-open-coding-jobs",
"purpose": "Run the packaged jobs with the external ep CLI",
"command": [
"ep",
"run",
"jobs.ep",
"--model_list",
"models.ep",
"--output",
"results.ep"
],
"mutates_state": true,
"requires_network": true,
"requires_user_approval": true
}
]
}results.ep here is a deterministic fixture: four plausible candidates across the three transcripts, one of which — deliberately — quotes the interviewer's question "Who had the harder years?".bewley open-coding ingest results.ep --jobs jobs.ep
Show command output
{
"schema_version": "2.0",
"status": "ok",
"command": "bewley open-coding ingest",
"argv": [
"bewley",
"open-coding",
"ingest",
"results.ep",
"--jobs",
"jobs.ep"
],
"data": {
"object_type": "CandidateCodes",
"output": "qualitative-analysis/candidate_codes.csv",
"ingest_log": "qualitative-analysis/ingest_log.jsonl",
"results": [
"results.ep"
],
"result_count": 3,
"candidate_count": 4,
"scenario_count": 3,
"models": [
"gpt-4.1-mini"
],
"retained_by_source": {
"results.ep": 3
},
"expected_scenarios": 3,
"expected_answers": 3,
"missing_answers": 0,
"duplicate_scenarios": 0,
"superseded_answers": 0,
"failed_scenarios": 0,
"stale_scenarios": 0,
"unresolved_quotes": 1,
"unresolved_details": [
{
"candidate_id": "567d3703b327147f",
"code_name": "shared_hardship",
"resolve_status": "interviewer_text",
"document_path": "corpus/interview-03-joint.txt",
"quote_prefix": "Who had the harder years, 1775 and\n1776?"
}
],
"partial": false
},
"warnings": [],
"errors": [],
"next_steps": []
}Three participant-anchored candidates resolved exactly; the interviewer-anchored one is itemized in unresolved_details with resolve_status: interviewer_text. The review queue shows all four, so the human review sees exactly what the model tried:
$ bewley open-coding candidates --human
4 candidates across 4 proposed codes
┏━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━┓
┃ Code ┃ Description ┃ Quote ┃ Document ┃ Decision ┃
┡━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━┩
│ marriage_by_post │ Sustaining a │ It taught us to │ interview-02-john │ - │
│ │ relationship │ conduct a │ │ │
│ │ through │ marriage by │ │ │
│ │ correspondence │ post, which is │ │ │
│ │ under delay. │ a thing │ │ │
│ shared_hardship │ Comparing │ Who had the │ interview-03-joint.txt │ - │
│ │ whose wartime │ harder years, │ │ │
│ │ years were │ 1775 and │ │ │
│ │ harder. │ 1776? │ │ │
│ unchosen_duties │ Responsibiliti │ There was very │ interview-01-abigail │ - │
│ │ es that │ little that was │ │ │
│ │ arrived with │ ordinary about │ │ │
│ │ the war rather │ it. │ │ │
│ │ than by │ │ │ │
│ │ choice. │ │ │ │
│ waiting_as_occupation │ Waiting for │ Waiting for │ interview-03-joint.txt │ - │
│ │ news framed as │ news is its own │ │ │
│ │ unpaid, │ occupation. │ │ │
│ │ invisible │ │ │ │
│ │ work. │ │ │ │
└───────────────────────┴────────────────┴─────────────────┴────────────────────────┴──────────┘shared_hardship is anchored in the interviewer's question — located, flagged, and excluded from apply unless a human re-anchors it in participant speech.12 Integrity and recovery
bewley fsck
Show command output
{
"schema_version": "2.0",
"status": "ok",
"command": "bewley fsck",
"argv": [
"bewley",
"fsck"
],
"data": {
"status": "ok"
},
"warnings": [],
"errors": [],
"next_steps": []
}bewley history
Show command output
{
"schema_version": "2.0",
"status": "ok",
"command": "bewley history",
"argv": [
"bewley",
"history"
],
"data": [
{
"sequence_number": 1,
"timestamp": "2026-09-08T14:31:06Z",
"event_type": "project_initialized",
"event_id": "8f8a43aeca084b77a2853a54a8d70a88"
},
{
"sequence_number": 2,
"timestamp": "2026-09-08T14:31:06Z",
"event_type": "study_configured",
"event_id": "b49c5585b7cf4d6897459fc2121139d2"
},
{
"sequence_number": 3,
"timestamp": "2026-09-08T14:31:06Z",
"event_type": "research_question_added",
"event_id": "744ea44fa1fc44c78c634b62f03df2f6"
},
{
"sequence_number": 4,
"timestamp": "2026-09-08T14:31:07Z",
"event_type": "document_added",
"event_id": "d2a08a818a334c6d92019fcc114e20f9"
},
{
"sequence_number": 5,
"timestamp": "2026-09-08T14:31:07Z",
"event_type": "document_added",
"event_id": "92b6eb88163d478ea4ae09f641aa34ba"
},
{
"sequence_number": 6,
"timestamp": "2026-09-08T14:31:07Z",
"event_type": "document_added",
"event_id": "972361f1d8ae4b6a9ef1cf7af8eb5e74"
}
… 104 further entries elided …
],
"warnings": [],
"errors": [],
"next_steps": []
}Undo appends a compensating event (bewley undo <event_id>); nothing is deleted. If the SQLite index is ever damaged, bewley rebuild-index reconstructs it from the event log alone. With annotations in place, next now recommends the analysis phase:
bewley next
Show command output
{
"schema_version": "2.0",
"status": "ok",
"command": "bewley next",
"argv": [
"bewley",
"next"
],
"data": {
"schema_version": "1.0",
"ready": true,
"stage": "candidates-awaiting-review",
"undecided_count": 2,
"artifacts": {
"candidates": "retry-demo.csv"
},
"blockers": []
},
"warnings": [],
"errors": [],
"next_steps": [
{
"id": "review-candidates",
"purpose": "Inspect undecided candidate evidence",
"command": [
"bewley",
"open-coding",
"candidates",
"--input",
"retry-demo.csv"
],
"mutates_state": false,
"requires_network": false,
"requires_user_approval": false
}
]
}Run fsck before handing artifacts downstream. If a source document changes, review conflicted annotations against the new revision rather than silently accepting shifted text.
13 Command map
| Need | Command family |
|---|---|
| Discover the workflow | version, guide, next, capabilities |
| Try it on bundled data | example list, example fetch |
| Declare the study | study set, study show, question add, question list |
| Say who the data is about | case create|list|show|set|link, attribute define|list, link add|list|remove |
| Say who is speaking | speakers detect|list|set-role|link-case, annotate apply --turn |
| Import or revise sources | add, add-audio, add-video, update, list documents |
| Run model-assisted open coding | open-coding jobs, external ep run, open-coding ingest, open-coding apply |
| Recover a failed run | open-coding jobs --from-failures, multi-file open-coding ingest |
| Build the codebook | code create|update|rename|merge|split|set-parent|link|set-core, code lint, codebook release|diff |
| Attach and inspect evidence | annotate apply|show|remove|resolve, show snippets |
| Compare cases | query, code coverage |
| Record interpretation | memo add|list|show|edit |
| Publish artifacts | export quotes|html|plots|theory|narrative, codegen theory-explorer |
| Verify and recover | fsck, rebuild-index, history, undo |
Exact options and defaults belong to bewley <command> --help; the embedded reference is bewley docs show commands. add-audio/add-video call the OpenAI transcription API directly — external paid calls whose envelopes carry a cost warning.
14 Agent-facing JSON contract
Bewley is designed to be driven by agents. Every command emits exactly one versioned JSON envelope by default. Branch on status; do not infer success from the shape of data.
{
"schema_version": "2.0",
"status": "ok",
"command": "bewley status",
"argv": ["bewley", "status"],
"data": {"documents": 20, "codes": 8},
"warnings": [],
"errors": [],
"next_steps": []
}
Failures set status to "error", populate errors with structured objects — ambiguous references list their candidate matches — and exit nonzero. Suggested actions use argv arrays and declare whether they mutate state, require network access, or require user approval.
bewley guide # the lifecycle and the ep-run execution boundary
bewley next # the single highest-priority next action, from artifact state
bewley capabilities # the versioned contract and bundled JSON Schemas
bewley agent schema envelope
--human or -H only for terminal presentation. Agents should consume the default envelope, and the repository's AGENTS.md is the operating contract.