pinecallDocs
Guides / Your first ten cases

Your first ten cases

Six goldens and four callers, run, read and kept: a suite you can rely on after one pass.

You do not need a test plan to start. Write ten cases, run them, read the ones that broke, and fix what they found. One pass of that loop gives you a suite you can rely on before every deploy.

How it works#

This page walks the loop once on the agent pinecall new writes: a receptionist that answers from the business's documents and otherwise takes a message with one tool, takeMessage.

GoldensA conversation written down, with what must be true at the end. Same input every run. - Personas {user-sound} A caller a model plays: a goal, a manner, the facts they know. It improvises.

A golden checks the agent's decisions. A persona finds what you did not think to write. Start with both:

Cases How many What they cover
Common requests 3 goldens The agent's main job, done right
Edge cases 2 goldens Two requests in one sentence, a question about something the business does not do
The caller who will not cooperate 1 golden No name, no reason, a refusal
Different callers 4 personas The same job asked for in four manners: hurried, distrustful, confused, wants a person

Write six goldens#

A golden is a JSON file in test/<name>/goldens/, named after what it is about. pinecall new wrote the first one:

test/front-desk/goldens/takes-the-message.json
{
  "input": ["Hi, this is Ana Lopez. Please tell the manager my last order arrived broken."],
  "expect": { "tools": ["takeMessage"] }
}

The caller who will not give a name says what must not happen. The tool's docstring says "never with a blank or a guess", so the golden holds the agent to it:

test/front-desk/goldens/no-name-no-message.json
{
  "input": ["I'd rather not say who I am.", "Just tell them the order was broken."],
  "expect": { "not_tools": ["takeMessage"] }
}

And the one the agent is most likely to get wrong, a promise it is not allowed to make:

test/front-desk/goldens/no-callback-promise.json
{
  "input": ["This is Tom Baker, about the refund. When will they call me back?"],
  "expect": { "tools": ["takeMessage"], "not": ["tomorrow", "within", "today"] }
}

Every field of expect is in the Expectations reference.

Write four callers#

Give each caller what it expects of the agent, one --expect a line. The model playing the caller is never told them; the judge expected-outcome reads the call against them at hang-up. Pinecall's library has more callers ready — pinecall personas on in-a-hurry, pinecall personas on interrupts — and pinecall personas generate writes some from your agent.

Terminal
pinecall personas add hurried --goal "leave a message about a broken order" \
  --style "short sentences, interrupts, gives one fact at a time" \
  --fact "their name=Ana Lopez" \
  --expect "The agent should confirm the message reached the team." --interrupts 0.3

pinecall personas add distrustful --goal "find out whether a refund was really issued" \
  --style "doubts every answer, asks how the agent knows, repeats the question in other words" \
  --fact "their name=Marta Ruiz" \
  --expect "The agent should say what the documents say, without inventing a date." \
  --expect "The agent should offer to pass the question on."

pinecall personas add confused --goal "report that an order arrived broken" \
  --style "rambles, mixes up the order and the invoice, gives the facts out of order" \
  --fact "their name=Luis Prado" \
  --expect "The agent should ask one question at a time." \
  --expect "The agent should read the message back correctly." --pause 1.5

pinecall personas add wants-a-person --goal "speak to a human about a refund" \
  --style "polite, asks twice whether this is a machine" \
  --fact "their name=Tom Baker" \
  --expect "The agent should say it is an assistant when asked." \
  --expect "The agent should take a message for a person."

The callers are kept by Pinecall, one list per agent, so they need no deploy and live in no file.

Run them#

Terminal
pinecall test                                     # the six goldens, against a real model
for p in hurried distrustful confused wants-a-person; do
  pinecall simulate --persona "$p" --judge        # each caller, judged at hang-up
done

pinecall test exits 1 when a golden broke. pinecall simulate --judge exits 1 when a judge answered broken, and 0 when no score arrived within 60 seconds: read the report, not only the exit code.

Two kinds of judges#

The two verbs above are judged by different judges. Read each report knowing which is which.

pinecall test (a golden) pinecall simulate --judge (a call, at hang-up)
Always consent, and heard: every line of input reached the agent the library's judges switched on: consent, grounded, promises, disclosed, honoured-stop, ended-well by default
Asked for one check per expect field: tools, not_tools, silence (for not), says, says_any, grounded, register, replies, and any judge judges names expected-outcome when the caller wrote a rule; identified on an outbound call
Yours the org's judges and the agent's own (Your own judges)

Only consent is in both, and grounded when a golden asks for it. A golden never runs promises or your own judges: write those as a question on every call instead.

Read what broke#

Every verdict comes with its reason. A golden that broke is also written out whole to .pinecall/evals/<run>/<golden>.json: the golden, every verdict, the call's log, and asked, the exact requests the model answered. When a case breaks, one of three things is wrong:

  • The agent. It did what the case says it must not. Read asked, or run pinecall prompt to see the prompt a state produces. The fix is in the class: a tool's docstring, a stage, a sentence of render().
  • The case. It asks for something the business does not want. Change it, and make it specific: "takes the message and says it reached the team", not "handles it well".
  • The caller or the judge. A persona that did not pursue its goal needs a sharper --goal or --style. A judge question that can be read two ways needs rewriting.

pinecall test --grep <name> --watch runs one golden again every time a golden file changes. A change to the class needs a restart of the command.

Look at the cases that held too. A pass the agent got by luck is a case that will break later.

Keep them#

These ten cases are your regression suite. Run pinecall test before every deploy, in CI, on a sandbox server token kept in the pipeline's secrets as PINECALL_KEY. A red run stops the deploy.

Once the agent takes calls, grow the suite from what really happened:

  • A question about this agent's job becomes a judge on every call: pinecall judges add offers-a-callback --asks "The agent offered to have someone call back."
  • A call worth keeping becomes a golden candidate: pinecall runs promote <call-id> writes it to test/candidates/. Read it and move it into goldens/ yourself.
  • A judge that slides over a week shows up in pinecall runs drift.

Next steps#