Files
PaperClipAI/tests/runner-e2e/PRODUCT-JOURNEYS.md
T
DottaandPaperclip d3f2a60e79 test(e2e): reuse core journeys across deployment targets (#18222)
## Thinking Path

> - Paperclip manages agents and their work.
> - Product E2E tests verify work through the browser and public APIs.
> - The classic matrix already tests several common task stories.
> - Its older stories depend on local fixture setup and workspace
access.
> - Deployment tests need the same story checks with a separate target
adapter.
> - This PR shares those checks and adds five explicit journey
selections.
> - A deployment pass still requires evidence from that deployment.

## Linked Issues or Issue Description

**What existing behavior does this improve?**

Product E2E journey reuse across managed instances and deployed Cloud
instances.

**Current behavior**

The deployed-target catalog covers task creation, assignment, Ask, and
fresh onboarding. Plan revision and Everyday stories run through the
classic fixture driver. Several story steps assume a local workspace.

**Proposed behavior**

Add explicit Plan revision, artifact revision, protected-action approval
and decline, Skill Studio, and Stop/redirect journeys. Share the main
browser sequences and content checks with the classic driver. Let target
adapters provide authentication, native agents, a reachable service
fixture, and owned cleanup.

**Reason and benefit**

A local pass cannot verify the deployed application. Shared story code
lets a deployed target exercise the same behavior without copying an
entire test or starting a replacement server.

**Breaking changes**

No product API or default matrix selection changes. Passed deployment
results must satisfy the declared story checks and work-run count.
Unsupported target capabilities fail before a story runs.

Related public work: #17262 and #18098. Upstream prerequisite: #18230.

## What Changed

- Extract shared Plan, artifact download/oracle, protected service,
Skill Studio, and Stop/redirect helpers. Keep classic callers on those
helpers.
- Add five explicit paid journey selections with owned-task observations
and native company, agent, model, and environment checks.
- Include classic prompts, delivery/decision code, and the decline
grader in the source-definition hash. Keep classic transient-read
retries and reject deployment binding failures immediately. Match cookie
directory boundaries in sensitive fixture requests.
- Require complete story checks and expected run receipts before
retaining a passing result. Keep failed evidence and private downloads.
- Add a secret-safe request method for deployment fixture credentials.
Add the missing declaration for the existing manual evidence policy
module so the E2E project can typecheck.
- Document target responsibilities and promotion after a real deployment
pass. This source catalog does not enroll candidates in a daily plan.

## Verification

- E2E TypeScript check: passed, including the review follow-up.
- Review follow-up: 30 tests passed across four files, covering
transient Plan reads, immediate binding rejection, cookie-path scope,
agent receipts, and artifact evidence. Stop now waits for a saved,
still-active native turn before the browser click and retains its
startup events. Counterexamples reject claimed-but-unstarted,
already-completed, interrupted, and non-native work. Shared observation
and API helpers also enter the definition fingerprint. Cancellation
snapshots are captured before lease cleanup. A direct terminal-boundary
assertion rejects native work after cancellation even if it is already
in the first snapshot; five cleanup-window counterexamples are covered.
The Stop-output regression first failed for both native `item.delta` and
`item.completed`; the fixed check detects both while allowing
accounting-only updates. A report-catalog test first failed because the
sandbox denied the tsx child IPC pipe (`listen EPERM`); the unchanged
test passed outside the sandbox.
- Affected journey, catalog, API, service fixture, and delivery checks:
313 tests passed across 19 files.
- Workspace build: passed after rebase.
- Workspace typecheck: passed. The default repository run was attempted.
Repeated embedded PostgreSQL startup failures prevented local database
coverage; it was stopped without claiming a full pass. Linux CI must
supply that coverage.
- Broad E2E support run: 4,793 passed, 23 failed, 2 skipped. Failures
include existing stale image/Pi assertions and process/startup timeouts
under host load. The affected tests pass separately. This is not a claim
that the broad suite is green.
- Upstream CI prerequisites: this branch is rebased on the merged #18230
repair. The earlier conflicting Turbo flags no longer apply. The
shell-hook fix is also supplied by that upstream change. Linux
shell-hook investigation: 19 focused tests and adapter TypeScript
passed. The exact staged-config sequence reproduced CI's 500 in a pinned
Node 24 Linux container. Node's default stdin socket at shell level zero
selects Bash remote-shell startup and reads `.bashrc` instead of
`BASH_ENV`. Ignored stdin and a nested shell each produce 1000. The
fixture now ignores stdin; the production hook and 1000 assertion are
unchanged. See [Bash startup
rules](https://www.gnu.org/software/bash/manual/html_node/Bash-Startup-Files).
- GitHub CI on the earlier head passed most suites but failed two
browser tests (a fixture cleanup lock timeout and a fixture run staying
queued) plus the shell-hook assertion. The lock-timeout case received
one retry, which was cancelled by a later source push and does not
establish recovery. These failures remain part of the verification
record.
- Real Cloud qualification: the private target adapter is reviewed and
merged. These candidates must pass there before daily-plan promotion.

Current source review: Greptile 5/5 and Paperclip Review 5/5 on
`a9df1d6e8d880710fce39b87e689e07e1c8e6edc`, with zero unresolved review
threads. The full [GitHub CI
run](https://github.com/paperclipai/paperclip/actions/runs/40974946081)
passed on that exact head, including both required aggregate checks.

## Risks

Shared helper changes affect classic callers. The independent ZIP oracle
and visible delivery requirements remain in place. Stop requires native
acknowledgement, terminal event evidence, resource cleanup, and no later
original-turn activity. The managed local target still lacks paid-agent
fixtures and reports these journeys as unsupported.

## Model Used

OpenAI Codex, GPT-6 family, with reasoning, terminal use, and code
execution. The exact serving model ID and context window are not exposed
to this session.

## 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
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-10-29 18:58:26 -05:00

14 KiB

Product journeys across deployments

product-journeys is an explicit Product E2E selection. Deployment is separate from the local / daytona execution environment. Existing selectors, execution IDs, and the --all matrix keep their historical meaning. These are scripted browser journeys with API setup and independent assertions; they are not strict browser-only “use as a user” discovery.

Concept Owner
Journey User actions, prerequisites, assertions, feature-map associations
Target provider Authentication, deployed identity, configuration, acquisition, cleanup
Setup recipe Resolve requested company/user/task capabilities through the fixture registry
Campaign Explicit journey order, target, model profile, limits, lifetime

Shared code is in product-journeys/. Cloud fleet operations, email-code login, private state, and trusted CI live in the private paperclip-cloud repository. The TargetProvider version-1 interface is defined in product-journeys/types.ts. Journeys receive a ready browser, company-scoped API, IDs, and capabilities. They never start a server or depend on a tenant filesystem.

Selections

Journey Required starting state Observed outcome
task-create Company and signed-in board user Create a task through the shared production composer, assign it to yourself, reopen and reload it, verify the saved prompt and no agent work
ask-question Company, configured agent capability, positive model allowance and exactly one permitted work run Reuse the existing cross-model Ask question prompt; require one successful run with matching company/task/agent/model/environment, one saved exact answer, and visible readback after reload
task-properties Company, signed-in board user, task-write capability, prepared run-owned backlog task Open its properties, assign it to the signed-in user, reload, check both UI and saved assignment; verify no agent run
plan-revise-accept Native agent; three work runs Reuse the classic Plan story: canonical draft, browser revision request, new revision-bound confirmation, approval and exact saved final response
build-revise Native agent; two work runs; qualified Docker artifact oracle Browser-download both ZIP versions, independently exercise source/CLI behavior, verify different hashes and preserve the original download
protected-actions Native agent; four work runs; authenticated reachable review-service fixture Approve one action and decline another on separate owned tasks; verify zero calls before either decision, one after approval, zero after decline and a truthful saved explanation
create-skill-studio Native agent with skill-write permission; one work run Create a uniquely named company skill, open its activity card, edit in Studio, reload and verify task-card readback
stop-redirect Native agent; three work runs; qualified Docker oracle Download a verified checkpoint, Stop an active revision, require native cancellation acknowledgement, submit a new direction once and verify completion without original-run activity
cloud-onboarding Signed-in admitted Cloud account; fresh workspace; qualified bounded provider route Create workspace in the portal, connect API key and native Codex agent in the wizard, complete a task, download and independently check its artifact

Supported acquisitions are managed self-hosted (local), registered existing Cloud (local or daytona when present), and new Cloud. New Cloud must start with onboarding. Its context can then satisfy the identical task-properties journey without another setup. A recipe reports startingState: recipe; reusing successful onboarding reports onboarding. Preconfigured state is never counted as onboarding coverage.

Save this credential-free local target config outside the repository:

{
  "version": 1,
  "id": "local-qa",
  "mode": "managed-self-hosted",
  "journeys": ["task-properties"],
  "executionEnvironment": "local",
  "baseline": { "features": {}, "connections": [] },
  "limits": { "durationMs": 600000, "modelBudgetCents": 0, "maxRuns": 0 }
}
pnpm test:e2e:runner -- --suite product-journeys --list
pnpm test:e2e:runner -- --target-config /private/target.json --preview
pnpm test:e2e:runner -- --target-config /private/target.json --output /private/new-campaign

Preview resolves selections and declared changes without loading the private provider, reading credentials, signing in, provisioning, or making model calls. Its output includes required state, features/connections, lifetime, and limits. Output directories must be new; an attempt is never overwritten. Browser-only results use null provider/model and runtime none, including failures before the journey starts. No artificial model run is introduced for reporting. New execution IDs have the form product-journeys.<target>.<profile-or-browser>.<environment>.<journey>; the report reader continues to accept historical four-part execution IDs.

Cloud config supplies adapterModule and the private provider's reference-only adapterConfig. Use the private CLI and runbook to configure those fields. Paid onboarding requires positive modelBudgetCents and maxRuns, a duration of at most one hour, and a qualified operations adapter. The initial private Cloud adapter admits at most 30 minutes and $10. Select runner-codex-mini explicitly for its gpt-5.4-mini pilot; existing profile defaults remain unchanged. There are no automatic story retries. One campaign may create at most one disposable workspace.

Workspace contention can defer a child before provider execution. The journey keeps that run and follows only its unique, scoped durable replacement when the server proves that no provider work started. A scheduled replacement remains pending and consumes the run limit. It cannot satisfy completion; the child must still finish through native execution and pass the independent artifact checks.

The attached ask-question recipe requires a prepared native general agent and an explicit execution environment. Legacy execution is unsupported until its saved model/provider identity can be verified. A registered agent avoids a new setup probe; a disposable QA agent uses the existing native fixture defaults and its setup remains separately unpriced. A restricted review bot is not a general-work fixture. The private provider supplies its exact execution identity through the agent capability and collects normalized run usage. The existing Cloud provider still owns authentication, conditional leases, fixture ownership, and cleanup. A model allowance is an accounting threshold; reused direct-provider routes do not acquire the onboarding gateway's request-level cash guarantee. The question observer allows one run and waits at most four minutes. Cleanup stops only run-owned question work; shared agent configuration stays outside ownership. When the QA adapter explicitly selects terminal-history retention, it terminates its disposable agent, makes owned tasks terminal, verifies no active runs, retains the records for decision-history integrity, and releases both QA leases. This is reported as retained data, rather than confirmed deletion.

Evidence and lifecycle

Results use the existing campaign/result schemas and dashboard. Each result retains campaign/attempt IDs, source revision, definition hash, deployed tenant commit, Cloud release, runtime identity, configuration hash, setup provenance, checks, and authentication/setup/journey/collection/cleanup outcomes. A failed prerequisite remains failed at that boundary; every dependent selection receives a blocked result. Unsupported target capabilities are distinct from failures. Deployment/configuration drift during a journey fails its result.

New onboarding attempts retain a bounded productJourney.artifact receipt in the existing private normalized-results.json, before disposable cleanup. It includes the exact downloaded connection-proof.json bytes as base64, attachment and originating-run IDs, byte count and SHA-256, plus the harness-generated input and independent expected result. Reinspection can decode the original bytes, recompute their checksum, and recompute the expected nonce, count, sum, and input checksum after the workspace is deleted. Only this exact four-field synthetic fixture is admitted, with a 4 KiB download limit and a closed receipt schema; arbitrary agent output is excluded. Capture follows attribution and independent download verification. The receipt survives later document, link, reload, or trace failures without turning those failed journey steps into passes. Historical attempts without it remain readable and do not gain retained-artifact proof retroactively. Descendant stories reuse context without copying the receipt.

Collection must finish within the campaign deadline. Cleanup and evidence publication then share a separate ten-minute grace period, with the last thirty seconds reserved for a twenty-second stop-only fallback and ten-second browser close. These are cleanup bounds and do not admit further story or model work. Provider lifecycle hooks receive a deadline and cancellation signal; they must bound in-flight requests and fence late resource mutations or lease writes when the signal aborts. A timed-out release cannot authorize evidence finalization or lease release. Its manifest stays available for cleanup recovery.

Every would-be successful result is checked against the retained-result success contract before successful cleanup. Missing assertions or paid accounting proof turn the campaign into a failure while preserving valid cost receipts, so an invalid success cannot trigger disposable-stack deletion.

The shared Plan story additionally retains draft and revised task screenshots privately. Other screenshots capture the post-journey task. Failed task/search screens are retained only after tenant setup; authentication pages are excluded. Browser state, login codes, raw errors, credentials, request headers, and auth screenshots are excluded. All Cloud evidence is private; the public history publisher rejects these bundles. The private provider uses conditional S3 identity/stack leases and intent/receipt ownership manifests. An expired lease permits cleanup recovery, not reuse.

Managed self-hosted instances are removed after every attempt. Existing Cloud stacks retain the QA account and declared baseline while removing verified run-owned fixtures. Successful disposable stacks are deleted. Failed disposable stacks are stopped and retained for up to 24 hours. The deployment provider declares earlier cleanup eligibility to leave time for its sweeper. Interrupted cleanup remains visible and retains exclusive stack ownership. QA identity and inbox never belong to disposable ownership.

Eval operations integration and qualification

The private adapter carries the same campaign/attempt identity through bounded request admission, setup/agent phase transitions, run observation, cost receipts, trace IDs, and feedback collection. A normalized operations receipt belongs only to the onboarding attempt; descendants do not repeat shared setup charges. Unknown cost remains unknown. Raw feedback, traces, and ledgers stay private. Invocation evidence distinguishes native event inspection from a proven provider_not_started workspace deferral. Both require complete event reads. Pending replacements and contradictory execution evidence remain partial.

The sibling Operationalize eval runs implementation currently admits local cells and binds its gateway to loopback. That is not Cloud reachability or budget qualification. A Cloud adapter must prove the selected runtime's route, effective credential binding, budget admission, and exact campaign deadline before any paid provider action. The normal wizard first saves a custom-gateway connection and selects its model without inference. Qualification checks this exact saved route from the actual runtime before the normal Connect probe. Missing qualification blocks onboarding; it must never fall back to an unmetered direct provider request.

As of 2026-10-22, the same task-properties story has passed in real browsers on managed self-hosted and a registered testing Cloud stack, including cleanup and zero model calls. Normal QA email-code authentication and account admission have also passed against testing. Cloud onboarding selectors and lifecycle contracts have offline tests; fresh onboarding, dependent reuse, and disposable cleanup remain unqualified. Provider and trace secret references are configured. The separate onboarding gateway product change, trusted CI selection, and real runtime gateway admission remain live qualification prerequisites. Testing and staging use separate registries and credentials; only the declared testing control plane is admitted. Defining a story or passing structural checks is not evidence that its Cloud journey passed.

Reuse and daily QA promotion

The classic matrix and deployment-targeted stories share Plan revision actions, artifact download/oracle code, Skill Studio editing and protected-action decisions. Their target adapters own login, agent/connection provisioning, environment identity, and cleanup. The managed local recipe currently provides browser-only capabilities; paid stories fail as unsupported until a native agent fixture is supplied. This does not turn a classic local pass into a QA Cloud pass.

Paid deployment campaigns declare the exact sum of expected work runs (Ask 1, Plan 3, artifact revision 2, protected actions 4, Skill Studio 1, Stop/redirect 3). Each observer reads only its owned tasks and checks native company, agent, model and execution-environment identity. Setup is separately unpriced. Skills and service connections are uniquely owned; customer credentials and company-wide configuration are never copied into a journey.

The private QA adapter keeps candidate cases available for manual qualification. A candidate's presence in this source catalog is not qualification. It enters the saved daily TestOps plan only after an inspected real QA Cloud pass including usage, artifact/decision evidence and cleanup. The daily driver runs that saved plan against the registered QA tenant; it does not substitute a local instance.