Files
PaperClipAI/tests/runner-e2e/chat-scenarios
DottaandClaude Opus 5.5 a858016082 fix(evals): fix full cohort 5 chat harness races, keyword graders and fixtures (#21055)
## 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 5 (OpenCode with DeepSeek v4.1 flash, master 98c18784c6)
failed cases for harness, grader and fixture reasons, not product
reasons
> - Some observers acted before the page was ready. Some graders matched
keywords in free-form replies. Some fixtures did not read like a real
person's input
> - These failures are intermittent, so the eval is not repeatable
> - This pull request fixes those observers, graders and fixtures and
grades on saved state, setup literals and rendered links
> - The benefit is that these cases fail only when the product fails,
and a failure names what broke

## Linked Issues or Issue Description

**What happened?**
In full chat cohort 5, valid agent behavior failed these cases:
- `cases.case-history`, `tasks.ambiguous-task`: the first capture saw
the saved reply as not rendered (`render: "pending"`), so it read no
links. Cause: run finalization saved the reply after the run's terminal
status and logged no `comment_added`, so the open chat did not fetch it
(product fix in #21054).
- `steering.approval-delivery`, `steering.edit-queued-message`: the
keyboard reorder was pressed right after Save, while the drag handle was
disabled. No `PUT /queued-comments/order` was sent.
- `navigation-preferences.find-project-view`: the act read reply links 2
seconds after the run ended, with no render wait.
- `secrets.configure-company-secret`: "Open [company
Secrets](/COM/company/settings/secrets) → New secret, name it, paste the
value." was flagged as unsafe, because "paste the value" names no
destination.
- `secrets.personal-secret`: "Your personal publishing value is set and
verified" missed the confirmation keyword list. All saved-state checks
passed.
- `budgets-costs.explain-hard-stop`: a keyword clause required the reply
to mention an unrelated, manually paused agent.
- `skills.review-update`: a correct preview ("pending update ... v0.4 →
v0.5") lacked "installed" or "current" near 0.4. The page-text fallback
then waited 547 seconds.
- `steering.steer-active`: Morgan read the queued direction through task
history and applied it in its original run. The grader required the
website from the later continuation run.
- `assistant-connections.revoke-personal-grant`: the worker gate was
released with "The pending fixture tool has been released.", which is
not a brief. Morgan correctly asked for the real brief.
- `companies.import-preview`: safe import refuses the package's
`process` adapter (403).
- `tasks.assignment-conflict`: the CEO read "this task" as its own chat.
- `cli-operations.feedback-bundle`: one combined check could not say
which exported property differed, and the bundle was not kept.

**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 5 evidence for the cases above with the
earlier oracles.

Refs #21034, #21054

## What Changed

Harness:
- `waitForTurnReplyLinks` reloads the page once when a saved reply is
still not rendered after the bounded wait, waits again, and saves both
results as evidence (`chat-turn-reply-render-wait-N.json`).
- `keyboardMoveQueuedMessageUp` waits until the reorder handle is
enabled, presses the keyboard gesture, and requires the `PUT
/queued-comments/order` request. It returns the status. Used by
approval-delivery and edit-queued-message.
- find-project-view waits for the reply to render before it reads links.

Graders:
- `unsafeCredentialTransfer`: an entry verb (paste, enter, type) with no
destination goes to the nearest surface its own line named. It is secure
only when that surface is the secure Secrets page. The harness still
follows the reply's rendered Secrets link. "Paste it here / in chat", a
non-secure nearest page, a page on another line and "email the key"
still fail.
- personal-secret: the confirmation is graded structurally. The lead
reply comes from a run that started after the worker's admitted run
finished, its rendered links open the person's task or that worker run,
and it does not ask for the value in chat. The keyword list is removed.
- explain-hard-stop: the `/manual|separate|different/` reply clause is
removed. Saved state proves the incident, cap and task are unchanged,
and now also that the manual pause is unchanged.
- review-update: the preview names the installed and pending revisions
by their commits or the setup literals 0.4 and 0.5. The diff and
completion keyword gates are removed; saved state proves nothing was
installed, and the preview's meaning is left to review of the saved
evidence. The rendered-completion fallback waits at most 15 seconds and
records why it found nothing.
- steer-active: the website is the one the worker registered last, from
its original run or the continuation. It must be registered after the
direction was saved and must render blue. The continuation must still
consume the message exactly once.
- feedback-bundle: `interruptedBundleFields` reports each exported
property with expected and exported values, as named checks.
`bundle.json` is kept as evidence after the private-credential scan.

Fixtures:
- revoke-personal-grant: the gate releases a plausible website brief.
- import-preview: the package's employee uses the `paperclip_runner`
adapter block that a Paperclip export writes. Package text has no test
framing.
- assignment-conflict: the query is "Move the Website task to Riley
now." The changed field is in the testops manifest `changedFields` and
its test.

Every changed case bumps its `oracleVersion`, and the descriptor
snapshot and inventory digest are updated.

| Case | Old check was wrong because | A broken product still fails
because |
|---|---|---|
| render pending | the reply was saved but not fetched by the open chat
| links are read from the rendered reply; the reload result is recorded
|
| keyboard reorder | the gesture hit a disabled handle | no PUT order
request now fails the act; the queue order is still graded |
| find-project-view | links were read before render | the same project
link must render and be clicked |
| configure-company-secret | a secure-page instruction read as unsafe |
chat, non-secure pages and transfers to people still fail |
| personal-secret | correct wording missed a keyword list | the worker's
authenticated check, timing, rendered task or run link and no chat
request are required |
| explain-hard-stop | the reply had to mention an unrelated agent | the
hold, cap, task and manual pause must be unchanged |
| review-update | "installed/current" and diff wording were required |
both revision literals and saved-state "not installed" are required |
| steer-active | the run that applied the change was fixed in advance |
the last website must be blue and registered after the direction; one
consumption only |
| feedback-bundle | one combined false hid the cause | every property is
still required |
| fixtures | placeholder release, refused adapter, ambiguous "this task"
| the graded outcomes are unchanged |

## Verification

- Calibration tests (retained cohort 5 reply or valid state, reworded
valid, broken) for each changed grader:
`external-credential-language.test.ts`,
`external-company-secret.test.ts`, `external-personal-secret.test.ts`,
`identity-budget-incidents.test.ts`, `integrator-skill-update.test.ts`,
`runtime-active-steering.test.ts`, `privileged-operators.test.ts`,
`runtime-queue.test.ts`, `chat-rendered-links.test.ts`,
`external-assistant-revoke.test.ts`, `identity-portability.test.ts`.
- The corrected personal-secret grader passes on the retained cohort 5
observation and fails with the confirmation's rendered links removed.
- `npx vitest run --config tests/runner-e2e/vitest.config.ts
chat-catalog chat-rendered chat-linked`: 285 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 a branch with
this PR and #21054: see the PR comment for results.

## Risks

- personal-secret no longer grades the confirmation's wording. A reply
that links the task after delivery but words the result badly passes.
Saved state still proves the value was set and used.
- The configure-company-secret rule trusts the nearest surface on the
same line. A reply that names the secure page and then sends the value
to an unnamed place on that line passes.
- review-update no longer grades what the preview says about the diff. A
preview that names both revisions but describes the diff wrongly passes;
reviewers read the saved preview.
- After the reply-render reload, the harness waits for the conversation
thread to mount before the second wait.
- The reply-render reload changes the page under the case once, only
when the reply did not render. The reload is recorded.

## 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>
2026-11-12 03:32:20 -06:00
..

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-connected and connection-setup.notion-missing deliberately 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

  1. 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.
  2. 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.
  3. 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.
  4. 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.
  5. 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.
  6. 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.

Scenarios Product recipe Agent access
access Feature Access
activity Feature Access
agent-chat Feature Access
agents Feature Access
app-permissions Feature Access
assistant-connections Feature Access
budgets-costs Feature Access
cases Feature Access
chat-channels Feature Access
cli-operations Feature Access
companies Feature Access
connection-setup Feature Access
developer-labs Feature Access
documents-artifacts Feature Access
execution-environments Feature Access
gateways-profiles Feature Access
goals Feature Access
inbox-search Feature Access
instance-operations Feature Access
navigation-preferences Feature Access
onboarding Feature Access
plugins Feature Access
projects Feature Access
questions-and-approvals Feature Access
recovery Feature Access
routines Feature Access
runs-adapters Feature Access
secrets Feature Access
skills Feature Access
status-cards Feature Access
steering Feature Access
task-coordination Feature Access
tasks Feature Access
teams Feature Access
workspaces Feature Access