## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - The chat eval catalog runs real agents through chat journeys and
grades the saved results
> - Full cohort 4 (OpenCode with DeepSeek v4.1 flash, master f1aef0cb58)
failed 14 cases for eval reasons, not product reasons
> - Some phase captures read links before the reply rendered. Some
graders matched keywords in free-form replies. One locator was stale.
One fixture answer looked like a placeholder. Some checks could not say
what failed
> - This pull request grades those cases on saved state, setup literals
and rendered links, and it fixes the observers and fixtures
> - The benefit is that these cases fail only when the product fails,
and a failure names the part that broke
## Linked Issues or Issue Description
**What happened?**
In full chat cohort 4, valid agent behavior failed these cases:
case-history, goals.explain-purpose and workspaces.preview-service
(phase captures read before the reply rendered); routines.pause-owned
("If you want that one stopped too, use Pause on its task page" matched
a cancellation regex); budgets-costs.spend-breakdown ("unknown, not
$0.00" matched a stated-zero regex); steering.edit-queued-message
("change ... and drag it above" missed a keyword list);
connection-setup.choose-provider (the resumed page says "Finish with
Notion"); questions-and-approvals.necessary-question (the answer "Acorn
2a4f72ec4ae2-1" was saved as "Acorn"). Some checks could not say what
failed: onboarding.new-company compared the whole members response as
JSON and saved no after-state; agents.choose-primary threw one combined
error.
**Expected behavior**
Each case grades the feature contract on saved state, setup literals and
rendered or clicked links. A valid reworded reply passes. A broken
product still fails. A failure names the clause and its values.
**Steps to reproduce**
Grade the retained cohort 4 evidence for the cases above with the
earlier oracles.
Refs #21009, #21002, #21007
## What Changed
- Phase captures (case-history first phase, goals.explain-purpose
phases, preview-service query phase, ambiguous-task clarification,
edit-queued-message guidance) now wait for the saved reply to render
(`waitForTurnReplyLinks`) before they observe. When a saved reply yields
no rendered links, the observation records thread and anchor diagnostics
(`linkDiagnostics`) and logs them.
- routines.pause-owned: remove the reply keyword clause. Saved state at
the reply proves the run kept running, and the final state proves it
completed normally.
- budgets-costs.spend-breakdown: the unresolved record is graded
structurally. The reply must identify it (rendered link, identifier or
run id), and a chat run must have read its saved accounting (cost
summary, run record or unranged company report, bound by input digest).
"$0.00" in the reply is not matched.
- steering.edit-queued-message: grade the rendered task link in the
reply, the refused agent edit and reorder (403), and the unchanged queue
at the reply. Keyword lists are removed.
- connection-setup.choose-provider: the sign-in locator also accepts
"Finish with Notion" (the resumed-draft label). The button has no test
id or other stable name.
- questions-and-approvals.necessary-question: the answer is now
"Quillmere". The case also accepts the exact answer in a saved
deliverable (a document revision or text attachment on the linked task,
saved by an agent of the company after the answer). The yaml expected
outcome records this decision, with a `changedFields` entry in the
testops manifest.
- onboarding.new-company: save the members after-state and compare
identity, role, status and grants, and name what changed. Guidance is
graded on a rendered link to `/onboarding` (the new-organization
wizard), the business literal from the query, and the saved company
count. The source company and person get realistic names for this case
and are restored at cleanup, because the CEO reasoned from the generated
names that the request was a graded test.
- agents.choose-primary: the settled-Chat check is split into four named
clauses with values, saved as evidence on failure.
- A question card the turn ended on counts as its reply (decision after
cohort 4) in cli-operations.owned-dev-instance, onboarding.new-company
and status-cards.other-authors-card. The card's Markdown-rendered fields
(question form description; confirmation prompt and details) count as
reply links. Link requirements still apply.
- Every changed case bumps its `oracleVersion`; the descriptor snapshot
and inventory digest are updated.
Why each old check was wrong and why the new one still catches a broken
product:
| Case | Old check was wrong because | A broken product still fails
because |
|---|---|---|
| phase captures | runs succeeded and the chat waited before the reply
rendered, so links were empty | the same link checks run on the rendered
reply |
| routines.pause-owned | a correct offer to pause the run read as a
cancellation | the checkpoint run must be running and the run must later
succeed |
| spend-breakdown | "unknown, not $0.00" read as "$0" | the reply must
identify the record and a chat run must read its saved accounting;
totals and per-agent amounts stay graded |
| edit-queued-message | correct wording missed a keyword list | the
reply must link the task, the agent's edit and reorder must be refused,
and the queue must be unchanged at the reply |
| choose-provider | the resumed page label was not accepted | the button
must still be visible on the selected Notion surface |
| necessary-question | the answer read as a placeholder; deliverables
did not count | the exact answer must be saved in the linked work after
the answer, by an agent of the company |
| new-company | timestamps and profiles made the JSON compare fail
without saying why | membership identity, role, status and grants must
be unchanged |
| choose-primary | one combined error | the same four conditions, each
named |
| card as reply | a card answer read as an empty reply | card text must
exist and link requirements still apply |
## Verification
- Calibration tests (valid, reworded valid, broken) for each changed
grader: `integrator-routine-pause.test.ts`,
`identity-accounting.test.ts` (includes the cohort 4 reply),
`runtime-queue-controls.test.ts` (includes the cohort 4 reply),
`external-provider-choice.test.ts`, `identity.test.ts`,
`identity-onboarding-work.test.ts`,
`runtime-primary-ready-chat.test.ts`, `integrator-status-cards.test.ts`,
`turn-response-card.test.ts`, `chat-rendered-links.test.ts`.
- The corrected spend-breakdown grader passes on the retained cohort 4
evidence (its cost-summary read binds to the unresolved task) and fails
with the reads removed.
- `npx vitest run --config tests/runner-e2e/vitest.config.ts
chat-catalog chat-rendered chat-linked`: 284 files passed.
- `node --test
tests/runner-e2e/chat-full-catalog-testops-manifest.test.mjs`: 10/10
passed.
- `node scripts/typecheck-test-projects.mjs tests/runner-e2e`: passed.
- `node tests/runner-e2e/source-sdk-public-inputs.mjs <sha> --check`:
unchanged.
- Paid run (8 cells, OpenCode + DeepSeek v4.1 flash) on this branch
combined with the guidance PR: see the PR comment for results.
## Risks
- The new-company guidance check now needs a rendered link to
`/onboarding`. A reply that only names the company switcher fails. The
companion guidance PR tells agents this route.
- spend-breakdown does not grade wording, so a reply that reads the
record and still calls it "$0" is not caught. The amounts, total and
identification are still graded.
- The new-company case renames the source company and the person for the
case and restores them at cleanup.
## Model Used
- Anthropic Claude Opus 5.5 (`claude-opus-5-5`) in Claude Code, with
tool use, shell execution and code editing.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [ ] All Paperclip CI gates are green
- [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [ ] I will address all Greptile and reviewer comments before
requesting merge
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Chat feature scenarios
This is the planning catalog for what happens when a person asks an agent to use
Paperclip. It belongs to Product E2E, alongside the existing chat
and Everyday Workflows suites. These files contain human requests, assumed context
and loosely specified outcomes. Entries alone do not run models, register tests,
or contribute passing results to a report. Two task entries now have explicit
executable Product E2E cases: tasks.create-backlog and
tasks.delegate-announcement. Their results remain in the existing E2E reports.
The feature map answers what the product lets a person do. The agent access map records which APIs and actual agent entry points exist and their boundaries. This catalog asks whether a person can accomplish that work through a conversation, including discovering a prerequisite, completing a human step and returning to work. All three reuse the same capability IDs.
Start with these requests
- Hire a Claude Code engineer:
agents.hire-claude-code, with missing-account, approval-required, denied-permission and ambiguous-wording cases. - Create a website task:
tasks.website-task, with missing-project, attached-brief, ambiguous-task, privacy and ownership-conflict cases. - Archive completed inbox items:
inbox-search.archive-done, with missing authority, partial failure and undo cases. - Read Notion:
connection-setup.notion-connectedandconnection-setup.notion-missingdeliberately use the same sentence. The second requires setup in the conversation and continuation after usable access exists. Other cases cover decline, wrong account, unassigned agent and expired access.
Reading a scenario
Each area file uses catalog.schema.json and implicitly inherits defaults.yaml. Scenario context overrides those defaults. A scenario is independent: mentions of previous turns or existing resources are fixture requirements, not dependencies on running an earlier catalog entry.
| Field | Meaning |
|---|---|
schema_version |
Version of this planning format; currently 1. |
feature |
Primary feature-map filename without .md. |
status |
Always planned; execution results belong to the existing E2E system. |
scenarios[].id |
Stable, globally unique <feature>.<scenario> identifier. |
capabilities |
Full feature-map IDs exercised by this request; may span areas. |
context |
Existing records, current identity, authority, accounts, gates and significant starting conditions, plus any later human action or fault needed for the branch. |
query |
The natural-language message a person would send, including an answer or follow-up where that is the subject of the case. |
expected_outcome |
A paragraph describing the visible conversation, resulting durable state, necessary human interaction and meaningful boundaries. |
For example, two fixtures can send “Read my Notion and tell me the latest on the launch.” In the connected fixture, the answer comes from current pages and links back to them. In the unconnected fixture, the agent requests setup, the addressed person completes it, and the original work continues. A third fixture declines setup and should receive a truthful explanation without any provider read.
Context and expected outcomes are evaluator material. Do not paste them into an agent's system prompt or teach the model which API to choose. A future fixture should expose the actual product state, ordinary production instructions, the human query and any genuine attachments or previous conversation. The model must discover the available operations normally.
What coverage means here
Every current feature-map capability has at least one associated scenario. This is inventory coverage, not complete operation coverage, source verification or live acceptance. Broad capabilities can still need more CRUD, actor, transport, provider and lifecycle variations. The maps' Policy and Gaps sections remain the source for access assumptions; recheck them against implementation when promoting an entry. The YAML does not grant permissions or promise an unimplemented UI.
The default starting point is a person talking to their CEO in Agent Chat with legitimate agent permissions. CEO is not board identity. A valid expected outcome can therefore be direct action, a grounded answer, a necessary clarification, a connection or approval request, a precise human handoff, or a truthful boundary. An unsupported operation must not be “passed” because the agent says it succeeded. A vague “you can do this in settings” is also insufficient when a supported specific control or request card exists.
Some capabilities cannot originate inside an already working chat: first server installation, login, an unfinished first-agent wizard, browser preferences and developer-only labs. Their entries explicitly describe the relevant boundary or companion journey. External MCP and channel cases also name their different surface. Preserve those labels in future selection and reporting; a successful operator, browser-only or external-client step cannot certify a direct Agent Chat action. Where only guidance is currently possible, qualifying automated chat execution requires a separate product change and updated access contract.
Conditional coverage to preserve
| Condition | Representative scenarios |
|---|---|
| Account ready / missing / wrong / expired / not assigned | connection-setup.notion-* |
| Allowed / human approval / denied / wrong resolver | agents.hire-*, questions-and-approvals.wrong-reviewer |
| Unambiguous / missing required detail / ambiguous reference | tasks.website-task, questions-and-approvals.necessary-question, tasks.ambiguous-task |
| Complete list / partial mutation / undo | tasks.organize-open-work, inbox-search.archive-partial-failure, inbox-search.restore-archive |
| Same company / other company / private resource | access.other-company, access.private-resource, tasks.private-task |
| Current decision / stale decision / duplicate answer | questions-and-approvals.ambiguous-yes, questions-and-approvals.review-plan, questions-and-approvals.duplicate-answer |
| Idle / busy / steering / stop / saved continuation | steering.*, recovery.post-stop-message |
| Known result / uncertain side effect / failed accounting | recovery.unknown-email-outcome, budgets-costs.repair-cost-recording |
| Feature enabled / disabled / different surface | cases.cases-disabled, agent-chat.chat-disabled, developer-labs.gated-lab |
| Saved record / actually ready / independently usable output | runs-adapters.validate-hire, workspaces.preview-service, documents-artifacts.produce-download |
Different starting conditions should usually be separate scenarios with the same query, so one favorable fixture cannot satisfy all branches. Outcomes can mention invariant guards without turning each paragraph into a complete executable test. When implementing a branch that currently contains several human choices, split those choices into explicit cells while preserving the planning scenario's ID as traceability metadata.
Promoting a scenario later
- Read the matching product recipe and access companion. Confirm the operation, actual admitted agent surface and permission rules; keep current gaps visible. Decide whether this is an Agent Chat journey or a named companion boundary.
- Reuse or extend the existing Product E2E fixture, catalog and report paths. Everyday Workflows already cover hiring/reuse, delegation, connection decisions and delivered work. Existing chat-hardening, chat-qualification, completion-update and confirmation suites are also documented in the E2E guide. Overlap is a reuse opportunity, not evidence that this exact query and context have passed.
- Create isolated synthetic state through supported production setup surfaces. Pin the clock, identities, policy, runtime and provider profile; record feature gates and owned test connections. Keep actual secret material out of this catalog. Follow fixture and security rules.
- Send the query through the real conversation UI. Drive each specified human answer, connection callback, navigation, reconnect or fault at its actual boundary. Preserve the source chat and linked tasks across those transitions.
- Turn the outcome into independent assertions: rendered response and correct links; saved records and attribution; exact target and audience; actual file bytes or provider receipt; absence of unintended mutations; correct return to work. Validate the oracle against a plausible wrong outcome. Success prose, a tool declaration or an accepted HTTP response alone cannot pass a journey.
- Declare the ordinary E2E execution selection, environment, turn/time/cost limits, cleanup and evidence contract. Retain failures, retries, source and definition revisions, actual model/profile, usage and missing evidence in the existing report. A changed setup or model is a separate measurement, and a missing observer is not a pass. No live or paid runs are authorized by these planning files themselves.
The first useful runnable slice is the user's three core jobs plus the paired Notion branches above. Extend from that slice across this catalog without building a second runner, scheduler, scoring system or evidence store.
Maintaining the catalog
Keep area filenames aligned with the feature map and IDs stable when wording changes. Add a capability reference only when the query and outcome exercise it; linking every scenario to every related subsystem would inflate coverage. Write specific context and an observable paragraph rather than mechanically repeating a feature description. Use cross-feature references for real combined journeys.
Before handing off edits, parse every YAML file, validate area files against the
schema, check unique IDs and nonempty prose, and reconcile capability references
with the Sub-features keys in maps/feature-map/*.md. Check both missing and
unknown IDs. The schema alone cannot validate references or uniqueness across
files. defaults.yaml is shared context and is not an area-schema document.
chat-scenario-catalog.test.ts performs these structural checks in
pnpm test:e2e:runner:unit, including the 35 areas, 244 scenario IDs and 191 mapped
capabilities. Update the inventory counts intentionally when the catalog grows.
After editing map links, also run node scripts/check-maps.mjs. That existing
check validates the maps, not this YAML catalog and not live chat behavior. Keep
structural validation and any future execution evidence separate. Do not label a
scenario passed, register a paid cell or alter default suite selection merely
because its planning text has been reviewed.
Browse by feature
The feature recipe and same-named access companion define each area's behavior and authority. Each YAML file can also reference capabilities from other areas.