@ishlabs/cli 0.17.6 → 0.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +54 -54
- package/dist/commands/ask.d.ts +4 -4
- package/dist/commands/ask.js +66 -66
- package/dist/commands/chat.js +10 -10
- package/dist/commands/config.js +1 -1
- package/dist/commands/docs.js +1 -1
- package/dist/commands/iteration.js +57 -57
- package/dist/commands/mcp.d.ts +23 -0
- package/dist/commands/mcp.js +676 -0
- package/dist/commands/person.d.ts +5 -0
- package/dist/commands/{profile.js → person.js} +197 -162
- package/dist/commands/source.d.ts +6 -2
- package/dist/commands/source.js +35 -30
- package/dist/commands/study-analyze.d.ts +1 -1
- package/dist/commands/study-analyze.js +3 -3
- package/dist/commands/study-participant.d.ts +8 -0
- package/dist/commands/{study-tester.js → study-participant.js} +50 -50
- package/dist/commands/study-run.d.ts +6 -6
- package/dist/commands/study-run.js +295 -271
- package/dist/commands/study.js +89 -66
- package/dist/commands/workspace.js +13 -13
- package/dist/connect.js +5 -5
- package/dist/index.js +6 -4
- package/dist/lib/accessibility-profile.d.ts +1 -1
- package/dist/lib/accessibility-profile.js +1 -1
- package/dist/lib/alias-hydrate.js +4 -4
- package/dist/lib/alias-store.d.ts +5 -5
- package/dist/lib/alias-store.js +8 -8
- package/dist/lib/api-client.d.ts +1 -1
- package/dist/lib/api-client.js +1 -1
- package/dist/lib/billing.d.ts +11 -11
- package/dist/lib/billing.js +16 -16
- package/dist/lib/chat-endpoint-templates.js +1 -1
- package/dist/lib/command-helpers.d.ts +18 -18
- package/dist/lib/command-helpers.js +83 -53
- package/dist/lib/docs.js +560 -386
- package/dist/lib/enums.d.ts +2 -2
- package/dist/lib/enums.js +2 -2
- package/dist/lib/local-sim/browser.d.ts +1 -1
- package/dist/lib/local-sim/browser.js +1 -1
- package/dist/lib/local-sim/debug-report.d.ts +2 -2
- package/dist/lib/local-sim/debug-report.js +3 -3
- package/dist/lib/local-sim/loop.d.ts +5 -5
- package/dist/lib/local-sim/loop.js +38 -38
- package/dist/lib/local-sim/types.d.ts +12 -12
- package/dist/lib/mcp-clients.d.ts +51 -0
- package/dist/lib/mcp-clients.js +175 -0
- package/dist/lib/modality.d.ts +10 -10
- package/dist/lib/modality.js +46 -46
- package/dist/lib/observability.d.ts +11 -0
- package/dist/lib/observability.js +16 -3
- package/dist/lib/output.d.ts +13 -12
- package/dist/lib/output.js +244 -184
- package/dist/lib/profile-sources.d.ts +64 -16
- package/dist/lib/profile-sources.js +91 -30
- package/dist/lib/skill-content.js +215 -168
- package/dist/lib/study-events.d.ts +3 -3
- package/dist/lib/study-events.js +1 -1
- package/dist/lib/study-inputs.d.ts +11 -1
- package/dist/lib/study-inputs.js +68 -17
- package/dist/lib/types.d.ts +105 -34
- package/package.json +1 -1
- package/dist/commands/profile.d.ts +0 -5
- package/dist/commands/study-tester.d.ts +0 -8
package/dist/lib/docs.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
const OVERVIEW = `# ish — overview for agents
|
|
9
9
|
|
|
10
|
-
ish is a CLI for running studies and asks against AI
|
|
10
|
+
ish is a CLI for running studies and asks against AI people.
|
|
11
11
|
The agent (you) is the primary user. Every command supports \`--json\`,
|
|
12
12
|
exits non-zero on failure, and resolves IDs from short aliases.
|
|
13
13
|
|
|
@@ -15,16 +15,16 @@ exits non-zero on failure, and resolves IDs from short aliases.
|
|
|
15
15
|
|
|
16
16
|
\`\`\`
|
|
17
17
|
Workspace (= product)
|
|
18
|
-
├──
|
|
18
|
+
├── People ────── reusable personas (alias: tp-…)
|
|
19
19
|
│ └── Sources ──────── transcripts/audio/images that seed generation
|
|
20
20
|
├── Study ──────────────── persistent research artifact (alias: s-…)
|
|
21
21
|
│ ├── modality ──────── interactive | text | video | audio | image | document | chat
|
|
22
|
-
│ ├── assignments ───── tasks the
|
|
23
|
-
│ ├── questionnaire ─── questions the
|
|
22
|
+
│ ├── assignments ───── tasks the participant does
|
|
23
|
+
│ ├── questionnaire ─── questions the participant answers
|
|
24
24
|
│ └── Iterations ────── one configured run (URL or content) (alias: i-…)
|
|
25
|
-
│ └──
|
|
25
|
+
│ └── Participants ─── instance of a Profile in this iteration (alias: t-…)
|
|
26
26
|
└── Ask ────────────────── lightweight reaction artifact (alias: a-…)
|
|
27
|
-
└── Rounds ────────── unit of execution;
|
|
27
|
+
└── Rounds ────────── unit of execution; participants fixed at ask creation
|
|
28
28
|
\`\`\`
|
|
29
29
|
|
|
30
30
|
Two top-level run verbs:
|
|
@@ -37,7 +37,7 @@ Two top-level run verbs:
|
|
|
37
37
|
2. Run \`ish docs get-page <slug>\` to read a specific page (e.g. \`concepts/study\`).
|
|
38
38
|
3. Run \`ish docs search <query>\` for keyword lookup across all pages.
|
|
39
39
|
4. Every command prints structured JSON when stdout is piped or \`--json\` is set.
|
|
40
|
-
5. Aliases like \`s-b2c\`, \`a-6ec\`, \`
|
|
40
|
+
5. Aliases like \`s-b2c\`, \`a-6ec\`, \`p-795\`, \`i-d4e\`, \`pt-a17\` are accepted
|
|
41
41
|
anywhere an ID is expected. See \`ish docs get-page reference/aliases\`.
|
|
42
42
|
|
|
43
43
|
## Where to look next
|
|
@@ -73,7 +73,7 @@ workspace.
|
|
|
73
73
|
A workspace carries:
|
|
74
74
|
- \`base_url\` — default origin used by site-access rules and study URLs.
|
|
75
75
|
- Site-access credentials (encrypted at rest) — see \`concepts/site-access\`.
|
|
76
|
-
-
|
|
76
|
+
- People + sources visible to every study/ask in the workspace.
|
|
77
77
|
|
|
78
78
|
## Selecting a workspace per command
|
|
79
79
|
|
|
@@ -113,15 +113,15 @@ ish workspace info --json
|
|
|
113
113
|
{
|
|
114
114
|
"studies_used": 2,
|
|
115
115
|
"studies_max": 3,
|
|
116
|
-
"
|
|
117
|
-
"
|
|
116
|
+
"participants_used": 0,
|
|
117
|
+
"participants_max": 3,
|
|
118
118
|
"tier": "free"
|
|
119
119
|
}
|
|
120
120
|
\`\`\`
|
|
121
121
|
|
|
122
122
|
A \`null\` value on a \`*_max\` field means "unlimited" (paid tiers).
|
|
123
123
|
Branch on \`studies_used >= studies_max\` before \`study create\`,
|
|
124
|
-
likewise for \`
|
|
124
|
+
likewise for \`participants_used\` before \`study run --sample\`.
|
|
125
125
|
|
|
126
126
|
## Cold start — \`workspace_create\` is not safe to call blind
|
|
127
127
|
|
|
@@ -138,13 +138,13 @@ Each row in the list response carries:
|
|
|
138
138
|
- \`last_activity_at\` — most recent run, iteration, ask, or write on
|
|
139
139
|
this workspace. Pick the most recently active workspace if you want
|
|
140
140
|
one the user is likely already thinking about.
|
|
141
|
-
- \`child_counts\` — \`{ studies, asks,
|
|
141
|
+
- \`child_counts\` — \`{ studies, asks, persons }\`. Zero across
|
|
142
142
|
the board = a quiet/empty workspace, safe to reuse without
|
|
143
143
|
cluttering anyone's view.
|
|
144
144
|
- \`has_headroom\` — \`true\` if the workspace is below
|
|
145
145
|
\`maxStudiesPerProduct\`, \`maxIterationsPerStudy\`, and
|
|
146
|
-
\`
|
|
147
|
-
before \`study create\` / \`
|
|
146
|
+
\`maxCustomPersons\` for the caller's tier. Branch on this
|
|
147
|
+
before \`study create\` / \`person generate\` — \`false\` here will be
|
|
148
148
|
\`usage_limit_reached\` on the next call.
|
|
149
149
|
|
|
150
150
|
For the idempotent create-or-reuse-by-name path, use
|
|
@@ -167,14 +167,14 @@ transcript) lives at \`guides/cold-start\`.
|
|
|
167
167
|
const CONCEPT_STUDY = `# concept: study
|
|
168
168
|
|
|
169
169
|
A **study** is the persistent research artifact. It defines:
|
|
170
|
-
- \`modality\`: \`interactive\` (the
|
|
170
|
+
- \`modality\`: \`interactive\` (the participant drives a real browser), one of
|
|
171
171
|
\`text | video | audio | image | document\` (media reaction studies),
|
|
172
172
|
or \`chat\` (multi-turn conversation — either with an external chatbot
|
|
173
|
-
endpoint or between two AI personas via
|
|
173
|
+
endpoint or between two AI personas via participant_pair mode).
|
|
174
174
|
- \`content_type\` (media studies only): \`email | social_post | ad | …\` —
|
|
175
|
-
controls the framing the
|
|
176
|
-
- \`assignments\`: the tasks the
|
|
177
|
-
- \`questionnaire\`: the questions the
|
|
175
|
+
controls the framing the participant is given.
|
|
176
|
+
- \`assignments\`: the tasks the participant performs. See \`concepts/assignment\`.
|
|
177
|
+
- \`questionnaire\`: the questions the participant answers. See \`concepts/questionnaire\`.
|
|
178
178
|
|
|
179
179
|
A study does **not** carry the URL or media being tested — that lives on
|
|
180
180
|
its iterations. Think: a study is the recipe; an iteration is one batch.
|
|
@@ -204,7 +204,7 @@ test artifact and don't need to A/B iterations:
|
|
|
204
204
|
| \`video\` | \`--content-url <url>\` |
|
|
205
205
|
| \`audio\` | \`--content-url <url>\` |
|
|
206
206
|
| \`document\` | \`--content-url <url>\` |
|
|
207
|
-
| \`chat\` | \`--endpoint <id>\` or \`--endpoint-config <file>\` (external_chatbot mode), or \`--chat-mode
|
|
207
|
+
| \`chat\` | \`--endpoint <id>\` or \`--endpoint-config <file>\` (external_chatbot mode), or \`--chat-mode participant_pair --group-a/-b --scenario-a/-b\` (two-AI rehearsal) |
|
|
208
208
|
|
|
209
209
|
\`\`\`
|
|
210
210
|
# Text — single email artifact:
|
|
@@ -256,8 +256,8 @@ Every study response carries two status-shaped fields:
|
|
|
256
256
|
|
|
257
257
|
- \`status\` — the raw lifecycle column on the row, values
|
|
258
258
|
\`draft | running | completed | cancelled\`. Updated lazily; can
|
|
259
|
-
disagree with what the
|
|
260
|
-
- \`runtime_status\` — derived by aggregating the iteration
|
|
259
|
+
disagree with what the participants actually did.
|
|
260
|
+
- \`runtime_status\` — derived by aggregating the iteration participants'
|
|
261
261
|
states. Values: \`draft | running | completed |
|
|
262
262
|
completed_with_errors | cancelled\`. **Never reports \`failed\` while
|
|
263
263
|
completed runs exist** (the Bk2 invariant). Prefer this for any
|
|
@@ -307,8 +307,8 @@ the chat payload (chat) — while the study carries the persistent
|
|
|
307
307
|
shape (assignments, questionnaire, modality).
|
|
308
308
|
|
|
309
309
|
For chat modality, the iteration's \`details.mode_details\` discriminator
|
|
310
|
-
selects between **external_chatbot** (
|
|
311
|
-
endpoint) and **
|
|
310
|
+
selects between **external_chatbot** (participants probe a customer chatbot
|
|
311
|
+
endpoint) and **participant_pair** (two AI people converse with
|
|
312
312
|
each other, one Conversation per pair index). Wire-shape examples and
|
|
313
313
|
pair-mode rules live under the "## Chat modality" section below; the
|
|
314
314
|
full chat-author workflow is at \`guides/chat\`.
|
|
@@ -322,7 +322,7 @@ full chat-author workflow is at \`guides/chat\`.
|
|
|
322
322
|
|
|
323
323
|
Because you want to A/B different URLs or content variants while keeping
|
|
324
324
|
the same task definitions and questionnaire. Each iteration also owns
|
|
325
|
-
its own roster of
|
|
325
|
+
its own roster of participants, so you can compare groups as well.
|
|
326
326
|
|
|
327
327
|
## Common commands
|
|
328
328
|
|
|
@@ -358,9 +358,9 @@ ish iteration create --content-url ./report.pdf
|
|
|
358
358
|
# Chat (external_chatbot) — probe a saved chatbot endpoint:
|
|
359
359
|
ish iteration create --chat-endpoint-id ce-... --max-turns 10 --early-termination
|
|
360
360
|
|
|
361
|
-
# Chat (
|
|
362
|
-
ish iteration create --chat-mode
|
|
363
|
-
--
|
|
361
|
+
# Chat (participant_pair) — rehearse a conversation between two AI groups:
|
|
362
|
+
ish iteration create --chat-mode participant_pair \\
|
|
363
|
+
--group-a p-a1,p-a2 --group-b p-b1,p-b2 \\
|
|
364
364
|
--scenario-a "You're a senior sales rep pitching ish." \\
|
|
365
365
|
--scenario-b "You're a skeptical CTO evaluating ish."
|
|
366
366
|
|
|
@@ -376,7 +376,7 @@ can be collected per **segment** instead of over the whole asset. A
|
|
|
376
376
|
segment is a contiguous slice of the iteration's content — a 30-second
|
|
377
377
|
window of a video, a paragraph range of an email, a section of a PDF.
|
|
378
378
|
Each segment can carry a human-readable **label** ("Intro", "Pricing
|
|
379
|
-
section", "Call to action") that surfaces in the
|
|
379
|
+
section", "Call to action") that surfaces in the participant UI and in
|
|
380
380
|
results.
|
|
381
381
|
|
|
382
382
|
Segments live inside the iteration's \`segmentation\` field — there is
|
|
@@ -423,7 +423,7 @@ reactions; otherwise the default just works.
|
|
|
423
423
|
|
|
424
424
|
### content_config — early termination + selected segments
|
|
425
425
|
|
|
426
|
-
A sibling of \`segmentation\` that controls how the
|
|
426
|
+
A sibling of \`segmentation\` that controls how the participant progresses
|
|
427
427
|
through segments:
|
|
428
428
|
|
|
429
429
|
- \`early_termination: true\` — stop the session once every selected
|
|
@@ -437,7 +437,7 @@ Pass via \`--content-config-json '<json>'\`.
|
|
|
437
437
|
|
|
438
438
|
- **Text modality**: pair plain \`--content-text\` with rich
|
|
439
439
|
\`--content-html\` to render emails / articles with formatting. The
|
|
440
|
-
plain text is what
|
|
440
|
+
plain text is what participants reason over; the HTML is what they see.
|
|
441
441
|
- **Media captions** (video, audio, image): \`--copy-text\` and
|
|
442
442
|
\`--copy-html\` attach a caption to the media — the social-post
|
|
443
443
|
pattern. Add \`--social-platform\` (instagram/tiktok/facebook/linkedin/x)
|
|
@@ -455,12 +455,12 @@ Chat iterations hold a multi-turn conversation. The conversation can
|
|
|
455
455
|
take one of two shapes, picked by the \`mode_details.mode\` discriminator
|
|
456
456
|
on the iteration:
|
|
457
457
|
|
|
458
|
-
- **\`external_chatbot\`** — a
|
|
458
|
+
- **\`external_chatbot\`** — a participant talks to a customer chatbot
|
|
459
459
|
endpoint (the original chat behaviour). The endpoint config or saved
|
|
460
460
|
chatbot-endpoint reference lives at
|
|
461
461
|
\`details.mode_details.endpoint\` / \`details.mode_details.chatbot_endpoint_id\`.
|
|
462
|
-
- **\`
|
|
463
|
-
|
|
462
|
+
- **\`participant_pair\`** — two AI people talk to each other.
|
|
463
|
+
group_a and group_b pair 1:1 by index when counts match (N
|
|
464
464
|
pairs → N conversations); a side of exactly 1 broadcasts across the
|
|
465
465
|
other side (so 1 × N → N conversations all sharing the lone profile).
|
|
466
466
|
Each side carries its own scenario + goal; the other side does not
|
|
@@ -483,13 +483,13 @@ Wire-shape:
|
|
|
483
483
|
"early_termination": true
|
|
484
484
|
}
|
|
485
485
|
|
|
486
|
-
//
|
|
486
|
+
// participant_pair (with explicit groups)
|
|
487
487
|
{
|
|
488
488
|
"type": "chat",
|
|
489
489
|
"mode_details": {
|
|
490
|
-
"mode": "
|
|
491
|
-
"
|
|
492
|
-
"
|
|
490
|
+
"mode": "participant_pair",
|
|
491
|
+
"group_a": ["p-uuid-1", "p-uuid-2"],
|
|
492
|
+
"group_b": ["p-uuid-3", "p-uuid-4"],
|
|
493
493
|
"scenario_a": "You are a senior sales rep pitching ish.",
|
|
494
494
|
"scenario_b": "You are a skeptical CTO evaluating ish.",
|
|
495
495
|
"initiator_side": "a"
|
|
@@ -498,13 +498,13 @@ Wire-shape:
|
|
|
498
498
|
"early_termination": true
|
|
499
499
|
}
|
|
500
500
|
|
|
501
|
-
//
|
|
501
|
+
// participant_pair (with role criteria — backend resolves the pool)
|
|
502
502
|
{
|
|
503
503
|
"type": "chat",
|
|
504
504
|
"mode_details": {
|
|
505
|
-
"mode": "
|
|
506
|
-
"
|
|
507
|
-
"
|
|
505
|
+
"mode": "participant_pair",
|
|
506
|
+
"group_a": [],
|
|
507
|
+
"group_b": [],
|
|
508
508
|
"role_criteria_a": {
|
|
509
509
|
"occupation": ["founder", "ceo"],
|
|
510
510
|
"min_age": 28, "max_age": 55,
|
|
@@ -520,23 +520,23 @@ Wire-shape:
|
|
|
520
520
|
}
|
|
521
521
|
\`\`\`
|
|
522
522
|
|
|
523
|
-
## Audience selection (
|
|
523
|
+
## Audience selection (participant_pair)
|
|
524
524
|
|
|
525
|
-
Each side of a pair needs **either** an explicit
|
|
525
|
+
Each side of a pair needs **either** an explicit people list **or** a
|
|
526
526
|
role-criteria filter (or both). Three input modes:
|
|
527
527
|
|
|
528
528
|
| Side A input | Side B input | Behaviour |
|
|
529
529
|
| --- | --- | --- |
|
|
530
|
-
| \`--
|
|
530
|
+
| \`--group-a\` (UUIDs) | \`--group-b\` (UUIDs) | Explicit pairing. Equal counts zip 1:1 by index; a side of exactly 1 broadcasts to the other. |
|
|
531
531
|
| \`--role-criteria-a\` (JSON) | \`--role-criteria-b\` (JSON) | Backend resolves matching pool from each side's criteria and persists the IDs back to the iteration. |
|
|
532
532
|
| Either flag pair | Either flag pair | Mixed (e.g. explicit A + criteria B). Backend handles each side independently. |
|
|
533
533
|
| Both flags on one side | (any) | Criteria validates the explicit list; mismatch blocks run with a clear error. |
|
|
534
534
|
|
|
535
|
-
**Persona-first principle**: the
|
|
535
|
+
**Persona-first principle**: the participant's persona is sacred — never
|
|
536
536
|
altered by the scenario. Criteria filter the *eligible pool* upstream
|
|
537
|
-
so that by the time a
|
|
537
|
+
so that by the time a participant reaches the LLM prompt, their persona is
|
|
538
538
|
already plausible for the role. The prompt construction itself does
|
|
539
|
-
not change between explicit-
|
|
539
|
+
not change between explicit-people and criteria-driven flows.
|
|
540
540
|
|
|
541
541
|
\`RoleCriteria\` keys (all optional):
|
|
542
542
|
|
|
@@ -554,7 +554,7 @@ not change between explicit-audience and criteria-driven flows.
|
|
|
554
554
|
If the resolved pool is smaller than the requested conversation count
|
|
555
555
|
for a side, \`ish study run\` exits 2 with the backend's error envelope
|
|
556
556
|
intact. No silent fallback. Broaden the criteria, generate more
|
|
557
|
-
profiles, or pass an explicit \`--
|
|
557
|
+
profiles, or pass an explicit \`--group-a/-b\` list to recover.
|
|
558
558
|
|
|
559
559
|
## Pair-mode flag names (CLI ↔ MCP alignment)
|
|
560
560
|
|
|
@@ -564,7 +564,7 @@ agent doesn't pay a translation tax when switching surfaces:
|
|
|
564
564
|
|
|
565
565
|
| CLI flag | MCP field | What it carries |
|
|
566
566
|
|---------------------------|----------------------------|-----------------------------------------------------|
|
|
567
|
-
| \`--
|
|
567
|
+
| \`--group-a\` / \`-b\` | \`group_a\` / \`group_b\` | Explicit person IDs (UUIDs or aliases) for that side. |
|
|
568
568
|
| \`--role-criteria-a\` / \`-b\` | \`role_criteria_a\` / \`role_criteria_b\` | JSON filter (occupation, country, …) the backend resolves into a pool. |
|
|
569
569
|
| \`--scenario-a\` / \`-b\` | \`scenario_a\` / \`scenario_b\` | The system-prompt-shaped role text injected into one side's prompt only (asymmetry contract). |
|
|
570
570
|
| \`--initiator-side\` | \`initiator_side\` | Which side speaks first (\`a\` default). |
|
|
@@ -572,9 +572,9 @@ agent doesn't pay a translation tax when switching surfaces:
|
|
|
572
572
|
| \`--early-termination\` | \`early_termination\` | Allow the worker to end early when parties signal. |
|
|
573
573
|
|
|
574
574
|
The pre-2026-05 \`--profile-a\` / \`--profile-b\` CLI flags were
|
|
575
|
-
renamed to \`--
|
|
576
|
-
the wire shape (\`mode_details.
|
|
577
|
-
\`mode_details.
|
|
575
|
+
renamed to \`--group-a\` / \`--group-b\` to match the MCP and
|
|
576
|
+
the wire shape (\`mode_details.group_a\` /
|
|
577
|
+
\`mode_details.group_b\`). Same intent, same accepted inputs
|
|
578
578
|
(comma-separated UUIDs or aliases, repeatable). \`--role-criteria-a\`
|
|
579
579
|
/ \`--role-criteria-b\` were already aligned with MCP and did not
|
|
580
580
|
change.
|
|
@@ -592,14 +592,14 @@ ish iteration create --endpoint-config ./bot.json
|
|
|
592
592
|
ish iteration create --chat-endpoint-id ep-abc --max-turns 10
|
|
593
593
|
ish iteration create --chat-endpoint-json '{"url":"https://..."}'
|
|
594
594
|
|
|
595
|
-
#
|
|
596
|
-
ish iteration create --chat-mode
|
|
597
|
-
--
|
|
595
|
+
# participant_pair — two AI groups, asymmetric per-side scenarios:
|
|
596
|
+
ish iteration create --chat-mode participant_pair \\
|
|
597
|
+
--group-a p-a1,p-a2 --group-b p-b1,p-b2 \\
|
|
598
598
|
--scenario-a @./sales_rep.md --scenario-b @./skeptical_cto.md \\
|
|
599
599
|
--max-turns 14
|
|
600
600
|
|
|
601
|
-
#
|
|
602
|
-
ish iteration create --chat-mode
|
|
601
|
+
# participant_pair — criteria-driven group (persona-first filtering):
|
|
602
|
+
ish iteration create --chat-mode participant_pair \\
|
|
603
603
|
--role-criteria-a '{"occupation":["founder","ceo"],"min_age":28}' \\
|
|
604
604
|
--role-criteria-b @./criteria_investor.json \\
|
|
605
605
|
--scenario-a @./sales_rep.md --scenario-b @./skeptical_cto.md \\
|
|
@@ -608,7 +608,7 @@ ish iteration create --chat-mode tester_pair \\
|
|
|
608
608
|
|
|
609
609
|
Tunables (both modes):
|
|
610
610
|
- \`--max-turns N\` — cap the conversation length (default 12 for
|
|
611
|
-
external_chatbot, 14 for
|
|
611
|
+
external_chatbot, 14 for participant_pair; persona drift starts ~20 turns
|
|
612
612
|
so cap accordingly).
|
|
613
613
|
- \`--early-termination\` — let the worker end the session early when
|
|
614
614
|
the parties signal the conversation is over.
|
|
@@ -617,27 +617,27 @@ Pair-mode rules:
|
|
|
617
617
|
- Each side needs **either** \`--profile-*\` (explicit IDs) **or**
|
|
618
618
|
\`--role-criteria-*\` (filter the backend resolves). The two can also
|
|
619
619
|
be combined — criteria then acts as validation on the explicit list.
|
|
620
|
-
- When both sides use explicit \`--
|
|
620
|
+
- When both sides use explicit \`--group-a\` / \`--group-b\`, they
|
|
621
621
|
must be the same length (≥ 1). Same profile on both sides is allowed
|
|
622
622
|
(self-talk rehearsal). When either side defers to criteria, the
|
|
623
623
|
length match is enforced server-side after pool resolution.
|
|
624
624
|
- **1×N broadcast**: pass exactly one profile on one side and N
|
|
625
625
|
profiles on the other to rehearse the fixed side against N
|
|
626
626
|
variations. The CLI auto-broadcasts the singleton to match length
|
|
627
|
-
N. Example: \`--
|
|
628
|
-
produces 3 conversations, all sharing
|
|
627
|
+
N. Example: \`--group-a p-rep --group-b p-cto1,p-cto2,p-cto3\`
|
|
628
|
+
produces 3 conversations, all sharing p-rep on side A. The CLI
|
|
629
629
|
prints a stderr notice so you know broadcasting kicked in.
|
|
630
630
|
- Both \`--scenario-a\` and \`--scenario-b\` are required and asymmetric.
|
|
631
631
|
- \`--initiator-side\` defaults to \`a\` (side A speaks first).
|
|
632
|
-
- \`--chat-mode\` accepts both \`
|
|
632
|
+
- \`--chat-mode\` accepts both \`participant_pair\` and \`participant-pair\`
|
|
633
633
|
(hyphenated variants are normalised). Same normalisation applies to
|
|
634
634
|
\`--screen-format\` (\`mobile_portrait\` ↔ \`mobile-portrait\`),
|
|
635
635
|
\`--kind\` on \`source upload\` (\`text_file\` ↔ \`text-file\`), and the
|
|
636
636
|
\`type\` field in \`--questionnaire\` / \`--questions\` manifests
|
|
637
637
|
(\`single-choice\` ↔ \`single_choice\`).
|
|
638
638
|
- Audiences are pinned to the iteration. \`ish study run\` refuses
|
|
639
|
-
run-time
|
|
640
|
-
filters) on a pair iteration — change the
|
|
639
|
+
run-time people overrides (\`--profile\` / \`--sample\` / \`--all\` /
|
|
640
|
+
filters) on a pair iteration — change the peoples via
|
|
641
641
|
\`ish iteration update <id> --details-json '{...}'\` instead.
|
|
642
642
|
- \`--max-turns\` / \`--early-termination\` on \`ish study run\` override
|
|
643
643
|
the iteration's saved values for that single dispatch (they are not
|
|
@@ -704,7 +704,7 @@ persona drift starts to dominate.
|
|
|
704
704
|
A scenario describes **voice, knowledge, and goal** for one role —
|
|
705
705
|
*not* the demographics of who plays it. Demographic constraints
|
|
706
706
|
("you are 35-year-old Swedish founder") belong in
|
|
707
|
-
\`--role-criteria-a\` / \`--role-criteria-b\` instead. The
|
|
707
|
+
\`--role-criteria-a\` / \`--role-criteria-b\` instead. The participant's
|
|
708
708
|
persona stays sacred; criteria filter the eligible pool upstream so
|
|
709
709
|
the persona is already plausible for the role by the time the LLM
|
|
710
710
|
sees the prompt. Mixing demographics into the scenario text
|
|
@@ -749,20 +749,22 @@ Treat this as actionable, not transient — re-running won't change anything.
|
|
|
749
749
|
|
|
750
750
|
- \`concepts/study\` — the parent artifact.
|
|
751
751
|
- \`concepts/run-verbs\` — how \`ish study run\` selects the iteration.
|
|
752
|
-
- \`concepts/
|
|
752
|
+
- \`concepts/people\` — how participants are picked for a run.
|
|
753
753
|
- \`reference/billing-limits\` — \`maxIterationsPerStudy\` cap on iteration creation.
|
|
754
|
-
- \`reference/credits\` — per-iteration-run credit cost & preview shape (\`pair_preview.credit_estimate\` for
|
|
754
|
+
- \`reference/credits\` — per-iteration-run credit cost & preview shape (\`pair_preview.credit_estimate\` for participant-pair, top-level \`credit_estimate\` otherwise).
|
|
755
755
|
`;
|
|
756
756
|
const CONCEPT_ASSIGNMENT = `# concept: assignment
|
|
757
757
|
|
|
758
|
-
An **assignment** is a single task a
|
|
758
|
+
An **assignment** is a single task a participant performs during an
|
|
759
759
|
interactive study (or considers, for media studies). A study has 1..N
|
|
760
760
|
assignments and they run in order.
|
|
761
761
|
|
|
762
762
|
Each assignment has:
|
|
763
|
-
- \`name\` — short label shown to the
|
|
764
|
-
- \`instructions\` — what the
|
|
763
|
+
- \`name\` — short label shown to the participant ("Sign up").
|
|
764
|
+
- \`instructions\` — what the participant is asked to do ("Complete the signup
|
|
765
765
|
flow using a personal email").
|
|
766
|
+
- \`steps\` (optional) — an ordered checklist of atomic actions the participant
|
|
767
|
+
should accomplish (see "Steps" below).
|
|
766
768
|
|
|
767
769
|
## CLI input formats
|
|
768
770
|
|
|
@@ -782,27 +784,59 @@ ish study create --name "Checkout" --modality interactive \\
|
|
|
782
784
|
ish study create … --assignments '[{"name":"Browse","instructions":"…"}]'
|
|
783
785
|
\`\`\`
|
|
784
786
|
|
|
785
|
-
\`assignments.json\` shape:
|
|
787
|
+
\`assignments.json\` shape (note the optional \`steps\` checklist):
|
|
786
788
|
\`\`\`json
|
|
787
789
|
[
|
|
788
790
|
{ "name": "Browse", "instructions": "Find a product you like" },
|
|
789
|
-
{
|
|
791
|
+
{
|
|
792
|
+
"name": "Buy",
|
|
793
|
+
"instructions": "Add to cart and checkout",
|
|
794
|
+
"steps": [
|
|
795
|
+
{ "name": "Find a product", "description": "Browse to any item" },
|
|
796
|
+
{ "name": "Add to cart" },
|
|
797
|
+
{ "name": "Complete checkout" }
|
|
798
|
+
]
|
|
799
|
+
}
|
|
790
800
|
]
|
|
791
801
|
\`\`\`
|
|
792
802
|
|
|
803
|
+
## Steps (checklist)
|
|
804
|
+
|
|
805
|
+
An assignment may carry an ordered \`steps\` list — atomic actions the participant
|
|
806
|
+
should accomplish. Each step is \`{ "name": "...", "description"?: "..." }\`
|
|
807
|
+
(name 1–80 chars, description ≤500). Steps are authored **only via the JSON
|
|
808
|
+
forms** (\`--assignments-file\` / \`--assignments\`); the inline
|
|
809
|
+
\`--assignment "Name:Instructions"\` shorthand cannot express them.
|
|
810
|
+
|
|
811
|
+
Steps are honored for **interactive** and **external_chatbot chat** studies
|
|
812
|
+
only. The backend rejects steps on media modalities (text/video/audio/image/
|
|
813
|
+
document) and on chat \`participant_pair\`.
|
|
814
|
+
|
|
815
|
+
After a run, an LLM verifier grades each step per participant. \`ish study get <id>\`
|
|
816
|
+
then surfaces, on each assignment:
|
|
817
|
+
- \`steps\` — the resolved checklist, each with a server-assigned \`id\` (a slug
|
|
818
|
+
like \`add-to-cart\`).
|
|
819
|
+
- \`step_completion\` — a per-step rollup: \`step_id\`, \`total\`, \`passed\`,
|
|
820
|
+
\`rate\` (null until graded), and up to 3 \`sample_failures\`
|
|
821
|
+
(\`{participant_id, reason}\`). Human output shows a pass-rate line per step;
|
|
822
|
+
\`sample_failures[].participant_id\` is a UUID, so it only appears under
|
|
823
|
+
\`--verbose\` in JSON mode.
|
|
824
|
+
|
|
793
825
|
## Update or replace
|
|
794
826
|
|
|
795
827
|
\`ish study update <id> --assignment …\` (or \`--assignments-file\`)
|
|
796
|
-
replaces the full assignment list — additive editing is not supported.
|
|
828
|
+
replaces the full assignment list — additive editing is not supported. Steps
|
|
829
|
+
ride along when present in the JSON forms.
|
|
797
830
|
|
|
798
831
|
## Related
|
|
799
832
|
|
|
800
833
|
- \`concepts/study\` — assignments are immutable to the run; questionnaire is too.
|
|
801
834
|
- \`concepts/questionnaire\` — the other half of the study definition.
|
|
835
|
+
- \`reference/json-mode\` — how \`step_completion\` renders in lean vs --verbose.
|
|
802
836
|
`;
|
|
803
837
|
const CONCEPT_QUESTIONNAIRE = `# concept: questionnaire
|
|
804
838
|
|
|
805
|
-
The **questionnaire** is the list of \`interview_questions\` a
|
|
839
|
+
The **questionnaire** is the list of \`interview_questions\` a participant
|
|
806
840
|
answers before or after their assignments. A study has 0..N
|
|
807
841
|
questions, each with a type and a timing.
|
|
808
842
|
|
|
@@ -848,20 +882,20 @@ so either works in your manifest.
|
|
|
848
882
|
const CONCEPT_ASK = `# concept: ask
|
|
849
883
|
|
|
850
884
|
An **ask** is a lightweight reaction artifact — much less ceremony than
|
|
851
|
-
a study. It has
|
|
852
|
-
**rounds**. Each round shows the
|
|
885
|
+
a study. It has a group of people (fixed at creation) and a sequence of
|
|
886
|
+
**rounds**. Each round shows the people a prompt plus 1..N variants
|
|
853
887
|
(text or image) and collects their reactions.
|
|
854
888
|
|
|
855
889
|
- Alias prefix: \`a-\`
|
|
856
|
-
- Audience is fixed at ask creation — you cannot swap
|
|
857
|
-
rounds. (You can extend it via \`ish ask add-
|
|
890
|
+
- Audience is fixed at ask creation — you cannot swap groups between
|
|
891
|
+
rounds. (You can extend it via \`ish ask add-people\`.)
|
|
858
892
|
- Up to 5 rounds per ask.
|
|
859
893
|
|
|
860
894
|
## When to use ask vs study
|
|
861
895
|
|
|
862
896
|
- Reach for **ask** for: tagline A/B, hero-image picks, copy comparisons,
|
|
863
897
|
quick reactions to creative variants.
|
|
864
|
-
- Reach for **study** for: anything that needs a
|
|
898
|
+
- Reach for **study** for: anything that needs a participant to *do* something
|
|
865
899
|
(interactive flow, multi-step task, time-on-page).
|
|
866
900
|
|
|
867
901
|
See \`concepts/run-verbs\` for the side-by-side.
|
|
@@ -903,11 +937,11 @@ intact — no \`--verbose\` needed to see it.
|
|
|
903
937
|
|
|
904
938
|
## Stage-then-dispatch (draft asks)
|
|
905
939
|
|
|
906
|
-
When you want a human to review the
|
|
940
|
+
When you want a human to review the people and prompt **before** any
|
|
907
941
|
credits are spent, separate creation from dispatch:
|
|
908
942
|
|
|
909
943
|
\`\`\`
|
|
910
|
-
# 1. Stage — materializes
|
|
944
|
+
# 1. Stage — materializes participants, no worker enqueue, no bill yet
|
|
911
945
|
ish ask create --workspace w-6ec --name "tagline AB" \\
|
|
912
946
|
--prompt "Which sounds better?" \\
|
|
913
947
|
--variant text:"Short and punchy." \\
|
|
@@ -921,7 +955,7 @@ ish ask create --workspace w-6ec --name "tagline AB" \\
|
|
|
921
955
|
ish ask dispatch a-6ec --wait
|
|
922
956
|
\`\`\`
|
|
923
957
|
|
|
924
|
-
\`--no-dispatch\` requires
|
|
958
|
+
\`--no-dispatch\` requires people flags (participants are still materialized
|
|
925
959
|
at create time — only the worker enqueue and billing are deferred). It
|
|
926
960
|
is incompatible with \`--wait\` since there is nothing to wait for.
|
|
927
961
|
|
|
@@ -986,7 +1020,7 @@ When the ask has 2+ rounds, \`ask results\` also includes a top-level
|
|
|
986
1020
|
\`ish ask retry <ask> --round N\` re-dispatches only the ERRORED
|
|
987
1021
|
responses on a round. COMPLETED responses are left untouched (their
|
|
988
1022
|
answers are the source of truth). Use this after a partial failure
|
|
989
|
-
(e.g. 4 of 5
|
|
1023
|
+
(e.g. 4 of 5 participants errored on round 1) — fix the underlying cause,
|
|
990
1024
|
then \`ask retry\` to backfill the missing rows. Idempotent: zero-errored
|
|
991
1025
|
is a no-op. Add \`--wait\` to block until the retried round settles.
|
|
992
1026
|
|
|
@@ -1004,7 +1038,7 @@ abort without parsing prose.
|
|
|
1004
1038
|
\`ish ask add-questions --round N --questions ./qs.json\` is **additive
|
|
1005
1039
|
by default**: prior phase-1 outputs (comment, pick, ratings) are
|
|
1006
1040
|
preserved on every non-errored response, and the worker only answers
|
|
1007
|
-
the newly-added questions for each
|
|
1041
|
+
the newly-added questions for each participant. Existing picks stay stable.
|
|
1008
1042
|
|
|
1009
1043
|
Pass \`--redispatch-all\` for the legacy reset behavior — useful when a
|
|
1010
1044
|
question is sufficiently different that you want fresh first
|
|
@@ -1033,11 +1067,11 @@ round-trips when you know them up front:
|
|
|
1033
1067
|
agent-tool result budgets. Pass
|
|
1034
1068
|
\`include_accessibility_profile=true\` to include it. Mirrors the
|
|
1035
1069
|
existing \`include_bio=false\` default — same opt-in pattern.
|
|
1036
|
-
- **\`
|
|
1070
|
+
- **\`ask_participants\` uses \`dispatch_into_round\`, not \`round\`.** The
|
|
1037
1071
|
parameter name was renamed from the ambiguous \`round\` (which read
|
|
1038
1072
|
as "start from round N") to the verbatim \`dispatch_into_round\`
|
|
1039
|
-
("add these new
|
|
1040
|
-
it appends
|
|
1073
|
+
("add these new participants into round N"). Behavior is unchanged —
|
|
1074
|
+
it appends participants to the named round on an existing ask, it does
|
|
1041
1075
|
not roll back or restart any prior round.
|
|
1042
1076
|
|
|
1043
1077
|
## Variant syntax
|
|
@@ -1052,14 +1086,14 @@ round-trips when you know them up front:
|
|
|
1052
1086
|
## Related
|
|
1053
1087
|
|
|
1054
1088
|
- \`concepts/round\` — what a round is and how it executes.
|
|
1055
|
-
- \`concepts/
|
|
1089
|
+
- \`concepts/people\` — how participants are chosen at ask creation.
|
|
1056
1090
|
- \`concepts/run-verbs\` — \`ish ask run\` vs \`ish study run\`.
|
|
1057
1091
|
- \`reference/credits\` — ask rounds bill 1 credit per successful response.
|
|
1058
1092
|
`;
|
|
1059
1093
|
const CONCEPT_ROUND = `# concept: round
|
|
1060
1094
|
|
|
1061
1095
|
A **round** is the unit of execution within an ask. Each round shows the
|
|
1062
|
-
ask
|
|
1096
|
+
ask.s participants a prompt + variants and collects reactions.
|
|
1063
1097
|
|
|
1064
1098
|
- Up to 5 rounds per ask.
|
|
1065
1099
|
- Rounds are 1-indexed in user-facing flags (\`--round 2\`) but stored
|
|
@@ -1081,7 +1115,7 @@ ish ask results a-6ec --round 1
|
|
|
1081
1115
|
|
|
1082
1116
|
Appending questions to a completed round preserves prior data — variant
|
|
1083
1117
|
comments, picks, ratings, and earlier-question answers all stay. Only
|
|
1084
|
-
the new question(s) get dispatched to the existing
|
|
1118
|
+
the new question(s) get dispatched to the existing participants. Cost is
|
|
1085
1119
|
roughly N phase-2 LLM calls instead of 2N (no phase-1 re-run). Errored
|
|
1086
1120
|
responses are skipped entirely; completed responses flip to PENDING and
|
|
1087
1121
|
re-finalize after the new question is answered.
|
|
@@ -1089,51 +1123,67 @@ re-finalize after the new question is answered.
|
|
|
1089
1123
|
## Related
|
|
1090
1124
|
|
|
1091
1125
|
- \`concepts/ask\` — the parent artifact.
|
|
1092
|
-
- \`concepts/
|
|
1126
|
+
- \`concepts/people\` — fixed at ask creation; rounds reuse it.
|
|
1093
1127
|
`;
|
|
1094
|
-
const CONCEPT_PROFILE = `# concept:
|
|
1128
|
+
const CONCEPT_PROFILE = `# concept: person
|
|
1095
1129
|
|
|
1096
|
-
A **
|
|
1097
|
-
human whose behaviour drives a
|
|
1130
|
+
A **person** is a reusable persona — the simulated
|
|
1131
|
+
human whose behaviour drives a participant instance during a study or ask.
|
|
1098
1132
|
|
|
1099
1133
|
- Alias prefix: \`tp-\`
|
|
1100
1134
|
- Lives at the workspace level, reusable across studies and asks.
|
|
1101
|
-
- Distinct from a "
|
|
1135
|
+
- Distinct from a "participant" (\`pt-\`) — a participant is one *instance* of a
|
|
1102
1136
|
profile inside one iteration.
|
|
1103
1137
|
|
|
1104
1138
|
## Generation vs. manual creation
|
|
1105
1139
|
|
|
1106
|
-
\`ish
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1140
|
+
\`ish person generate\` runs an agentic generation job: it reads your
|
|
1141
|
+
description (a researcher brief) and any uploaded sources (transcripts,
|
|
1142
|
+
emails, customer records, audio, images) describing how real people
|
|
1143
|
+
reacted, then produces people PLUS scenarios grounded in those
|
|
1144
|
+
reactions. It is async — ish enqueues the job, polls until it's done
|
|
1145
|
+
(~30-60s), then prints the resulting profile(s) with their scenarios.
|
|
1146
|
+
Provide \`--description\` (>=10 chars) and/or at least one \`--source\`.
|
|
1110
1147
|
|
|
1111
1148
|
\`\`\`
|
|
1112
1149
|
# 5 profiles from a written brief:
|
|
1113
|
-
ish
|
|
1150
|
+
ish person generate \\
|
|
1114
1151
|
--description "Tech-savvy millennials in the US who use mobile banking" \\
|
|
1115
1152
|
--count 5
|
|
1116
1153
|
|
|
1117
1154
|
# One profile from a transcript (auto-uploaded):
|
|
1118
|
-
ish
|
|
1155
|
+
ish person generate --source ./interviews/sarah.txt --count 1
|
|
1119
1156
|
|
|
1120
1157
|
# Audio with diarization:
|
|
1121
|
-
ish
|
|
1158
|
+
ish person generate --description "Voices behind support tickets" \\
|
|
1122
1159
|
--source ./call.mp3 --diarize --count 3
|
|
1160
|
+
|
|
1161
|
+
# Ground a profile in how someone reacted to a real artifact:
|
|
1162
|
+
ish source upload ./proposal.eml --description "called this proposal lazy and vague"
|
|
1163
|
+
# → ps-3a4
|
|
1164
|
+
ish person generate --description "Skeptical enterprise buyer" \\
|
|
1165
|
+
--source ps-3a4 --count 1 --json
|
|
1123
1166
|
\`\`\`
|
|
1124
1167
|
|
|
1168
|
+
The per-source \`--description\` (set on \`source upload\`, or
|
|
1169
|
+
\`--source-description\` on an inline path) is the researcher note: how
|
|
1170
|
+
that person reacted to THAT file. The job grounds each scenario in those
|
|
1171
|
+
notes plus the source content. Pass \`--no-scenarios\` to skip fetching
|
|
1172
|
+
the scenarios. The job keeps running server-side past \`--timeout\`
|
|
1173
|
+
(default 600s) — re-poll the job, don't re-enqueue.
|
|
1174
|
+
|
|
1125
1175
|
For explicit control over uploads (reusing one source across runs):
|
|
1126
1176
|
|
|
1127
1177
|
\`\`\`
|
|
1128
1178
|
ish source upload ./call.mp3 --diarize
|
|
1129
|
-
# →
|
|
1130
|
-
ish
|
|
1179
|
+
# → ps-3a4 (status: processed)
|
|
1180
|
+
ish person generate --source ps-3a4 --count 4
|
|
1131
1181
|
\`\`\`
|
|
1132
1182
|
|
|
1133
1183
|
## Manual create
|
|
1134
1184
|
|
|
1135
1185
|
\`\`\`
|
|
1136
|
-
ish
|
|
1186
|
+
ish person create --file profile.json
|
|
1137
1187
|
\`\`\`
|
|
1138
1188
|
|
|
1139
1189
|
Expected JSON: \`{ "name": "...", "type": "ai", "gender": "female",
|
|
@@ -1141,10 +1191,12 @@ Expected JSON: \`{ "name": "...", "type": "ai", "gender": "female",
|
|
|
1141
1191
|
|
|
1142
1192
|
## Generation behavior to expect
|
|
1143
1193
|
|
|
1144
|
-
- **Latency**: \`
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
know it's not stuck. Suppress with \`--quiet\`.
|
|
1194
|
+
- **Latency**: \`person generate\` runs an async job that typically
|
|
1195
|
+
takes ~30-60s. The CLI enqueues, then polls every ~2.5s and prints a
|
|
1196
|
+
stderr status line each time the job's progress message changes, so you
|
|
1197
|
+
know it's not stuck. Suppress with \`--quiet\`. Bound the wait with
|
|
1198
|
+
\`--timeout\` (seconds, default 600); on timeout the job keeps running
|
|
1199
|
+
server-side, so re-poll rather than re-enqueueing.
|
|
1148
1200
|
- **Brief fidelity**: bios reference domain-specific terms from your
|
|
1149
1201
|
description verbatim or as close paraphrase. If you mention
|
|
1150
1202
|
\`F-skatt\`, "manual Excel invoicing", "Stripe payouts", or similar
|
|
@@ -1152,16 +1204,16 @@ Expected JSON: \`{ "name": "...", "type": "ai", "gender": "female",
|
|
|
1152
1204
|
each generated bio's daily-routine framing — not sanded down to
|
|
1153
1205
|
generic prose.
|
|
1154
1206
|
- **DOB diversity**: month-and-day are derived from a deterministic
|
|
1155
|
-
per-
|
|
1156
|
-
every-
|
|
1207
|
+
per-person hash so birthdays spread across the year (no more
|
|
1208
|
+
every-person-on-\`06-15\`). Year follows the requested age.
|
|
1157
1209
|
Re-generating the same name/country/occupation/age yields the
|
|
1158
1210
|
same DOB.
|
|
1159
1211
|
|
|
1160
|
-
## Structured
|
|
1212
|
+
## Structured person fields
|
|
1161
1213
|
|
|
1162
1214
|
Five universal enums + a versioned accessibility JSONB live on every
|
|
1163
|
-
|
|
1164
|
-
\`https://ishlabs.io/spec/
|
|
1215
|
+
Person. Values are snake_case and match
|
|
1216
|
+
\`https://ishlabs.io/spec/person-enums.v1.json\` byte-for-byte.
|
|
1165
1217
|
|
|
1166
1218
|
- \`education_level\`: \`less_than_secondary\`, \`secondary\`,
|
|
1167
1219
|
\`some_post_secondary\`, \`vocational_or_associate\`, \`bachelor\`, \`graduate\`
|
|
@@ -1182,12 +1234,12 @@ TesterProfile. Values are snake_case and match
|
|
|
1182
1234
|
\`auditory\`, \`motor\`, \`cognitive\`, \`data\` groups, plus
|
|
1183
1235
|
\`assistive_tech: string[]\` and \`notes\`. Empty \`{}\` means "no
|
|
1184
1236
|
accessibility configuration declared". Schema:
|
|
1185
|
-
\`https://ishlabs.io/spec/accessibility-
|
|
1237
|
+
\`https://ishlabs.io/spec/accessibility-person-schema.v1.json\`.
|
|
1186
1238
|
|
|
1187
|
-
Set them on \`ish
|
|
1239
|
+
Set them on \`ish person update\`:
|
|
1188
1240
|
|
|
1189
1241
|
\`\`\`
|
|
1190
|
-
ish
|
|
1242
|
+
ish person update p-1b9 \\
|
|
1191
1243
|
--education-level bachelor \\
|
|
1192
1244
|
--household couple_with_kids \\
|
|
1193
1245
|
--locale-type suburban \\
|
|
@@ -1195,33 +1247,33 @@ ish profile update tp-1b9 \\
|
|
|
1195
1247
|
--employment-status employed_full_time
|
|
1196
1248
|
|
|
1197
1249
|
# accessibility_profile accepts inline JSON or a path:
|
|
1198
|
-
ish
|
|
1250
|
+
ish person update p-1b9 --accessibility-profile '{
|
|
1199
1251
|
"version": "1.0",
|
|
1200
1252
|
"visual": {"uses_screen_reader": true, "text_size": "large"},
|
|
1201
1253
|
"cognitive": {"reduce_motion": true},
|
|
1202
1254
|
"assistive_tech": ["VoiceOver"]
|
|
1203
1255
|
}'
|
|
1204
1256
|
|
|
1205
|
-
ish
|
|
1257
|
+
ish person update p-1b9 --accessibility-profile ./a11y.json
|
|
1206
1258
|
\`\`\`
|
|
1207
1259
|
|
|
1208
1260
|
The legacy \`--tech-savviness\` flag was removed in
|
|
1209
|
-
\`
|
|
1261
|
+
\`person-schema-v2\`; passing it now produces commander's standard
|
|
1210
1262
|
"unknown option" error.
|
|
1211
1263
|
|
|
1212
1264
|
## Related
|
|
1213
1265
|
|
|
1214
|
-
- \`concepts/source\` — the inputs to \`
|
|
1215
|
-
- \`concepts/
|
|
1216
|
-
- \`guides/build-specific-
|
|
1217
|
-
(\`
|
|
1266
|
+
- \`concepts/source\` — the inputs to \`person generate\`.
|
|
1267
|
+
- \`concepts/people\` — how profiles get selected into a run.
|
|
1268
|
+
- \`guides/build-specific-person\` — iterative probe loop
|
|
1269
|
+
(\`person suggest-scenarios\` + \`person evidence add\`/\`list\`)
|
|
1218
1270
|
for crafting one specific persona, distinct from the
|
|
1219
|
-
|
|
1220
|
-
- \`reference/billing-limits\` — \`
|
|
1271
|
+
people-generation flow.
|
|
1272
|
+
- \`reference/billing-limits\` — \`maxCustomPersons\` cap on person creation.
|
|
1221
1273
|
`;
|
|
1222
1274
|
const CONCEPT_SOURCE = `# concept: source
|
|
1223
1275
|
|
|
1224
|
-
A **source** is an input to \`ish
|
|
1276
|
+
A **source** is an input to \`ish person generate\`: a transcript,
|
|
1225
1277
|
audio file, image, or PDF that an LLM reads to ground generated profiles
|
|
1226
1278
|
in real customer evidence.
|
|
1227
1279
|
|
|
@@ -1231,36 +1283,36 @@ in real customer evidence.
|
|
|
1231
1283
|
|
|
1232
1284
|
## Two ways to use a source
|
|
1233
1285
|
|
|
1234
|
-
1. **Inline** — pass a local file directly to \`
|
|
1286
|
+
1. **Inline** — pass a local file directly to \`person generate\`. The
|
|
1235
1287
|
file is uploaded and processed in-line:
|
|
1236
1288
|
\`\`\`
|
|
1237
|
-
ish
|
|
1289
|
+
ish person generate --source ./call.mp3 --diarize --count 3
|
|
1238
1290
|
\`\`\`
|
|
1239
1291
|
|
|
1240
1292
|
2. **Upload-then-reuse** — upload once, reference the alias from many
|
|
1241
1293
|
\`generate\` runs:
|
|
1242
1294
|
\`\`\`
|
|
1243
1295
|
ish source upload ./call.mp3 --diarize
|
|
1244
|
-
# →
|
|
1245
|
-
ish
|
|
1296
|
+
# → ps-3a4 (status: processed)
|
|
1297
|
+
ish person generate --source ps-3a4 --count 4
|
|
1246
1298
|
\`\`\`
|
|
1247
1299
|
|
|
1248
1300
|
## Inspect
|
|
1249
1301
|
|
|
1250
1302
|
\`\`\`
|
|
1251
|
-
ish source get
|
|
1303
|
+
ish source get ps-3a4
|
|
1252
1304
|
\`\`\`
|
|
1253
1305
|
|
|
1254
1306
|
## Related
|
|
1255
1307
|
|
|
1256
|
-
- \`concepts/
|
|
1308
|
+
- \`concepts/person\` — sources feed profile generation.
|
|
1257
1309
|
`;
|
|
1258
|
-
const
|
|
1310
|
+
const CONCEPT_PEOPLE = `# concept: people selection
|
|
1259
1311
|
|
|
1260
|
-
Both \`ish study run\` and \`ish ask run --new\` accept the same
|
|
1312
|
+
Both \`ish study run\` and \`ish ask run --new\` accept the same people-selection
|
|
1261
1313
|
flags. Two ways to select:
|
|
1262
1314
|
|
|
1263
|
-
1. **Explicit profile IDs** — \`--
|
|
1315
|
+
1. **Explicit profile IDs** — \`--person p-795,p-af2\` (or repeated).
|
|
1264
1316
|
2. **Demographic-filtered sample from the workspace pool** — combine any
|
|
1265
1317
|
of the filters with \`--sample <N>\` or \`--all\` / \`--all-simulatable\`:
|
|
1266
1318
|
- \`--country SE,NO\` (repeatable)
|
|
@@ -1285,14 +1337,14 @@ the filter set, not both.
|
|
|
1285
1337
|
When a filter combination matches zero profiles, the error message
|
|
1286
1338
|
includes the top three populated countries that satisfy your *other*
|
|
1287
1339
|
filters — so you can pivot to a country with actual coverage without a
|
|
1288
|
-
second \`
|
|
1340
|
+
second \`person list\` round-trip:
|
|
1289
1341
|
|
|
1290
1342
|
\`\`\`
|
|
1291
1343
|
$ ish study run --country XX --min-age 35 --sample 5
|
|
1292
|
-
Error: No simulatable AI
|
|
1344
|
+
Error: No simulatable AI people in workspace w-b32 match:
|
|
1293
1345
|
--country XX --min-age 35.
|
|
1294
1346
|
Populated countries with these other filters: SE (12), DE (8), NL (3).
|
|
1295
|
-
Broaden your filters or run \`ish
|
|
1347
|
+
Broaden your filters or run \`ish person list\` to inspect the pool.
|
|
1296
1348
|
\`\`\`
|
|
1297
1349
|
|
|
1298
1350
|
The suggestion is best-effort — it never replaces the original error,
|
|
@@ -1300,13 +1352,13 @@ just augments it.
|
|
|
1300
1352
|
|
|
1301
1353
|
## Audience-build behaviors to know before dispatch
|
|
1302
1354
|
|
|
1303
|
-
Two adjacent footguns surface most often on first-time
|
|
1355
|
+
Two adjacent footguns surface most often on first-time people
|
|
1304
1356
|
construction. Both are documented here because they cost a round-trip
|
|
1305
1357
|
to discover by experiment.
|
|
1306
1358
|
|
|
1307
1359
|
### \`--occupation\` is a loose substring match
|
|
1308
1360
|
|
|
1309
|
-
\`
|
|
1361
|
+
\`group_build\` and the \`--occupation\` flag treat the value as a
|
|
1310
1362
|
**loose, case-insensitive substring filter**, not a whole-token or
|
|
1311
1363
|
taxonomy match. \`--occupation manager\` will match hotel managers,
|
|
1312
1364
|
retail store managers, bank branch managers: anything containing the
|
|
@@ -1321,7 +1373,7 @@ you usually want:
|
|
|
1321
1373
|
- **Pair with other filters**: \`--occupation manager --min-age 28
|
|
1322
1374
|
--country US --country SE\` narrows even a loose substring
|
|
1323
1375
|
meaningfully.
|
|
1324
|
-
- **Preview before dispatch**: \`
|
|
1376
|
+
- **Preview before dispatch**: \`group_build\` returns a
|
|
1325
1377
|
\`match_preview\` summary on the response — a 1-line histogram of
|
|
1326
1378
|
matched occupations (e.g. \`"matched 17 — software developer (12),
|
|
1327
1379
|
DevOps engineer (3), other (2)"\`). Read it before
|
|
@@ -1330,34 +1382,34 @@ you usually want:
|
|
|
1330
1382
|
|
|
1331
1383
|
### The public profile pool skews non-tech / non-Western
|
|
1332
1384
|
|
|
1333
|
-
The default public
|
|
1385
|
+
The default public person pool was built from a broad
|
|
1334
1386
|
demographic sample — so a substring like \`"software engineering
|
|
1335
1387
|
manager"\` may return only a handful of matches, while \`"hotel
|
|
1336
1388
|
manager"\` or \`"retail associate"\` return many. Two adaptations:
|
|
1337
1389
|
|
|
1338
|
-
- **Don't assume Silicon Valley defaults.** A criteria-driven
|
|
1390
|
+
- **Don't assume Silicon Valley defaults.** A criteria-driven group
|
|
1339
1391
|
that works on a private testing pool may resolve to a much smaller
|
|
1340
1392
|
count in the public pool. Read the \`match_preview\` (or count) on
|
|
1341
|
-
every \`
|
|
1393
|
+
every \`group_build\` before dispatching a run that depends on
|
|
1342
1394
|
reaching N matches.
|
|
1343
1395
|
- **Seed your own pool when you need a specific archetype.** If the
|
|
1344
|
-
public pool is genuinely thin for your role, generate the
|
|
1345
|
-
yourself via \`ish
|
|
1396
|
+
public pool is genuinely thin for your role, generate the people
|
|
1397
|
+
yourself via \`ish person generate --description "..."\` — that
|
|
1346
1398
|
produces profiles plausible for the role you described, regardless
|
|
1347
|
-
of public-pool composition. See \`concepts/
|
|
1399
|
+
of public-pool composition. See \`concepts/person\`.
|
|
1348
1400
|
|
|
1349
1401
|
## Defaults
|
|
1350
1402
|
|
|
1351
|
-
- \`ish study run\` with no
|
|
1352
|
-
existing
|
|
1353
|
-
- \`ish ask run\` (without \`--new\`) → cannot change
|
|
1403
|
+
- \`ish study run\` with no people flags → reuses the iteration's
|
|
1404
|
+
existing participants. Ideal for re-running the same participants.
|
|
1405
|
+
- \`ish ask run\` (without \`--new\`) → cannot change participants; the ask
|
|
1354
1406
|
fixes it at creation. Audience flags only apply with \`--new\`.
|
|
1355
1407
|
|
|
1356
1408
|
## Examples
|
|
1357
1409
|
|
|
1358
1410
|
\`\`\`
|
|
1359
1411
|
# Explicit:
|
|
1360
|
-
ish study run --
|
|
1412
|
+
ish study run --person p-795,p-af2
|
|
1361
1413
|
|
|
1362
1414
|
# Sample 3 Swedish profiles aged 35-50:
|
|
1363
1415
|
ish study run --country SE --min-age 35 --max-age 50 --sample 3
|
|
@@ -1371,7 +1423,7 @@ ish study run --bio "screen reader" --all
|
|
|
1371
1423
|
# Every female profile in the workspace:
|
|
1372
1424
|
ish study run --gender female --all
|
|
1373
1425
|
|
|
1374
|
-
# Ask +
|
|
1426
|
+
# Ask + people in one shot:
|
|
1375
1427
|
ish ask run --new --name "SE 35-50" --prompt "Which sounds better?" \\
|
|
1376
1428
|
--variant text:"A" --variant text:"B" \\
|
|
1377
1429
|
--country SE --min-age 35 --max-age 50 --sample 10
|
|
@@ -1379,14 +1431,14 @@ ish ask run --new --name "SE 35-50" --prompt "Which sounds better?" \\
|
|
|
1379
1431
|
|
|
1380
1432
|
## Related
|
|
1381
1433
|
|
|
1382
|
-
- \`concepts/
|
|
1383
|
-
- \`concepts/run-verbs\` — when
|
|
1434
|
+
- \`concepts/person\` — generate profiles to fill the pool.
|
|
1435
|
+
- \`concepts/run-verbs\` — when people flags apply.
|
|
1384
1436
|
`;
|
|
1385
1437
|
const CONCEPT_SITE_ACCESS = `# concept: site access
|
|
1386
1438
|
|
|
1387
1439
|
For interactive studies that target a gated URL — HTTP basic auth,
|
|
1388
1440
|
session-cookie walls (Vercel preview, Lovable, etc.), or login forms —
|
|
1389
|
-
configure credentials on the workspace once.
|
|
1441
|
+
configure credentials on the workspace once. Participants reuse them when a
|
|
1390
1442
|
study points at a matching origin.
|
|
1391
1443
|
|
|
1392
1444
|
Credentials are encrypted at rest. The CLI never reads them back; it
|
|
@@ -1404,7 +1456,7 @@ ish workspace site-access basic-auth --username alice --password hunter2
|
|
|
1404
1456
|
# Session cookie:
|
|
1405
1457
|
ish workspace site-access cookie --name session --value abc123
|
|
1406
1458
|
|
|
1407
|
-
# Login form (typed by the
|
|
1459
|
+
# Login form (typed by the participant):
|
|
1408
1460
|
ish workspace site-access login --username demo --password demo
|
|
1409
1461
|
|
|
1410
1462
|
# Mark as public (silence the "credentials needed?" prompt):
|
|
@@ -1495,7 +1547,7 @@ or shouldn't be committed to a config JSON file, use a secret.
|
|
|
1495
1547
|
`;
|
|
1496
1548
|
const CONCEPT_RUN_VERBS = `# concept: run verbs — \`study run\` vs \`ask run\`
|
|
1497
1549
|
|
|
1498
|
-
Both verbs dispatch simulations against
|
|
1550
|
+
Both verbs dispatch simulations against a group of people, but the lifecycle
|
|
1499
1551
|
and what they target differ.
|
|
1500
1552
|
|
|
1501
1553
|
## Side by side
|
|
@@ -1505,23 +1557,23 @@ and what they target differ.
|
|
|
1505
1557
|
| Default | latest iteration of the active study | append a round to the active ask |
|
|
1506
1558
|
| Fresh setup | \`ish iteration create …\` first, then run | \`--new\` (creates ask + round 1 in one shot) |
|
|
1507
1559
|
| Specific target| \`--iteration <id>\` | positional ask id (\`a-6ec\`) |
|
|
1508
|
-
| Audience | \`--profile\` OR filters with \`--sample\`/\`--all\` — else reuse iteration's
|
|
1509
|
-
| Output unit | per-
|
|
1560
|
+
| Audience | \`--profile\` OR filters with \`--sample\`/\`--all\` — else reuse iteration's participants | only at \`--new\`; fixed for the ask afterwards |
|
|
1561
|
+
| Output unit | per-participant interactions + questionnaire answers | per-participant reactions per round |
|
|
1510
1562
|
|
|
1511
1563
|
## Decision rule
|
|
1512
1564
|
|
|
1513
|
-
- The
|
|
1565
|
+
- The participant needs to **do** something on a real surface
|
|
1514
1566
|
(URL/app/document)? → study.
|
|
1515
|
-
- The
|
|
1567
|
+
- The participant needs to **react** to one or more variants
|
|
1516
1568
|
(text/image) of creative? → ask.
|
|
1517
1569
|
|
|
1518
1570
|
## Common commands
|
|
1519
1571
|
|
|
1520
1572
|
\`\`\`
|
|
1521
|
-
# Study — reuse iteration
|
|
1573
|
+
# Study — reuse iteration participants, block until done:
|
|
1522
1574
|
ish study run --wait
|
|
1523
1575
|
|
|
1524
|
-
# Study — fresh
|
|
1576
|
+
# Study — fresh group by demographic:
|
|
1525
1577
|
ish study run --country SE --min-age 35 --sample 3
|
|
1526
1578
|
|
|
1527
1579
|
# Ask — append a round:
|
|
@@ -1533,35 +1585,35 @@ ish ask run --new --name "tagline AB" \\
|
|
|
1533
1585
|
--variant text:"A" --variant text:"B" --sample 30 --wants-pick --wait
|
|
1534
1586
|
\`\`\`
|
|
1535
1587
|
|
|
1536
|
-
## Tracking individual
|
|
1588
|
+
## Tracking individual participants after \`study run\`
|
|
1537
1589
|
|
|
1538
|
-
\`ish study run --json\` returns a top-level \`
|
|
1539
|
-
\`
|
|
1590
|
+
\`ish study run --json\` returns a top-level \`participant_aliases[]\` and
|
|
1591
|
+
\`participant_ids[]\` for the participants it just dispatched. Pass either to the
|
|
1540
1592
|
low-level lifecycle verbs:
|
|
1541
1593
|
|
|
1542
1594
|
\`\`\`
|
|
1543
|
-
ish study run --study s-b2c -y --json | jq -r '.
|
|
1595
|
+
ish study run --study s-b2c -y --json | jq -r '.participant_aliases[]' # → pt-072, pt-1ed, ...
|
|
1544
1596
|
|
|
1545
|
-
ish study poll <
|
|
1546
|
-
ish study wait <
|
|
1547
|
-
ish study cancel <
|
|
1548
|
-
ish study extend <
|
|
1597
|
+
ish study poll <participant_id> # one-shot status for one participant
|
|
1598
|
+
ish study wait <participant_id> --timeout 600 # block until that participant finishes
|
|
1599
|
+
ish study cancel <participant_id> # cancel a running simulation
|
|
1600
|
+
ish study extend <participant_id> --add-steps 10 # resume a terminal participant with N more steps
|
|
1549
1601
|
\`\`\`
|
|
1550
1602
|
|
|
1551
|
-
\`<
|
|
1603
|
+
\`<participant_id>\` accepts a participant alias (\`t-…\`) or a full UUID. The
|
|
1552
1604
|
study-level \`poll\`/\`wait\` forms also exist (\`--study <id>\` /
|
|
1553
1605
|
\`--iteration <id>\`) for whole-batch progress.
|
|
1554
1606
|
|
|
1555
1607
|
\`cancel\` and \`extend\` form a reversible stop/start pair. \`cancel\`
|
|
1556
|
-
walks a running
|
|
1557
|
-
removed); \`extend\` then spawns a fresh
|
|
1558
|
-
cancelled
|
|
1608
|
+
walks a running participant to a terminal \`cancelled\` status (no row
|
|
1609
|
+
removed); \`extend\` then spawns a fresh participant branched from the
|
|
1610
|
+
cancelled participant's last interaction. See
|
|
1559
1611
|
\`concepts/extending-a-simulation\` for the full mental model.
|
|
1560
1612
|
|
|
1561
1613
|
## Related
|
|
1562
1614
|
|
|
1563
1615
|
- \`reference/json-mode\` — output modes (display vs capture vs chain).
|
|
1564
|
-
Use \`--get
|
|
1616
|
+
Use \`--get participant_aliases\` to capture the run's participants without
|
|
1565
1617
|
piping through \`jq\`. \`--human\` forces table output even through
|
|
1566
1618
|
\`tee\`/redirection.
|
|
1567
1619
|
- \`concepts/extending-a-simulation\` — \`study extend\` flow, when to
|
|
@@ -1569,58 +1621,58 @@ cancelled tester's last interaction. See
|
|
|
1569
1621
|
`;
|
|
1570
1622
|
const CONCEPT_EXTENDING_SIMULATION = `# concept: extending a simulation
|
|
1571
1623
|
|
|
1572
|
-
\`ish study extend <
|
|
1624
|
+
\`ish study extend <participant_id>\` resumes a **terminal** participant with
|
|
1573
1625
|
more interactions — and optionally a mid-run instruction. The source
|
|
1574
|
-
|
|
1626
|
+
participant is left untouched; a **new** participant row is spawned under the
|
|
1575
1627
|
same iteration, branched from the source's last interaction. Use it
|
|
1576
|
-
when a run hits the \`--max-interactions\` cap before the
|
|
1628
|
+
when a run hits the \`--max-interactions\` cap before the participant
|
|
1577
1629
|
finished, or when you want to probe a "what if I had told them X
|
|
1578
1630
|
mid-run?" scenario without restarting from scratch.
|
|
1579
1631
|
|
|
1580
1632
|
## When extend is the right verb
|
|
1581
1633
|
|
|
1582
|
-
- Run hit the step cap (\`--max-interactions\`) before the
|
|
1634
|
+
- Run hit the step cap (\`--max-interactions\`) before the participant
|
|
1583
1635
|
completed the assignment — give it 10 more steps to push through.
|
|
1584
|
-
-
|
|
1636
|
+
- Participant veered off into a dead-end — cancel it, then extend with an
|
|
1585
1637
|
instruction redirecting it ("Stop browsing the blog. Open the pricing
|
|
1586
1638
|
page and try to add a seat.").
|
|
1587
|
-
- You want to test how a
|
|
1639
|
+
- You want to test how a participant reacts to a mid-run change you didn't
|
|
1588
1640
|
capture in the original assignment — without re-running the whole
|
|
1589
1641
|
cohort.
|
|
1590
1642
|
|
|
1591
1643
|
When extend is **not** the right verb:
|
|
1592
1644
|
|
|
1593
|
-
- Source
|
|
1645
|
+
- Source participant is still RUNNING. \`cancel\` it first, then extend.
|
|
1594
1646
|
Extend refuses non-terminal sources server-side.
|
|
1595
|
-
- You want a fresh cohort with new
|
|
1647
|
+
- You want a fresh cohort with new people flags. Use \`study run\`
|
|
1596
1648
|
with \`--profile\` / \`--sample\` / \`--all\` instead — extend is a
|
|
1597
|
-
per-
|
|
1649
|
+
per-participant resume, not a batch op.
|
|
1598
1650
|
- You want to change the iteration's URL or content. Edit the iteration
|
|
1599
1651
|
itself (\`iteration update\` or a fresh iteration) — extend always
|
|
1600
1652
|
inherits the source's iteration config.
|
|
1601
1653
|
|
|
1602
1654
|
## Mental model — cancel + extend are a reversible pair
|
|
1603
1655
|
|
|
1604
|
-
\`cancel\` and \`extend\` are siblings in the
|
|
1656
|
+
\`cancel\` and \`extend\` are siblings in the participant lifecycle:
|
|
1605
1657
|
|
|
1606
1658
|
\`\`\`
|
|
1607
|
-
RUNNING ──(cancel)──▶ CANCELLED ──(extend)──▶ new RUNNING
|
|
1659
|
+
RUNNING ──(cancel)──▶ CANCELLED ──(extend)──▶ new RUNNING participant
|
|
1608
1660
|
(branched from the
|
|
1609
|
-
cancelled
|
|
1661
|
+
cancelled participant's
|
|
1610
1662
|
last interaction)
|
|
1611
1663
|
|
|
1612
|
-
COMPLETED / FAILED ──(extend)──▶ new RUNNING
|
|
1664
|
+
COMPLETED / FAILED ──(extend)──▶ new RUNNING participant
|
|
1613
1665
|
\`\`\`
|
|
1614
1666
|
|
|
1615
|
-
\`cancel\` is non-destructive — the
|
|
1667
|
+
\`cancel\` is non-destructive — the participant row, every interaction, every
|
|
1616
1668
|
screenshot, and the questionnaire answers all survive. \`extend\` then
|
|
1617
|
-
forks from the last interaction to keep the new
|
|
1669
|
+
forks from the last interaction to keep the new participant's history
|
|
1618
1670
|
seamlessly continuous.
|
|
1619
1671
|
|
|
1620
1672
|
## Flags
|
|
1621
1673
|
|
|
1622
1674
|
\`\`\`
|
|
1623
|
-
ish study extend <
|
|
1675
|
+
ish study extend <participant_id>
|
|
1624
1676
|
[--add-steps <n>] # extra steps, 1-50, default 10
|
|
1625
1677
|
[--instruction <text|@path|->] # optional mid-run user message
|
|
1626
1678
|
[--wait] # block until terminal
|
|
@@ -1633,17 +1685,17 @@ CLI:
|
|
|
1633
1685
|
|
|
1634
1686
|
\`\`\`bash
|
|
1635
1687
|
# Inline:
|
|
1636
|
-
ish study extend
|
|
1688
|
+
ish study extend pt-072 --instruction "Switch to the German pricing page."
|
|
1637
1689
|
|
|
1638
1690
|
# From a file (long-form prompts, version-controlled):
|
|
1639
|
-
ish study extend
|
|
1691
|
+
ish study extend pt-072 --instruction @/tmp/redirect.md
|
|
1640
1692
|
|
|
1641
1693
|
# From stdin (pipe-friendly):
|
|
1642
|
-
echo "Try the search bar instead." | ish study extend
|
|
1694
|
+
echo "Try the search bar instead." | ish study extend pt-072 --instruction -
|
|
1643
1695
|
\`\`\`
|
|
1644
1696
|
|
|
1645
1697
|
The instruction is sent to the backend as \`user_message\`. The new
|
|
1646
|
-
|
|
1698
|
+
participant treats it as **overriding direction** for the rest of the run —
|
|
1647
1699
|
the backend surfaces it in a dedicated \`<user_added_instructions>\`
|
|
1648
1700
|
block on every prompt, not just the first turn, so the LLM doesn't
|
|
1649
1701
|
forget about it as the run goes on.
|
|
@@ -1654,10 +1706,10 @@ Default (no \`--wait\`):
|
|
|
1654
1706
|
|
|
1655
1707
|
\`\`\`json
|
|
1656
1708
|
{
|
|
1657
|
-
"
|
|
1658
|
-
"
|
|
1659
|
-
"
|
|
1660
|
-
"source_alias": "
|
|
1709
|
+
"participant_id": "<new-uuid>",
|
|
1710
|
+
"participant_alias": "pt-xyz",
|
|
1711
|
+
"source_participant_id": "<source-uuid>",
|
|
1712
|
+
"source_alias": "pt-abc",
|
|
1661
1713
|
"study_id": "<study-uuid>",
|
|
1662
1714
|
"job_id": "<job-uuid>",
|
|
1663
1715
|
"additional_steps": 10,
|
|
@@ -1666,7 +1718,7 @@ Default (no \`--wait\`):
|
|
|
1666
1718
|
}
|
|
1667
1719
|
\`\`\`
|
|
1668
1720
|
|
|
1669
|
-
With \`--wait\`, a \`result\` field is appended once the new
|
|
1721
|
+
With \`--wait\`, a \`result\` field is appended once the new participant
|
|
1670
1722
|
reaches a terminal status:
|
|
1671
1723
|
|
|
1672
1724
|
\`\`\`json
|
|
@@ -1675,31 +1727,31 @@ reaches a terminal status:
|
|
|
1675
1727
|
"result": {
|
|
1676
1728
|
"status": "completed",
|
|
1677
1729
|
"interaction_count": 14,
|
|
1678
|
-
"
|
|
1730
|
+
"participant_name": "Anna, 34, Munich"
|
|
1679
1731
|
}
|
|
1680
1732
|
}
|
|
1681
1733
|
\`\`\`
|
|
1682
1734
|
|
|
1683
|
-
UUID fields (\`
|
|
1684
|
-
are preserved in lean output because the new \`
|
|
1735
|
+
UUID fields (\`participant_id\`, \`source_participant_id\`, \`study_id\`, \`job_id\`)
|
|
1736
|
+
are preserved in lean output because the new \`participant_id\` is the
|
|
1685
1737
|
load-bearing return value — same exception \`study run\` makes.
|
|
1686
1738
|
|
|
1687
1739
|
## Errors
|
|
1688
1740
|
|
|
1689
1741
|
| Backend | CLI behavior | Exit |
|
|
1690
1742
|
|---|---|---|
|
|
1691
|
-
| Source not terminal (RUNNING / QUEUED) | \`
|
|
1692
|
-
| Source
|
|
1743
|
+
| Source not terminal (RUNNING / QUEUED) | \`Participant is still running — cancel it first or wait for completion.\` | 2 |
|
|
1744
|
+
| Source participant not found | \`Participant not found: <id>\` | 4 |
|
|
1693
1745
|
| \`additional_steps\` out of range | Client-side parser rejects before the network call | 2 |
|
|
1694
1746
|
| Insufficient credits | Bubbles the server message; retry only after topping up | 5 |
|
|
1695
|
-
| Wait timed out (\`--wait\` only) | \`WaitTimeoutError\` envelope with current status under \`progress.rows[0]\` — the run keeps going server-side; resume with \`study wait <new-
|
|
1747
|
+
| Wait timed out (\`--wait\` only) | \`WaitTimeoutError\` envelope with current status under \`progress.rows[0]\` — the run keeps going server-side; resume with \`study wait <new-participant>\` | 5 |
|
|
1696
1748
|
|
|
1697
1749
|
## Cost model
|
|
1698
1750
|
|
|
1699
1751
|
\`extend\` charges credits for **only \`additional_steps\`**, not for
|
|
1700
1752
|
the source's original \`max_interactions\` cap. The formula is the same
|
|
1701
1753
|
as \`study run\` for interactive runs: \`max(1, round(N / 10))\` per
|
|
1702
|
-
|
|
1754
|
+
participant. So \`--add-steps 10\` costs **1 credit**; \`--add-steps 50\`
|
|
1703
1755
|
costs **5 credits**. See \`reference/credits\` for the full table.
|
|
1704
1756
|
|
|
1705
1757
|
## Worked example — push past the step cap
|
|
@@ -1707,25 +1759,25 @@ costs **5 credits**. See \`reference/credits\` for the full table.
|
|
|
1707
1759
|
\`\`\`bash
|
|
1708
1760
|
# 1. Run a study with a small step cap to feel the limit:
|
|
1709
1761
|
ish study run --sample 1 --max-interactions 5 --wait
|
|
1710
|
-
# →
|
|
1762
|
+
# → participant pt-072 (status: completed_with_errors, hit cap on step 5)
|
|
1711
1763
|
|
|
1712
1764
|
# 2. Inspect what happened:
|
|
1713
|
-
ish study
|
|
1765
|
+
ish study participant pt-072 --summary
|
|
1714
1766
|
|
|
1715
1767
|
# 3. Give it 15 more steps:
|
|
1716
|
-
ish study extend
|
|
1717
|
-
# → new
|
|
1768
|
+
ish study extend pt-072 --add-steps 15 --wait --timeout 600
|
|
1769
|
+
# → new participant pt-9af, status: completed, 18 interactions total
|
|
1718
1770
|
|
|
1719
|
-
# 4. Read the new
|
|
1720
|
-
ish study
|
|
1771
|
+
# 4. Read the new participant's transcript:
|
|
1772
|
+
ish study participant pt-9af --summary
|
|
1721
1773
|
\`\`\`
|
|
1722
1774
|
|
|
1723
1775
|
## Worked example — redirect mid-run
|
|
1724
1776
|
|
|
1725
1777
|
\`\`\`bash
|
|
1726
|
-
#
|
|
1727
|
-
ish study cancel
|
|
1728
|
-
ish study extend
|
|
1778
|
+
# Participant wandered into the wrong flow. Cancel, then redirect:
|
|
1779
|
+
ish study cancel pt-072
|
|
1780
|
+
ish study extend pt-072 \\
|
|
1729
1781
|
--instruction "Stop browsing the blog. Open the pricing page and try to upgrade to Pro." \\
|
|
1730
1782
|
--add-steps 10 --wait
|
|
1731
1783
|
\`\`\`
|
|
@@ -1737,8 +1789,8 @@ ish study extend t-072 \\
|
|
|
1737
1789
|
- \`reference/credits\` — per-modality cost formulas. \`extend\` follows
|
|
1738
1790
|
the interactive formula scaled to \`additional_steps\`.
|
|
1739
1791
|
- \`reference/aliases\` — the \`t-…\` prefix and how aliases resolve.
|
|
1740
|
-
- \`reference/json-mode\` — capture-mode (\`--get
|
|
1741
|
-
chaining the new
|
|
1792
|
+
- \`reference/json-mode\` — capture-mode (\`--get participant_alias\`) for
|
|
1793
|
+
chaining the new participant into the next call.
|
|
1742
1794
|
`;
|
|
1743
1795
|
const REFERENCE_ALIASES = `# reference: aliases
|
|
1744
1796
|
|
|
@@ -1752,9 +1804,9 @@ time the CLI sees an entity.
|
|
|
1752
1804
|
- \`w-\` workspace
|
|
1753
1805
|
- \`s-\` study
|
|
1754
1806
|
- \`i-\` iteration
|
|
1755
|
-
- \`
|
|
1756
|
-
- \`tp-\`
|
|
1757
|
-
- \`tps-\`
|
|
1807
|
+
- \`pt-\` participant (instance of a person in an iteration)
|
|
1808
|
+
- \`tp-\` person
|
|
1809
|
+
- \`tps-\` person source
|
|
1758
1810
|
- \`a-\` ask
|
|
1759
1811
|
- \`r-\` ask round
|
|
1760
1812
|
- \`c-\` config (simulation config)
|
|
@@ -1766,8 +1818,8 @@ time the CLI sees an entity.
|
|
|
1766
1818
|
\`\`\`
|
|
1767
1819
|
ish iteration create --study s-b2c --url https://example.com
|
|
1768
1820
|
ish iteration get i-d4e
|
|
1769
|
-
ish study
|
|
1770
|
-
ish
|
|
1821
|
+
ish study participant pt-a17
|
|
1822
|
+
ish person generate --source ps-3a4 --count 4
|
|
1771
1823
|
\`\`\`
|
|
1772
1824
|
|
|
1773
1825
|
The full UUID is also always accepted. Add \`--verbose\` to JSON output
|
|
@@ -1776,7 +1828,7 @@ to see UUIDs alongside aliases.
|
|
|
1776
1828
|
const REFERENCE_SCREENSHOTS = `# reference: screenshots and iteration media
|
|
1777
1829
|
|
|
1778
1830
|
Interactive study runs produce per-frame screenshots server-side. They
|
|
1779
|
-
let you (or an agent) see what
|
|
1831
|
+
let you (or an agent) see what participants actually saw alongside the
|
|
1780
1832
|
sentiment summary.
|
|
1781
1833
|
|
|
1782
1834
|
## Screenshots — interactive studies only
|
|
@@ -1794,14 +1846,14 @@ ish study screenshots download <study-id> --all --out ./shots/
|
|
|
1794
1846
|
\`\`\`
|
|
1795
1847
|
|
|
1796
1848
|
The list is grouped by frame. Each frame represents a distinct viewport
|
|
1797
|
-
|
|
1849
|
+
participants landed on (e.g. the hero, the pricing block, a documentation
|
|
1798
1850
|
page). Pulling every screenshot can be heavy — start with the listing,
|
|
1799
1851
|
then download representative frames.
|
|
1800
1852
|
|
|
1801
1853
|
### MCP (agent-facing)
|
|
1802
1854
|
|
|
1803
1855
|
\`ish-mcp\` exposes the same artifacts as MCP Resources so an agent can
|
|
1804
|
-
look at them inline. \`study_get(view='summary' | '
|
|
1856
|
+
look at them inline. \`study_get(view='summary' | 'per_participant')\` on an
|
|
1805
1857
|
interactive study now carries:
|
|
1806
1858
|
|
|
1807
1859
|
- \`screenshots_resource: ish://study/<id>/screenshots\` — JSON index
|
|
@@ -1862,7 +1914,7 @@ post-process CLI output with \`jq\` or \`python\` for routine tasks:
|
|
|
1862
1914
|
|-------------------------------------------|--------------------------------------------------|
|
|
1863
1915
|
| Show the user a list of workspaces | bare command (TTY) or \`--human\` if redirecting |
|
|
1864
1916
|
| Capture an alias for a follow-up command | \`--get alias\` |
|
|
1865
|
-
| Inspect a specific nested field | \`--get
|
|
1917
|
+
| Inspect a specific nested field | \`--get person.name\` |
|
|
1866
1918
|
| Compare 2+ fields, or pipe into jq | \`--json\` (or auto-on when piped) |
|
|
1867
1919
|
| Force human output through \`tee\` | \`--human\` |
|
|
1868
1920
|
| Force JSON on a TTY | \`--json\` |
|
|
@@ -1902,7 +1954,7 @@ ish ask results "$ASK" --human | tee /tmp/transcript.txt
|
|
|
1902
1954
|
\`--get\`.
|
|
1903
1955
|
- \`--get <field>\` — extract a single field from the JSON response
|
|
1904
1956
|
and print only its bare value. Supports dotted
|
|
1905
|
-
paths (\`
|
|
1957
|
+
paths (\`person.name\`). On a paginated
|
|
1906
1958
|
\`{items: [...]}\` response, the path
|
|
1907
1959
|
auto-descends into \`items\` so \`--get alias\`
|
|
1908
1960
|
on a list yields one value per line. Implies
|
|
@@ -1955,10 +2007,12 @@ The CLI guarantees these contracts so agents can chain safely:
|
|
|
1955
2007
|
\`--fields\` set, you can identify the affected resource. Default
|
|
1956
2008
|
write-path JSON is compact (\`{id, alias, name, updated_at,
|
|
1957
2009
|
...changed_fields}\`); pass \`--verbose\` for the full server payload.
|
|
1958
|
-
- **\`
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
2010
|
+
- **\`person generate\` returns \`{job: {id, status, person_ids},
|
|
2011
|
+
profiles: [...]}\`** in \`--json\` mode. Each profile is the
|
|
2012
|
+
lean \`person\` shape (pass \`--verbose\` for the full record,
|
|
2013
|
+
including \`simulation_config\`) with its evidence-grounded
|
|
2014
|
+
\`scenarios\` attached; pass \`--no-scenarios\` to omit them.
|
|
2015
|
+
- **\`<entity> get\` accepts multiple IDs.** \`person get\`, \`study get\`,
|
|
1962
2016
|
\`iteration get\`, and \`ask get\` all take \`<ids...>\` — pass two or
|
|
1963
2017
|
more aliases (space- or comma-separated) and the response is a
|
|
1964
2018
|
\`{items:[...], total:N}\` envelope. Use this instead of piping
|
|
@@ -1969,7 +2023,7 @@ The CLI guarantees these contracts so agents can chain safely:
|
|
|
1969
2023
|
|
|
1970
2024
|
\`\`\`json
|
|
1971
2025
|
{
|
|
1972
|
-
"
|
|
2026
|
+
"participants_count": 3,
|
|
1973
2027
|
"responses_total": 9,
|
|
1974
2028
|
"responses_complete": 9,
|
|
1975
2029
|
"rounds": [
|
|
@@ -1979,13 +2033,13 @@ The CLI guarantees these contracts so agents can chain safely:
|
|
|
1979
2033
|
\`\`\`
|
|
1980
2034
|
|
|
1981
2035
|
\`responses_errored\` only appears when at least one response errored.
|
|
1982
|
-
Use these instead of \`jq '.
|
|
2036
|
+
Use these instead of \`jq '.participants | length'\` /
|
|
1983
2037
|
\`jq '.rounds[0].responses | length'\`.
|
|
1984
|
-
- **\`study run --json\` exposes
|
|
1985
|
-
\`
|
|
2038
|
+
- **\`study run --json\` exposes participant handles.** The top-level
|
|
2039
|
+
\`participant_ids[]\` and \`participant_aliases[]\` arrays are the canonical
|
|
1986
2040
|
inputs to \`ish study poll/wait/cancel\`. The \`simulations[]\` array
|
|
1987
2041
|
is collapsed to one batch entry per study (M13) with nested
|
|
1988
|
-
\`
|
|
2042
|
+
\`participant_ids[]\`, \`participant_aliases[]\`, \`job_ids[]\`, and \`count\` —
|
|
1989
2043
|
an N-sample dispatch is a single row, not N near-duplicate rows.
|
|
1990
2044
|
- **\`study\` JSON includes a \`url\` field.** \`study create\`,
|
|
1991
2045
|
\`study generate\`, \`study get\`, \`study list\` (per item), and
|
|
@@ -1998,27 +2052,35 @@ The CLI guarantees these contracts so agents can chain safely:
|
|
|
1998
2052
|
with the \`ISH_APP_URL\` env var for staging or self-hosted UIs.
|
|
1999
2053
|
- **\`study results --json\` includes per-answer sentiment** (M10).
|
|
2000
2054
|
Every \`interview_answers[].answers[]\` row carries \`sentiment\`
|
|
2001
|
-
(the
|
|
2002
|
-
and every \`
|
|
2003
|
-
\`study
|
|
2055
|
+
(the participant's session-level label from \`participant_summary.sentiment\`),
|
|
2056
|
+
and every \`participants[]\` row carries \`sentiment\` + \`comment\`. No
|
|
2057
|
+
\`study participant <id>\` round-trip required.
|
|
2004
2058
|
- **\`study results --summary\`** is a lean projection: counts +
|
|
2005
|
-
sentiment histogram + per-
|
|
2059
|
+
sentiment histogram + per-participant {alias, status, sentiment, comment,
|
|
2006
2060
|
error_message}. Drops \`interview_answers\` and per-interaction
|
|
2007
2061
|
breakdowns. Cheapest "did this run land?" shape.
|
|
2008
|
-
- **\`study
|
|
2062
|
+
- **\`study get --json\` carries assignment step completion** when an
|
|
2063
|
+
assignment has a checklist (see \`concepts/assignment\`). Each
|
|
2064
|
+
\`assignments[].step_completion[]\` row is
|
|
2065
|
+
\`{step_id, name, total, passed, rate, sample_failures}\`. \`step_id\`
|
|
2066
|
+
(a slug like \`add-to-cart\`), \`total\`, \`passed\`, and \`rate\`
|
|
2067
|
+
survive the lean default; \`sample_failures[].participant_id\` is a UUID and
|
|
2068
|
+
so is stripped unless you pass \`--verbose\`. \`rate\` is \`null\` until
|
|
2069
|
+
a run grades the steps.
|
|
2070
|
+
- **\`study results --transcript <participant_id>\`** is the chat-modality
|
|
2009
2071
|
projection — **external_chatbot mode only in v1**. Returns
|
|
2010
|
-
\`{
|
|
2011
|
-
|
|
2072
|
+
\`{participant_id, participant_alias, transcript: [...], unique_bot_replies,
|
|
2073
|
+
participant_summary}\`. Each transcript entry is \`{role, text, turn_index,
|
|
2012
2074
|
...}\` — bot turns add \`failure\` (set when the dispatch crashed);
|
|
2013
|
-
|
|
2014
|
-
\`text\` is null on
|
|
2075
|
+
participant turns add \`action_type\`, \`option_label\`, and \`sentiment\`.
|
|
2076
|
+
\`text\` is null on participant turns whose action carries no text
|
|
2015
2077
|
(\`select_option\`, \`ignore_offered\`); read intent from
|
|
2016
2078
|
\`action_type\` + \`option_label\`. Same shape as the MCP
|
|
2017
2079
|
\`get_chat_transcript\` tool. \`unique_bot_replies = 1\` on a
|
|
2018
2080
|
multi-turn run is the M2 loop signature.
|
|
2019
2081
|
|
|
2020
|
-
**For
|
|
2021
|
-
apply (both speakers are
|
|
2082
|
+
**For participant_pair conversations**, the bot/participant role pair doesn't
|
|
2083
|
+
apply (both speakers are participants). Inspect pair transcripts via the
|
|
2022
2084
|
iteration response instead:
|
|
2023
2085
|
|
|
2024
2086
|
\`\`\`bash
|
|
@@ -2026,19 +2088,19 @@ The CLI guarantees these contracts so agents can chain safely:
|
|
|
2026
2088
|
# → [{ id, pair_index, started_at, ended_at, end_reason, summary, ... }]
|
|
2027
2089
|
\`\`\`
|
|
2028
2090
|
|
|
2029
|
-
Per-side
|
|
2030
|
-
(\`ish study
|
|
2091
|
+
Per-side participant summaries still land on each participant row
|
|
2092
|
+
(\`ish study participant <id> --json\`); the conversation-level summary
|
|
2031
2093
|
(\`end_reason\`, \`dominant_dynamic\`, \`who_steered\`) lands on
|
|
2032
2094
|
\`iteration.conversations[]\`.
|
|
2033
|
-
- **\`study
|
|
2034
|
-
returns just \`{
|
|
2095
|
+
- **\`study participant --summary\`** drops the action timeline and
|
|
2096
|
+
returns just \`{participant, interaction_count, sentiment, comment,
|
|
2035
2097
|
error_message?, error_kind?}\`.
|
|
2036
2098
|
- **\`study poll\` honors the active study.** Pass no \`--study\`
|
|
2037
2099
|
flag and it falls back to the active study (set by
|
|
2038
2100
|
\`ish study use\`), parity with \`study results\` /
|
|
2039
2101
|
\`study wait\` / \`study run\`.
|
|
2040
|
-
- **\`iteration get --json\`
|
|
2041
|
-
Same identifying triple as \`study results --json\`'s
|
|
2102
|
+
- **\`iteration get --json\` participants carry \`alias\` + \`name\`** (M12).
|
|
2103
|
+
Same identifying triple as \`study results --json\`'s participant rows.
|
|
2042
2104
|
- **\`ask results --json\` keeps \`variant_pick_id\` on every response**
|
|
2043
2105
|
(C5-Bug4). It's the load-bearing field for "who picked what" — no
|
|
2044
2106
|
\`--verbose\` required. Same logic on \`ask get --json\`.
|
|
@@ -2051,12 +2113,12 @@ The CLI guarantees these contracts so agents can chain safely:
|
|
|
2051
2113
|
envelope carries \`progress: {study_id, iteration_id?,
|
|
2052
2114
|
timeout_seconds, done, total, pending, rows[]}\` so the agent
|
|
2053
2115
|
can resume by polling rather than re-dispatching. Same shape on
|
|
2054
|
-
\`study wait\` (single-
|
|
2116
|
+
\`study wait\` (single-participant rows[] has length 1).
|
|
2055
2117
|
- **\`study run\` accepts \`--dispatch-timeout <s>\`** (default 120)
|
|
2056
|
-
for the per-POST
|
|
2118
|
+
for the per-POST participants/batch + simulation/start budget. On
|
|
2057
2119
|
timeout (or any dispatch failure), the error envelope includes
|
|
2058
2120
|
\`seeded_but_not_dispatched_ids[]\` + \`seeded_but_not_dispatched_aliases[]\`
|
|
2059
|
-
listing the
|
|
2121
|
+
listing the participants that exist server-side but didn't get
|
|
2060
2122
|
dispatched. Resume by polling those instead of re-running
|
|
2061
2123
|
\`study run\` (which would create another batch on top).
|
|
2062
2124
|
- **\`ask run --new\` is non-idempotent and marked \`retryable: false\`**
|
|
@@ -2083,13 +2145,13 @@ The CLI guarantees these contracts so agents can chain safely:
|
|
|
2083
2145
|
- **Study responses carry a derived \`runtime_status\` field**
|
|
2084
2146
|
(\`draft | running | completed | completed_with_errors | cancelled\`).
|
|
2085
2147
|
Prefer this over the raw \`status\` field — \`runtime_status\` is
|
|
2086
|
-
computed from the iteration
|
|
2148
|
+
computed from the iteration participants' actual run state and never
|
|
2087
2149
|
reports \`failed\` while completed runs exist. Available on
|
|
2088
2150
|
\`study get\`, \`study results\`, and the response from
|
|
2089
2151
|
\`study generate\`. The CLI also surfaces a \`status_inferred\` field
|
|
2090
2152
|
alongside the raw \`status\` when it detects a partial-failure
|
|
2091
2153
|
inconsistency, plus a stderr warning ("Warning: study reports
|
|
2092
|
-
status='failed' but N/M
|
|
2154
|
+
status='failed' but N/M participants completed…").
|
|
2093
2155
|
- **\`study generate --json\` includes a \`modality_rationale\`** —
|
|
2094
2156
|
one short sentence explaining why the LLM picked that modality. Use
|
|
2095
2157
|
it to detect mis-classifications (e.g. brief was a static concept doc
|
|
@@ -2114,11 +2176,11 @@ The CLI guarantees these contracts so agents can chain safely:
|
|
|
2114
2176
|
agent tool result budgets. Pass
|
|
2115
2177
|
\`include_accessibility_profile=true\` to opt in. Mirrors the
|
|
2116
2178
|
existing \`include_bio=false\` opt-in.
|
|
2117
|
-
- **\`
|
|
2118
|
-
\`round\`.** Reads verbatim — "dispatch these new
|
|
2179
|
+
- **\`ask_participants\` parameter is \`dispatch_into_round\`, not
|
|
2180
|
+
\`round\`.** Reads verbatim — "dispatch these new participants into round
|
|
2119
2181
|
N". The old name (\`round\`) read as "start from round N", which
|
|
2120
2182
|
was wrong: the call never restarts prior rounds, it only appends
|
|
2121
|
-
|
|
2183
|
+
participants to the named round. Behavior unchanged across the rename.
|
|
2122
2184
|
- **No more auto-empty iteration A.** \`study create\` and
|
|
2123
2185
|
\`study generate\` no longer produce a placeholder iteration A. The
|
|
2124
2186
|
first explicit \`ish iteration create\` becomes label A.
|
|
@@ -2126,13 +2188,13 @@ The CLI guarantees these contracts so agents can chain safely:
|
|
|
2126
2188
|
(interactive) inline so a single call yields a runnable study.
|
|
2127
2189
|
Running \`study run\` on a study with zero iterations exits 2 with
|
|
2128
2190
|
a suggestion to run \`ish iteration create\` first.
|
|
2129
|
-
- **
|
|
2191
|
+
- **Participant responses include \`error_message\`.** When a participant is
|
|
2130
2192
|
\`status: failed\`, the JSON exposes \`error_message: "<reason>"\` so
|
|
2131
2193
|
agents can act without drilling into logs. \`study results\` rolls
|
|
2132
|
-
this up: top-level \`failed_count\`, plus per-
|
|
2133
|
-
in the \`
|
|
2134
|
-
human output. Empty when the
|
|
2135
|
-
- **\`
|
|
2194
|
+
this up: top-level \`failed_count\`, plus per-participant \`error_message\`
|
|
2195
|
+
in the \`participants[]\` array, and a "Failed participants" subsection in
|
|
2196
|
+
human output. Empty when the participant succeeded.
|
|
2197
|
+
- **\`person list\` emits a stderr pagination hint** when
|
|
2136
2198
|
\`has_more=true\` and \`--quiet\` is not set. The hint goes to **stderr
|
|
2137
2199
|
in every mode** including \`--json\` and piped stdout — it never
|
|
2138
2200
|
pollutes machine-readable stdout but is visible to any agent that
|
|
@@ -2174,11 +2236,11 @@ The CLI guarantees these contracts so agents can chain safely:
|
|
|
2174
2236
|
COMPLETED rows are left untouched; only ERRORED responses are reset
|
|
2175
2237
|
to PENDING and re-run from scratch. Idempotent: zero-errored is a
|
|
2176
2238
|
no-op. Add \`--wait\` to block until the retry settles.
|
|
2177
|
-
- **\`ask results --json\` deduplicates
|
|
2178
|
-
\`
|
|
2239
|
+
- **\`ask results --json\` deduplicates person snapshots.** When
|
|
2240
|
+
\`person\` and \`person_snapshot\` share all
|
|
2179
2241
|
overlapping fields (the common case — they only diverge if the
|
|
2180
2242
|
profile was edited after dispatch), the snapshot is collapsed to
|
|
2181
|
-
\`{snapshotted_at, snapshot_version,
|
|
2243
|
+
\`{snapshotted_at, snapshot_version, _matches_person: true}\`.
|
|
2182
2244
|
Use \`--verbose\` to keep both copies in full.
|
|
2183
2245
|
|
|
2184
2246
|
## Exit codes
|
|
@@ -2208,14 +2270,14 @@ a structured error object on **stdout** and a human message on
|
|
|
2208
2270
|
"retryable": false,
|
|
2209
2271
|
"errors": [
|
|
2210
2272
|
{
|
|
2211
|
-
"loc": ["body", "
|
|
2273
|
+
"loc": ["body", "participants", 0, "person_id"],
|
|
2212
2274
|
"msg": "Input should be a valid UUID",
|
|
2213
2275
|
"type": "uuid_parsing",
|
|
2214
|
-
"input": "
|
|
2276
|
+
"input": "p-bogus",
|
|
2215
2277
|
"allowed_values": ["..."]
|
|
2216
2278
|
}
|
|
2217
2279
|
],
|
|
2218
|
-
"suggestions": ["Pass a
|
|
2280
|
+
"suggestions": ["Pass a person alias (p-...) or UUID."]
|
|
2219
2281
|
}
|
|
2220
2282
|
\`\`\`
|
|
2221
2283
|
|
|
@@ -2245,7 +2307,7 @@ ish ask results a-6ec --human | tee /tmp/results.txt
|
|
|
2245
2307
|
WS=$(ish workspace list --get alias | head -1)
|
|
2246
2308
|
|
|
2247
2309
|
# Inspect a nested field:
|
|
2248
|
-
ish study
|
|
2310
|
+
ish study participant pt-a17 --get person.name
|
|
2249
2311
|
|
|
2250
2312
|
# Chain (full JSON for jq when you need multiple fields):
|
|
2251
2313
|
ish study get s-b2c --fields alias,name,status,iterations --json
|
|
@@ -2260,8 +2322,8 @@ a script, then display the final result back to the user:
|
|
|
2260
2322
|
\`\`\`
|
|
2261
2323
|
# Capture — bare values, no jq needed:
|
|
2262
2324
|
ITER=$(ish iteration create --url https://example.com --get alias)
|
|
2263
|
-
|
|
2264
|
-
for t in $
|
|
2325
|
+
PARTICIPANTS=$(ish study run --iteration "$ITER" --sample 5 --country SE --get participant_aliases)
|
|
2326
|
+
for t in $PARTICIPANTS; do
|
|
2265
2327
|
ish study wait "$t" --timeout 600
|
|
2266
2328
|
done
|
|
2267
2329
|
|
|
@@ -2275,7 +2337,7 @@ reshaping output.
|
|
|
2275
2337
|
`;
|
|
2276
2338
|
const GUIDE_FIRST_STUDY = `# guide: your first study, end to end
|
|
2277
2339
|
|
|
2278
|
-
Goal: from zero to a finished interactive study with 3
|
|
2340
|
+
Goal: from zero to a finished interactive study with 3 participants and one
|
|
2279
2341
|
question, produced in a single workspace.
|
|
2280
2342
|
|
|
2281
2343
|
## 1. Authenticate
|
|
@@ -2291,10 +2353,10 @@ ish workspace create --name "Demo" --base-url https://example.com
|
|
|
2291
2353
|
ish workspace use w-… # use the alias printed above
|
|
2292
2354
|
\`\`\`
|
|
2293
2355
|
|
|
2294
|
-
## 3. Generate a small
|
|
2356
|
+
## 3. Generate a small group of people
|
|
2295
2357
|
|
|
2296
2358
|
\`\`\`
|
|
2297
|
-
ish
|
|
2359
|
+
ish person generate \\
|
|
2298
2360
|
--description "Tech-savvy millennials in the US who use mobile banking" \\
|
|
2299
2361
|
--count 3
|
|
2300
2362
|
\`\`\`
|
|
@@ -2433,31 +2495,31 @@ compute cost. Agents should:
|
|
|
2433
2495
|
|
|
2434
2496
|
- For prospective cost preview: read \`credit_estimate\` from \`study run\`'s
|
|
2435
2497
|
JSON envelope (top-level for solo/media runs; under \`pair_preview\` for
|
|
2436
|
-
|
|
2498
|
+
participant-pair chat).
|
|
2437
2499
|
- For hard budget checks: catch the backend's \`insufficient_credits\`
|
|
2438
2500
|
rejection (HTTP 402; envelope shape below) and react to
|
|
2439
2501
|
\`required\` / \`available\`.
|
|
2440
2502
|
|
|
2441
2503
|
| Surface | Per-principal cost | Total formula | Example |
|
|
2442
2504
|
|---------------------|---------------------------------|--------------------------------------------------|--------------------------------------|
|
|
2443
|
-
| Interactive (URL) | \`max(1, round(steps/10))\` | \`
|
|
2444
|
-
| Text/image/video/audio/document | same | same | 5
|
|
2445
|
-
| Chat (external chatbot, solo) | \`max(1, round(turns/10))\` | \`
|
|
2446
|
-
| Chat (
|
|
2447
|
-
| Ask round | 1 / successful response | \`
|
|
2505
|
+
| Interactive (URL) | \`max(1, round(steps/10))\` | \`participants × per-participant\` | 10 participants × 30 steps → 30 credits |
|
|
2506
|
+
| Text/image/video/audio/document | same | same | 5 participants × 20 steps → 10 credits |
|
|
2507
|
+
| Chat (external chatbot, solo) | \`max(1, round(turns/10))\` | \`participants × per-participant\` | 5 participants × 12 turns → 10 credits |
|
|
2508
|
+
| Chat (participant pair) | \`max(1, round(turns/10))\` × 2 | \`conv × per-side × 2\` | 3 conv × 14 turns → 6 credits |
|
|
2509
|
+
| Ask round | 1 / successful response | \`successful_participants\` | 50 responses → 50 credits |
|
|
2448
2510
|
| Study insights | first free, then **10 flat** | n/a | 2nd analysis → 10 credits |
|
|
2449
2511
|
|
|
2450
2512
|
All numbers are **upper bounds**. Early termination, refusals, or
|
|
2451
|
-
backend
|
|
2513
|
+
backend trimming can reduce actual charge.
|
|
2452
2514
|
|
|
2453
2515
|
## Capping interactive/media spend (\`--max-interactions\`)
|
|
2454
2516
|
|
|
2455
2517
|
\`ish study run\` always sends \`max_interactions\` to the backend for
|
|
2456
2518
|
interactive and media runs. Precedence: \`--max-interactions <n>\` flag
|
|
2457
2519
|
> the iteration's stored \`details.max_interactions\` > **CLI default
|
|
2458
|
-
of 20**. The default exists to prevent runaway spend when a
|
|
2520
|
+
of 20**. The default exists to prevent runaway spend when a participant
|
|
2459
2521
|
gets stuck on a broken or non-responsive surface — without a cap, one
|
|
2460
|
-
stuck
|
|
2522
|
+
stuck participant can rack up 100+ steps before the SDK gives up. Pass
|
|
2461
2523
|
\`--max-interactions\` to override (e.g. \`--max-interactions 50\` for
|
|
2462
2524
|
deeper exploration, \`--max-interactions 5\` for a cheap smoke test).
|
|
2463
2525
|
The confirmation block shows the resolved value and where it came
|
|
@@ -2502,14 +2564,14 @@ Solo media/interactive/chat — top-level \`credit_estimate\`:
|
|
|
2502
2564
|
"iteration_id": "…",
|
|
2503
2565
|
"credit_estimate": {
|
|
2504
2566
|
"upper_bound": 30,
|
|
2505
|
-
"formula": "
|
|
2506
|
-
"breakdown": "10
|
|
2567
|
+
"formula": "media_per_participant",
|
|
2568
|
+
"breakdown": "10 participant(s) × max(1, round(30 steps / 10)) = 10 × 3 = 30",
|
|
2507
2569
|
"unit": "credits"
|
|
2508
2570
|
}
|
|
2509
2571
|
}
|
|
2510
2572
|
\`\`\`
|
|
2511
2573
|
|
|
2512
|
-
The \`formula\` key is stable: agents can branch on it (\`
|
|
2574
|
+
The \`formula\` key is stable: agents can branch on it (\`media_per_participant\`,
|
|
2513
2575
|
\`chat_solo\`, \`chat_pair\`, \`ask_per_response\`).
|
|
2514
2576
|
|
|
2515
2577
|
## Tier allotments
|
|
@@ -2601,11 +2663,11 @@ request time, for any client, is the backend's \`TIER_LIMITS\` dict in
|
|
|
2601
2663
|
| \`maxProducts\` | 1 | 1 | ∞ | ∞ | ∞ |
|
|
2602
2664
|
| \`maxStudiesPerProduct\` | 3 | ∞ | ∞ | ∞ | ∞ |
|
|
2603
2665
|
| \`maxIterationsPerStudy\` | 2 | ∞ | ∞ | ∞ | ∞ |
|
|
2604
|
-
| \`
|
|
2666
|
+
| \`maxCustomPersons\` | 3 | 10 | 10 | ∞ | ∞ |
|
|
2605
2667
|
|
|
2606
2668
|
Commands that may hit a limit: \`ish workspace create\`,
|
|
2607
2669
|
\`ish study create\`, \`ish study generate\`, \`ish iteration create\`,
|
|
2608
|
-
\`ish
|
|
2670
|
+
\`ish person create\`, \`ish person generate\`.
|
|
2609
2671
|
|
|
2610
2672
|
## What you see when a limit is hit
|
|
2611
2673
|
|
|
@@ -2645,7 +2707,7 @@ upgrade or delete an existing resource to free up headroom.
|
|
|
2645
2707
|
- Use \`limit\`, \`current\`, \`max\`, \`tier\` to construct your own
|
|
2646
2708
|
recovery message. The \`limit\` value matches the table above and is
|
|
2647
2709
|
stable.
|
|
2648
|
-
- The \`generate\` endpoints (\`study generate\`, \`
|
|
2710
|
+
- The \`generate\` endpoints (\`study generate\`, \`person generate\`)
|
|
2649
2711
|
refuse the entire batch when the post-generation count would exceed
|
|
2650
2712
|
the cap, rather than partially fulfilling — re-issue with a smaller
|
|
2651
2713
|
\`--count\` after upgrading or pruning.
|
|
@@ -2658,16 +2720,16 @@ upgrade or delete an existing resource to free up headroom.
|
|
|
2658
2720
|
- \`concepts/workspace\` — \`maxProducts\` is per-account.
|
|
2659
2721
|
- \`concepts/study\` — \`maxStudiesPerProduct\` gates study creation.
|
|
2660
2722
|
- \`concepts/iteration\` — \`maxIterationsPerStudy\` gates iteration creation.
|
|
2661
|
-
- \`concepts/
|
|
2723
|
+
- \`concepts/person\` — \`maxCustomPersons\` gates person creation.
|
|
2662
2724
|
- \`reference/json-mode\` — full error envelope shape and exit codes.
|
|
2663
2725
|
`;
|
|
2664
2726
|
const GUIDE_CHAT = `# guide: chat-modality studies
|
|
2665
2727
|
|
|
2666
2728
|
Chat-modality studies cover two distinct shapes:
|
|
2667
2729
|
|
|
2668
|
-
- **external_chatbot** —
|
|
2730
|
+
- **external_chatbot** — participants probe a customer chatbot endpoint
|
|
2669
2731
|
(sections 1-3 below: configure → smoke test → run).
|
|
2670
|
-
- **
|
|
2732
|
+
- **participant_pair** — two AI personas converse with each other for
|
|
2671
2733
|
rehearsal scenarios. Pitch rehearsals, difficult-conversation
|
|
2672
2734
|
prep, founder-vs-investor archetypes. See section 7a/7b and the
|
|
2673
2735
|
TL;DR below.
|
|
@@ -2680,17 +2742,17 @@ scenarios — no extra files needed:
|
|
|
2680
2742
|
|
|
2681
2743
|
\`\`\`bash
|
|
2682
2744
|
# Capture aliases for the rep (1) and CTOs (3) via subshell:
|
|
2683
|
-
REP=$(ish
|
|
2745
|
+
REP=$(ish person generate \\
|
|
2684
2746
|
--description "Senior B2B SaaS account executive; concise, technical" \\
|
|
2685
2747
|
--count 1 --json | jq -r '.items[0].alias')
|
|
2686
|
-
CTOS=$(ish
|
|
2748
|
+
CTOS=$(ish person generate \\
|
|
2687
2749
|
--description "Skeptical CTO at Series B SaaS; distrusts AI vendors" \\
|
|
2688
2750
|
--count 3 --json | jq -r '[.items[].alias] | join(",")')
|
|
2689
2751
|
|
|
2690
2752
|
# One-shot study + iteration A (1×N broadcast does the rest):
|
|
2691
|
-
ish study create --modality chat --chat-mode
|
|
2753
|
+
ish study create --modality chat --chat-mode participant_pair \\
|
|
2692
2754
|
--name "Pitch rehearsal" \\
|
|
2693
|
-
--
|
|
2755
|
+
--group-a "$REP" --group-b "$CTOS" \\
|
|
2694
2756
|
--scenario-a "You are pitching <your product>. Be concise, push back on vague objections. Goal: land a pilot or a clear next step." \\
|
|
2695
2757
|
--scenario-b "You are a skeptical CTO. Probe for technical depth, distrust marketing-speak, refuse to commit without evidence. Goal: leave with either a concrete proof point or a graceful 'no'." \\
|
|
2696
2758
|
--assignment "Pitch:Land a pilot" --max-turns 14
|
|
@@ -2704,7 +2766,7 @@ ish iteration get <iter-id> --json \\
|
|
|
2704
2766
|
\`\`\`
|
|
2705
2767
|
|
|
2706
2768
|
Section 7b below has the longer version with scenario-writing
|
|
2707
|
-
guidance, criteria-driven
|
|
2769
|
+
guidance, criteria-driven groups, and the broadcast rule.
|
|
2708
2770
|
|
|
2709
2771
|
---
|
|
2710
2772
|
|
|
@@ -2804,7 +2866,7 @@ The renderer expands these tokens at request time:
|
|
|
2804
2866
|
- \`{{turn.role}}\` / \`{{turn.text}}\`: per-turn expansion. Place
|
|
2805
2867
|
one element with these tokens inside an array literal; the
|
|
2806
2868
|
renderer expands it to one entry per past turn.
|
|
2807
|
-
- \`{{
|
|
2869
|
+
- \`{{participant.name}}\` / \`{{participant.locale}}\`: persona attributes.
|
|
2808
2870
|
- \`{{conversation_id}}\`: bot-supplied session id (stateful mode).
|
|
2809
2871
|
- \`{{secret:KEY}}\`: workspace secret (see below).
|
|
2810
2872
|
|
|
@@ -2999,7 +3061,7 @@ cat ./bot-config.json | ish study create \\
|
|
|
2999
3061
|
--name "Sign-up Q1" --assignment "Sign up:Try to sign up"
|
|
3000
3062
|
\`\`\`
|
|
3001
3063
|
|
|
3002
|
-
Optional \`--max-turns <n>\` (default 12) caps the chat per
|
|
3064
|
+
Optional \`--max-turns <n>\` (default 12) caps the chat per participant.
|
|
3003
3065
|
|
|
3004
3066
|
Audience size is set at run time for **external_chatbot** chat
|
|
3005
3067
|
studies. Use \`--sample <N>\` to pick N random simulatable profiles,
|
|
@@ -3010,10 +3072,10 @@ ish study run stu-xyz --sample 5 --wait
|
|
|
3010
3072
|
\`\`\`
|
|
3011
3073
|
|
|
3012
3074
|
> **Pair-mode is different.** \`--sample\` / \`--profile\` / demographic
|
|
3013
|
-
> filters on \`study run\` are **refused** for
|
|
3014
|
-
> — pair
|
|
3015
|
-
> iteration-create time via \`--
|
|
3016
|
-
> or \`--role-criteria-a/-b\`. See the
|
|
3075
|
+
> filters on \`study run\` are **refused** for participant_pair iterations
|
|
3076
|
+
> — pair groups live on the iteration itself. Set them at
|
|
3077
|
+
> iteration-create time via \`--group-a/-b\` (with 1×N broadcast)
|
|
3078
|
+
> or \`--role-criteria-a/-b\`. See the participant_pair section below.
|
|
3017
3079
|
|
|
3018
3080
|
Pull raw interactions:
|
|
3019
3081
|
\`\`\`
|
|
@@ -3036,23 +3098,23 @@ ish iteration create --study stu-xyz --endpoint-config ./bot.json
|
|
|
3036
3098
|
|
|
3037
3099
|
Same flag set as \`study create\`'s chat shortcut.
|
|
3038
3100
|
|
|
3039
|
-
##
|
|
3101
|
+
## participant_pair: rehearse a conversation between two AI personas
|
|
3040
3102
|
|
|
3041
|
-
\`Modality.CHAT\` also supports a **
|
|
3042
|
-
|
|
3103
|
+
\`Modality.CHAT\` also supports a **participant_pair** mode where two AI
|
|
3104
|
+
people converse with each other — useful for rehearsing a
|
|
3043
3105
|
sales pitch, a difficult conversation, a fundraising chat, or any
|
|
3044
3106
|
two-role scenario. Each side has its own scenario + goal text; the
|
|
3045
3107
|
other side does NOT see it (the asymmetry contract). Audiences are
|
|
3046
|
-
1:1 paired by index (
|
|
3108
|
+
1:1 paired by index (group_a[i] talks to group_b[i]).
|
|
3047
3109
|
|
|
3048
3110
|
One-shot study + iteration:
|
|
3049
3111
|
|
|
3050
3112
|
\`\`\`
|
|
3051
3113
|
ish study create \\
|
|
3052
|
-
--modality chat --chat-mode
|
|
3114
|
+
--modality chat --chat-mode participant_pair \\
|
|
3053
3115
|
--name "Pitch rehearsal" \\
|
|
3054
|
-
--
|
|
3055
|
-
--
|
|
3116
|
+
--group-a p-sales-1,p-sales-2 \\
|
|
3117
|
+
--group-b p-cto-skeptic-1,p-cto-skeptic-2 \\
|
|
3056
3118
|
--scenario-a @./sales_rep.md \\
|
|
3057
3119
|
--scenario-b @./skeptical_cto.md \\
|
|
3058
3120
|
--assignment "Pitch:Try to win the meeting"
|
|
@@ -3061,8 +3123,8 @@ ish study create \\
|
|
|
3061
3123
|
Or add a pair iteration to an existing chat study:
|
|
3062
3124
|
|
|
3063
3125
|
\`\`\`
|
|
3064
|
-
ish iteration create --study stu-xyz --chat-mode
|
|
3065
|
-
--
|
|
3126
|
+
ish iteration create --study stu-xyz --chat-mode participant_pair \\
|
|
3127
|
+
--group-a p-a1,p-a2 --group-b p-b1,p-b2 \\
|
|
3066
3128
|
--scenario-a "..." --scenario-b "..." \\
|
|
3067
3129
|
--max-turns 14
|
|
3068
3130
|
\`\`\`
|
|
@@ -3070,23 +3132,23 @@ ish iteration create --study stu-xyz --chat-mode tester_pair \\
|
|
|
3070
3132
|
### Rehearsing against N variations of one side (1×N)
|
|
3071
3133
|
|
|
3072
3134
|
The most common rehearsal shape: fix one side (your role) and vary
|
|
3073
|
-
the other (the
|
|
3135
|
+
the other (the people you're rehearsing against). E.g. "pitch this
|
|
3074
3136
|
once and see how it lands against 3 different skeptical CTOs."
|
|
3075
3137
|
|
|
3076
3138
|
Step 1 — produce N distinct profiles for the varying side:
|
|
3077
3139
|
|
|
3078
3140
|
\`\`\`bash
|
|
3079
3141
|
# Generate 3 skeptical-CTO profiles (or any archetype):
|
|
3080
|
-
ish
|
|
3142
|
+
ish person generate \\
|
|
3081
3143
|
--description "Skeptical CTO at a Series B SaaS startup; distrusts AI vendors" \\
|
|
3082
3144
|
--count 3 --json | jq -r '.items[].alias'
|
|
3083
|
-
# →
|
|
3145
|
+
# → p-cto1, p-cto2, p-cto3
|
|
3084
3146
|
\`\`\`
|
|
3085
3147
|
|
|
3086
3148
|
If you already have profiles you want to reuse, list them:
|
|
3087
3149
|
|
|
3088
3150
|
\`\`\`bash
|
|
3089
|
-
ish
|
|
3151
|
+
ish person list --search "cto" --json | jq -r '.items[].alias'
|
|
3090
3152
|
\`\`\`
|
|
3091
3153
|
|
|
3092
3154
|
Step 2 — author the two scenarios as separate files (\`sales_rep.md\`
|
|
@@ -3101,21 +3163,21 @@ template.
|
|
|
3101
3163
|
Step 3 — create the iteration with **one profile** on the fixed
|
|
3102
3164
|
side and **N profiles** on the varying side. The CLI auto-broadcasts
|
|
3103
3165
|
the singleton to match length N (and prints a stderr notice like
|
|
3104
|
-
\`Broadcasting --
|
|
3166
|
+
\`Broadcasting --group-a (1 profile) to length 3 to match --group-b\`
|
|
3105
3167
|
when it does, so you can see it happen):
|
|
3106
3168
|
|
|
3107
3169
|
\`\`\`bash
|
|
3108
3170
|
ish study create \\
|
|
3109
|
-
--modality chat --chat-mode
|
|
3171
|
+
--modality chat --chat-mode participant_pair \\
|
|
3110
3172
|
--name "Pitch rehearsal — 3 CTO variants" \\
|
|
3111
|
-
--
|
|
3112
|
-
--
|
|
3173
|
+
--group-a p-rep \\
|
|
3174
|
+
--group-b p-cto1,p-cto2,p-cto3 \\
|
|
3113
3175
|
--scenario-a @./sales_rep.md \\
|
|
3114
3176
|
--scenario-b @./skeptical_cto.md \\
|
|
3115
3177
|
--assignment "Pitch:Land a pilot or a clear next step"
|
|
3116
3178
|
|
|
3117
|
-
# Result: 3 conversations, all using
|
|
3118
|
-
# of
|
|
3179
|
+
# Result: 3 conversations, all using p-rep on side A, one each
|
|
3180
|
+
# of p-cto1/2/3 on side B. Same scenario for the CTOs (they share
|
|
3119
3181
|
# the role description) but different underlying personas, so the
|
|
3120
3182
|
# conversations diverge in tone and pressure points.
|
|
3121
3183
|
\`\`\`
|
|
@@ -3136,22 +3198,22 @@ ish iteration get <iter-id> --json \\
|
|
|
3136
3198
|
**When to use criteria instead**: if you don't care about specific
|
|
3137
3199
|
profile IDs and just want "any 3 CTOs the backend can find", pass
|
|
3138
3200
|
\`--role-criteria-b '{"occupation":["cto"]}'\` (alone or with a single
|
|
3139
|
-
\`--
|
|
3201
|
+
\`--group-a p-rep\`). The backend resolves the matching pool at
|
|
3140
3202
|
iteration-create time. Caveat: the resolved pool may collapse onto
|
|
3141
3203
|
similar personas — for guaranteed distinctness, generate explicit
|
|
3142
3204
|
profiles first.
|
|
3143
3205
|
|
|
3144
|
-
### Criteria-driven
|
|
3206
|
+
### Criteria-driven group (persona-first filtering)
|
|
3145
3207
|
|
|
3146
3208
|
When you don't want to hand-pick UUIDs, pass a **role-criteria
|
|
3147
3209
|
filter** per side. The backend resolves it into an eligible pool of
|
|
3148
|
-
|
|
3210
|
+
people and pairs them 1:1. The persona itself is never
|
|
3149
3211
|
altered — criteria filter the pool upstream so the persona is
|
|
3150
3212
|
already plausible for the role:
|
|
3151
3213
|
|
|
3152
3214
|
\`\`\`
|
|
3153
3215
|
ish study create \\
|
|
3154
|
-
--modality chat --chat-mode
|
|
3216
|
+
--modality chat --chat-mode participant_pair \\
|
|
3155
3217
|
--name "Pitch rehearsal" \\
|
|
3156
3218
|
--role-criteria-a '{"occupation":["sales","account executive"],"min_age":28}' \\
|
|
3157
3219
|
--role-criteria-b '{"occupation":["cto","vp engineering"],"country":["US","SE"]}' \\
|
|
@@ -3172,23 +3234,23 @@ MECE notes for the list filters:
|
|
|
3172
3234
|
- \`household_in\`: \`couple_with_kids\` covers couples raising children;
|
|
3173
3235
|
\`couple_no_kids\` is strictly child-free. \`single\` means lives alone
|
|
3174
3236
|
(no partner, no roommates, no parents, no children in the household).
|
|
3175
|
-
- \`employment_status_in\`: pick the
|
|
3237
|
+
- \`employment_status_in\`: pick the participant's primary daytime activity.
|
|
3176
3238
|
A student who works 15 hrs/week is \`student\`; a retiree who freelances
|
|
3177
3239
|
is \`retired\`.
|
|
3178
3240
|
|
|
3179
3241
|
If the resolved pool is too small, \`ish study run\` exits 2 with the
|
|
3180
3242
|
backend's error message intact — no silent fallback. Broaden the
|
|
3181
3243
|
criteria or generate more matching profiles via
|
|
3182
|
-
\`ish
|
|
3244
|
+
\`ish person generate --description "..."\`.
|
|
3183
3245
|
|
|
3184
3246
|
Dispatch is per-Conversation (one task per pair index). Run-time
|
|
3185
|
-
|
|
3186
|
-
filters) are refused on pair iterations — the iteration's
|
|
3247
|
+
people overrides (\`--profile\`, \`--sample\`, \`--all\`, demographic
|
|
3248
|
+
filters) are refused on pair iterations — the iteration's groups
|
|
3187
3249
|
are authoritative. To change them, update the iteration:
|
|
3188
3250
|
|
|
3189
3251
|
\`\`\`
|
|
3190
3252
|
ish study run --study stu-xyz --iteration i-pair -y
|
|
3191
|
-
ish iteration update i-pair --details-json '{...}' # change
|
|
3253
|
+
ish iteration update i-pair --details-json '{...}' # change groups
|
|
3192
3254
|
\`\`\`
|
|
3193
3255
|
|
|
3194
3256
|
Inspect:
|
|
@@ -3198,8 +3260,8 @@ ish iteration get i-pair --json | jq '.details.mode_details.mode, .conversations
|
|
|
3198
3260
|
\`\`\`
|
|
3199
3261
|
|
|
3200
3262
|
Per-Conversation summaries (\`end_reason\`, \`dominant_dynamic\`,
|
|
3201
|
-
\`who_steered\`) land on \`iteration.conversations[]\`. Per-
|
|
3202
|
-
summaries land on \`
|
|
3263
|
+
\`who_steered\`) land on \`iteration.conversations[]\`. Per-participant
|
|
3264
|
+
summaries land on \`participant.summary\` as before.
|
|
3203
3265
|
|
|
3204
3266
|
## Active-endpoint convention
|
|
3205
3267
|
|
|
@@ -3234,7 +3296,7 @@ Mirrors \`workspace use\` / \`study use\` / \`ask use\`.
|
|
|
3234
3296
|
- \`concepts/iteration\` — chat iteration shape
|
|
3235
3297
|
(\`details.mode_details\` discriminator, \`mode_details.endpoint\` /
|
|
3236
3298
|
\`mode_details.chatbot_endpoint_id\` for external_chatbot,
|
|
3237
|
-
\`mode_details.
|
|
3299
|
+
\`mode_details.group_a/_b\` + \`scenario_a/_b\` for participant_pair,
|
|
3238
3300
|
\`details.max_turns\`).
|
|
3239
3301
|
- \`concepts/study\` — modality + assignments + iteration nesting.
|
|
3240
3302
|
- \`reference/json-mode\` — JSON output, error envelope, exit codes.
|
|
@@ -3296,13 +3358,13 @@ ish workspace list --json
|
|
|
3296
3358
|
"id": "...", "alias": "w-6ec", "name": "Onboarding revamp",
|
|
3297
3359
|
"base_url": "https://example.com",
|
|
3298
3360
|
"last_activity_at": "2026-05-10T14:22:00Z",
|
|
3299
|
-
"child_counts": { "studies": 2, "asks": 1, "
|
|
3361
|
+
"child_counts": { "studies": 2, "asks": 1, "persons": 4 },
|
|
3300
3362
|
"has_headroom": true
|
|
3301
3363
|
},
|
|
3302
3364
|
{
|
|
3303
3365
|
"id": "...", "alias": "w-d02", "name": "Demo",
|
|
3304
3366
|
"last_activity_at": "2025-11-02T09:11:00Z",
|
|
3305
|
-
"child_counts": { "studies": 3, "asks": 0, "
|
|
3367
|
+
"child_counts": { "studies": 3, "asks": 0, "persons": 0 },
|
|
3306
3368
|
"has_headroom": false
|
|
3307
3369
|
}
|
|
3308
3370
|
],
|
|
@@ -3315,14 +3377,14 @@ Read three fields per row:
|
|
|
3315
3377
|
- **\`last_activity_at\`** — most recent run, iteration, ask, or write
|
|
3316
3378
|
on this workspace. The most recently active one is usually the
|
|
3317
3379
|
workspace the user is mentally already in.
|
|
3318
|
-
- **\`child_counts\`** — \`{ studies, asks,
|
|
3380
|
+
- **\`child_counts\`** — \`{ studies, asks, persons }\`. Zero
|
|
3319
3381
|
across the board = quiet/empty, ideal reuse target without
|
|
3320
3382
|
cluttering anyone's view. A workspace with content the user owns is
|
|
3321
3383
|
also fine to reuse if there's still headroom.
|
|
3322
3384
|
- **\`has_headroom\`** — \`true\` if the workspace still has room under
|
|
3323
3385
|
\`maxStudiesPerProduct\`, \`maxIterationsPerStudy\`, and
|
|
3324
|
-
\`
|
|
3325
|
-
next \`study create\` / \`
|
|
3386
|
+
\`maxCustomPersons\` for the caller's tier. If \`false\`, the
|
|
3387
|
+
next \`study create\` / \`person generate\` against this workspace
|
|
3326
3388
|
will be \`usage_limit_reached\`. Filter these out unless the user
|
|
3327
3389
|
explicitly wants to free space by deleting state.
|
|
3328
3390
|
|
|
@@ -3381,11 +3443,11 @@ ish workspace list --json --fields alias,name,last_activity_at,child_counts,has_
|
|
|
3381
3443
|
# [
|
|
3382
3444
|
# {"alias":"w-6ec","name":"Onboarding revamp",
|
|
3383
3445
|
# "last_activity_at":"2026-05-10T14:22:00Z",
|
|
3384
|
-
# "child_counts":{"studies":2,"asks":1,"
|
|
3446
|
+
# "child_counts":{"studies":2,"asks":1,"persons":4},
|
|
3385
3447
|
# "has_headroom":true},
|
|
3386
3448
|
# {"alias":"w-d02","name":"Demo",
|
|
3387
3449
|
# "last_activity_at":"2025-11-02T09:11:00Z",
|
|
3388
|
-
# "child_counts":{"studies":3,"asks":0,"
|
|
3450
|
+
# "child_counts":{"studies":3,"asks":0,"persons":0},
|
|
3389
3451
|
# "has_headroom":false},
|
|
3390
3452
|
# ...
|
|
3391
3453
|
# ]
|
|
@@ -3394,7 +3456,7 @@ ish workspace list --json --fields alias,name,last_activity_at,child_counts,has_
|
|
|
3394
3456
|
ish workspace use w-6ec
|
|
3395
3457
|
|
|
3396
3458
|
# 3. Carry on as if the workspace_create had succeeded.
|
|
3397
|
-
ish
|
|
3459
|
+
ish person generate --description "..." --count 3
|
|
3398
3460
|
ish study create --modality interactive --name "..." \\
|
|
3399
3461
|
--url https://example.com \\
|
|
3400
3462
|
--assignment "..." --question "..."
|
|
@@ -3437,24 +3499,24 @@ without a second round-trip.
|
|
|
3437
3499
|
- \`reference/json-mode\` — error envelope shape and exit code mapping
|
|
3438
3500
|
(\`usage_limit_reached\` is HTTP 403, exit 1, non-retryable).
|
|
3439
3501
|
`;
|
|
3440
|
-
const
|
|
3502
|
+
const GUIDE_BUILD_SPECIFIC_PERSON = `# guide: build a specific simulated person from notes
|
|
3441
3503
|
|
|
3442
|
-
\`
|
|
3504
|
+
\`person generate\` is the right tool for *groups* (many profiles
|
|
3443
3505
|
from a description or interview sources). When you want **one specific
|
|
3444
|
-
|
|
3506
|
+
person** — modelling a real prospect, rebuilding a persona from a
|
|
3445
3507
|
single interview, or simulating a named stakeholder for a pitch
|
|
3446
3508
|
rehearsal — use the iterative probe loop:
|
|
3447
3509
|
|
|
3448
|
-
1. \`ish
|
|
3510
|
+
1. \`ish person suggest-scenarios\` — describe what you already
|
|
3449
3511
|
know; the LLM returns 1–10 scenario probes designed to expose what
|
|
3450
3512
|
you don't.
|
|
3451
3513
|
2. Answer the probes locally (in chat, with the user, or from
|
|
3452
3514
|
transcripts).
|
|
3453
|
-
3. \`ish
|
|
3454
|
-
4. \`ish
|
|
3515
|
+
3. \`ish person create --file ...\` — save the profile shell.
|
|
3516
|
+
4. \`ish person evidence add <id>\` — persist the answered probes
|
|
3455
3517
|
as structured evidence on the profile so they survive into runtime
|
|
3456
3518
|
persona injection.
|
|
3457
|
-
5. \`ish
|
|
3519
|
+
5. \`ish person evidence list <id>\` — read back what's saved,
|
|
3458
3520
|
newest first. Useful for verifying a session or branching on prior
|
|
3459
3521
|
state before the next probe round.
|
|
3460
3522
|
|
|
@@ -3467,7 +3529,7 @@ to surface a different facet of the persona:
|
|
|
3467
3529
|
X; which option fits?" Multiple-choice, lets the persona pick
|
|
3468
3530
|
behavior.
|
|
3469
3531
|
- \`voice\` — \`{situation, options[2..4]}\`: same shape as situation
|
|
3470
|
-
but framed around tone/phrasing the
|
|
3532
|
+
but framed around tone/phrasing the participant would actually use.
|
|
3471
3533
|
- \`binary\` — \`{description, option_a, option_b}\`: forced choice
|
|
3472
3534
|
between two competing values or trade-offs.
|
|
3473
3535
|
- \`micro-story\` — \`{prompt}\`: open-ended; the persona narrates a
|
|
@@ -3490,7 +3552,7 @@ probe, copy the scenario's \`type\` straight into the trace's
|
|
|
3490
3552
|
|
|
3491
3553
|
\`\`\`
|
|
3492
3554
|
# 1. Suggest 5 probes from a context blob
|
|
3493
|
-
ish
|
|
3555
|
+
ish person suggest-scenarios \\
|
|
3494
3556
|
--context "Staff platform engineer at a Stripe-using fintech. \\
|
|
3495
3557
|
Owns on-call for the payments edge. Burned by a Black Friday \\
|
|
3496
3558
|
outage last year." \\
|
|
@@ -3508,16 +3570,16 @@ ish profile suggest-scenarios \\
|
|
|
3508
3570
|
# ]
|
|
3509
3571
|
|
|
3510
3572
|
# 3. Create the profile shell
|
|
3511
|
-
ish
|
|
3512
|
-
# →
|
|
3573
|
+
ish person create --file ./persona.json
|
|
3574
|
+
# → p-d4e
|
|
3513
3575
|
|
|
3514
3576
|
# 4. Persist the answered probes as evidence
|
|
3515
|
-
ish
|
|
3577
|
+
ish person evidence add p-d4e --traces-file ./answers.json
|
|
3516
3578
|
# → {items: [{id, text, source, scenario_prompt, created_at}, ...], total: N}
|
|
3517
3579
|
|
|
3518
3580
|
# 5. Read back what got saved (also useful before the next probe round)
|
|
3519
|
-
ish
|
|
3520
|
-
ish
|
|
3581
|
+
ish person evidence list p-d4e
|
|
3582
|
+
ish person evidence list p-d4e --get source # one source per line
|
|
3521
3583
|
\`\`\`
|
|
3522
3584
|
|
|
3523
3585
|
## Iterating the probe loop
|
|
@@ -3526,7 +3588,7 @@ To go deeper on a follow-up pass, feed the prior round back in so the
|
|
|
3526
3588
|
LLM doesn't paraphrase what you already asked:
|
|
3527
3589
|
|
|
3528
3590
|
\`\`\`
|
|
3529
|
-
ish
|
|
3591
|
+
ish person suggest-scenarios \\
|
|
3530
3592
|
--context-file ./notes.md \\
|
|
3531
3593
|
--count 3 \\
|
|
3532
3594
|
--already-surfaced '["PagerDuty fires at 02:00 on payments edge."]' \\
|
|
@@ -3542,17 +3604,123 @@ cap at 40 entries.
|
|
|
3542
3604
|
|
|
3543
3605
|
| Need | Command |
|
|
3544
3606
|
|---|---|
|
|
3545
|
-
| Many profiles from a description or interview | \`ish
|
|
3546
|
-
| One specific persona, iterative probe loop | \`ish
|
|
3547
|
-
| Exact profile from a JSON spec, no LLM | \`ish
|
|
3607
|
+
| Many profiles from a description or interview | \`ish person generate\` |
|
|
3608
|
+
| One specific persona, iterative probe loop | \`ish person suggest-scenarios\` + \`evidence add\`/\`list\` |
|
|
3609
|
+
| Exact profile from a JSON spec, no LLM | \`ish person create --file\` |
|
|
3548
3610
|
|
|
3549
3611
|
## Related
|
|
3550
3612
|
|
|
3551
|
-
- \`concepts/
|
|
3613
|
+
- \`concepts/person\` — what a person is; structured fields.
|
|
3552
3614
|
- \`concepts/source\` — interview transcripts / audio / PDF inputs
|
|
3553
|
-
for the
|
|
3615
|
+
for the people-generation flow.
|
|
3554
3616
|
- \`reference/aliases\` — \`tp-…\` is the profile alias prefix.
|
|
3555
3617
|
`;
|
|
3618
|
+
const GUIDE_MCP_ADD = `# guide: wire ish into your AI clients (\`ish mcp add\`)
|
|
3619
|
+
|
|
3620
|
+
The hosted ish MCP server lets agents inside Cursor, VS Code, Claude
|
|
3621
|
+
Code, Claude Desktop, and Windsurf call ish operations (study
|
|
3622
|
+
run, ask run, person generate, …) directly. \`ish mcp add\` writes the
|
|
3623
|
+
per-client config block so each client knows where to find the
|
|
3624
|
+
server. OAuth is handled by the server itself on first connect — no
|
|
3625
|
+
token is written to your client config file.
|
|
3626
|
+
|
|
3627
|
+
## When to run this
|
|
3628
|
+
|
|
3629
|
+
- You've installed the ish CLI and want your editor/desktop agent to
|
|
3630
|
+
drive ish without you copy-pasting JSON into a config file.
|
|
3631
|
+
- You're switching machines and want to re-wire every client in one
|
|
3632
|
+
command.
|
|
3633
|
+
- You changed the server URL (\`ISH_MCP_URL\`) and want every client
|
|
3634
|
+
re-pointed at the new endpoint.
|
|
3635
|
+
|
|
3636
|
+
## Verbs
|
|
3637
|
+
|
|
3638
|
+
\`\`\`bash
|
|
3639
|
+
ish mcp list # read-only: which clients are detected + status
|
|
3640
|
+
ish mcp add # dry-run plan + next-step hint (default)
|
|
3641
|
+
ish mcp add --all --yes # wire every detected client
|
|
3642
|
+
ish mcp add --client cursor --yes # wire one specific client
|
|
3643
|
+
ish mcp remove --client cursor --yes
|
|
3644
|
+
\`\`\`
|
|
3645
|
+
|
|
3646
|
+
## Supported clients
|
|
3647
|
+
|
|
3648
|
+
| Client | Config path | Server key |
|
|
3649
|
+
|-----------------|-----------------------------------------------------------------------------------|--------------------|
|
|
3650
|
+
| cursor | \`~/.cursor/mcp.json\` | \`mcpServers\` |
|
|
3651
|
+
| vscode | \`~/Library/Application Support/Code/User/mcp.json\` (macOS), \`~/.config/Code/User/mcp.json\` (Linux), \`%APPDATA%\\Code\\User\\mcp.json\` (Windows) | \`servers\` |
|
|
3652
|
+
| claude-code | \`~/.claude.json\` (user scope) | \`mcpServers\` |
|
|
3653
|
+
| claude-desktop | \`~/Library/Application Support/Claude/claude_desktop_config.json\` (macOS), \`%APPDATA%\\Claude\\claude_desktop_config.json\` (Windows). Not available on Linux. | \`mcpServers\` |
|
|
3654
|
+
| windsurf | \`~/.codeium/windsurf/mcp_config.json\` | \`mcpServers\` |
|
|
3655
|
+
|
|
3656
|
+
Detection is by per-client config-dir existence — if the dir is
|
|
3657
|
+
missing, the client is reported \`detected: false\`. You can still wire
|
|
3658
|
+
that client explicitly via \`--client <name>\` (the dir is created on
|
|
3659
|
+
write).
|
|
3660
|
+
|
|
3661
|
+
## Conventions
|
|
3662
|
+
|
|
3663
|
+
- **Atomic writes.** A tmp file is renamed into place; partially-written
|
|
3664
|
+
configs never appear.
|
|
3665
|
+
- **Idempotent.** Re-running \`mcp add\` is a no-op when the ish block
|
|
3666
|
+
already matches the expected shape.
|
|
3667
|
+
- **Preserves unrelated keys.** Other MCP servers in the same config
|
|
3668
|
+
file, and unrelated top-level settings, are kept verbatim.
|
|
3669
|
+
- **No tokens written.** The hosted MCP server handles OAuth on first
|
|
3670
|
+
connect; only the URL goes into the client config file.
|
|
3671
|
+
- **Drift refusal.** If the existing ish block in a client config has
|
|
3672
|
+
a different URL/shape than ours, \`mcp add\` exits 2 unless
|
|
3673
|
+
\`--force\` is passed.
|
|
3674
|
+
|
|
3675
|
+
## Flags
|
|
3676
|
+
|
|
3677
|
+
| Flag | Effect |
|
|
3678
|
+
|-------------------|-----------------------------------------------------------------------------------------|
|
|
3679
|
+
| \`--client a,b\` | Comma-separated and/or repeatable client keys. Mutually exclusive with \`--all\`. |
|
|
3680
|
+
| \`--all\` | Apply to every *detected* client on this OS. |
|
|
3681
|
+
| \`--dry-run\` | Print the planned mutations as JSON; write nothing. |
|
|
3682
|
+
| \`--force\` | Overwrite an existing ish block that has drifted. |
|
|
3683
|
+
| \`-y, --yes\` | Confirm writes. Required when stdout is piped or \`--json\` is set. |
|
|
3684
|
+
|
|
3685
|
+
## JSON output
|
|
3686
|
+
|
|
3687
|
+
\`\`\`json
|
|
3688
|
+
{
|
|
3689
|
+
"ok": true,
|
|
3690
|
+
"server_url": "https://mcp.ishlabs.io/mcp",
|
|
3691
|
+
"dry_run": true,
|
|
3692
|
+
"plan": [
|
|
3693
|
+
{
|
|
3694
|
+
"client": "cursor",
|
|
3695
|
+
"display_name": "Cursor",
|
|
3696
|
+
"config_path": "/Users/me/.cursor/mcp.json",
|
|
3697
|
+
"action": "create",
|
|
3698
|
+
"expected": { "url": "https://mcp.ishlabs.io/mcp" }
|
|
3699
|
+
}
|
|
3700
|
+
],
|
|
3701
|
+
"hint": "Re-run with \`ish mcp add --all --yes\` to commit these writes."
|
|
3702
|
+
}
|
|
3703
|
+
\`\`\`
|
|
3704
|
+
|
|
3705
|
+
\`action\` is one of: \`create\` (no client config file yet),
|
|
3706
|
+
\`update\` (file exists, ish block being added or overwritten),
|
|
3707
|
+
\`skip\` (already up to date), \`refuse-drift\` (different block exists,
|
|
3708
|
+
re-run with \`--force\`), \`remove\` / \`remove-noop\` (for \`mcp remove\`).
|
|
3709
|
+
|
|
3710
|
+
## Overriding the server URL
|
|
3711
|
+
|
|
3712
|
+
\`\`\`bash
|
|
3713
|
+
ISH_MCP_URL=http://localhost:8000/mcp ish mcp add --client cursor --yes
|
|
3714
|
+
\`\`\`
|
|
3715
|
+
|
|
3716
|
+
Useful for the dev backend or a hosted preview environment. The same
|
|
3717
|
+
env var is read by \`mcp list\` so the reported status reflects the
|
|
3718
|
+
overridden URL.
|
|
3719
|
+
|
|
3720
|
+
## Related
|
|
3721
|
+
|
|
3722
|
+
- \`reference/json-mode\` — display vs capture vs chain output rules.
|
|
3723
|
+
`;
|
|
3556
3724
|
const PAGES = [
|
|
3557
3725
|
{
|
|
3558
3726
|
slug: "overview",
|
|
@@ -3575,13 +3743,13 @@ const PAGES = [
|
|
|
3575
3743
|
{
|
|
3576
3744
|
slug: "concepts/iteration",
|
|
3577
3745
|
title: "concept: iteration",
|
|
3578
|
-
description: "One configured run of a study (URL, media, or chat). Covers segments, segment labels, HTML content, and chat mode_details (external_chatbot vs
|
|
3746
|
+
description: "One configured run of a study (URL, media, or chat). Covers segments, segment labels, HTML content, and chat mode_details (external_chatbot vs participant_pair).",
|
|
3579
3747
|
body: CONCEPT_ITERATION,
|
|
3580
3748
|
},
|
|
3581
3749
|
{
|
|
3582
3750
|
slug: "concepts/assignment",
|
|
3583
3751
|
title: "concept: assignment",
|
|
3584
|
-
description: "A single task a
|
|
3752
|
+
description: "A single task a participant performs; CLI input formats.",
|
|
3585
3753
|
body: CONCEPT_ASSIGNMENT,
|
|
3586
3754
|
},
|
|
3587
3755
|
{
|
|
@@ -3603,9 +3771,9 @@ const PAGES = [
|
|
|
3603
3771
|
body: CONCEPT_ROUND,
|
|
3604
3772
|
},
|
|
3605
3773
|
{
|
|
3606
|
-
slug: "concepts/
|
|
3607
|
-
title: "concept:
|
|
3608
|
-
description: "Reusable
|
|
3774
|
+
slug: "concepts/person",
|
|
3775
|
+
title: "concept: person",
|
|
3776
|
+
description: "Reusable persona; generate vs manual create.",
|
|
3609
3777
|
body: CONCEPT_PROFILE,
|
|
3610
3778
|
},
|
|
3611
3779
|
{
|
|
@@ -3615,10 +3783,10 @@ const PAGES = [
|
|
|
3615
3783
|
body: CONCEPT_SOURCE,
|
|
3616
3784
|
},
|
|
3617
3785
|
{
|
|
3618
|
-
slug: "concepts/
|
|
3619
|
-
title: "concept:
|
|
3620
|
-
description: "
|
|
3621
|
-
body:
|
|
3786
|
+
slug: "concepts/people",
|
|
3787
|
+
title: "concept: people selection",
|
|
3788
|
+
description: "People-selection flags shared by study run and ask run --new.",
|
|
3789
|
+
body: CONCEPT_PEOPLE,
|
|
3622
3790
|
},
|
|
3623
3791
|
{
|
|
3624
3792
|
slug: "concepts/site-access",
|
|
@@ -3641,7 +3809,7 @@ const PAGES = [
|
|
|
3641
3809
|
{
|
|
3642
3810
|
slug: "concepts/extending-a-simulation",
|
|
3643
3811
|
title: "concept: extending a simulation (study extend)",
|
|
3644
|
-
description: "Resume a terminal
|
|
3812
|
+
description: "Resume a terminal participant with more steps and an optional mid-run instruction. Cancel + extend as a reversible stop/start pair.",
|
|
3645
3813
|
body: CONCEPT_EXTENDING_SIMULATION,
|
|
3646
3814
|
},
|
|
3647
3815
|
{
|
|
@@ -3683,13 +3851,13 @@ const PAGES = [
|
|
|
3683
3851
|
{
|
|
3684
3852
|
slug: "guides/first-study",
|
|
3685
3853
|
title: "guide: your first study, end to end",
|
|
3686
|
-
description: "Login → workspace →
|
|
3854
|
+
description: "Login → workspace → people → study → iteration → run → results.",
|
|
3687
3855
|
body: GUIDE_FIRST_STUDY,
|
|
3688
3856
|
},
|
|
3689
3857
|
{
|
|
3690
3858
|
slug: "guides/chat",
|
|
3691
3859
|
title: "guide: chat-modality studies",
|
|
3692
|
-
description: "Configure a chatbot endpoint (slots-only model), smoke test it, run a chat-modality study (external_chatbot mode). Also:
|
|
3860
|
+
description: "Configure a chatbot endpoint (slots-only model), smoke test it, run a chat-modality study (external_chatbot mode). Also: participant_pair mode — two AI personas talk to each other for rehearsal scenarios.",
|
|
3693
3861
|
body: GUIDE_CHAT,
|
|
3694
3862
|
},
|
|
3695
3863
|
{
|
|
@@ -3699,10 +3867,16 @@ const PAGES = [
|
|
|
3699
3867
|
body: GUIDE_COLD_START,
|
|
3700
3868
|
},
|
|
3701
3869
|
{
|
|
3702
|
-
slug: "guides/build-specific-
|
|
3703
|
-
title: "guide: build a specific simulated
|
|
3704
|
-
description: "Iterative probe loop for one specific persona:
|
|
3705
|
-
body:
|
|
3870
|
+
slug: "guides/build-specific-person",
|
|
3871
|
+
title: "guide: build a specific simulated person from notes",
|
|
3872
|
+
description: "Iterative probe loop for one specific persona: person suggest-scenarios returns LLM probes; answer them locally; person evidence add persists answers; person evidence list reads them back.",
|
|
3873
|
+
body: GUIDE_BUILD_SPECIFIC_PERSON,
|
|
3874
|
+
},
|
|
3875
|
+
{
|
|
3876
|
+
slug: "guides/mcp-add",
|
|
3877
|
+
title: "guide: wire ish into your AI clients (`ish mcp add`)",
|
|
3878
|
+
description: "One command wires the hosted ish MCP server into Cursor, VS Code, Claude Code, Claude Desktop, and Windsurf. Idempotent, atomic, preserves unrelated keys, no tokens written.",
|
|
3879
|
+
body: GUIDE_MCP_ADD,
|
|
3706
3880
|
},
|
|
3707
3881
|
];
|
|
3708
3882
|
const PAGES_BY_SLUG = new Map(PAGES.map((p) => [p.slug, p]));
|