mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-30 13:20:54 +01:00
## Thinking Path > - Paperclip lets people delegate work to agents and approve proposed work. > - The onboarding first task asks for scope, proposes a task, and waits for a human decision. > - An answer to its opening question supplies scope. It does not approve a proposal. > - Task creation already checks the saved proposal decision. > - Native completion used the general completion policy and could mark the onboarding parent Done without that decision. > - This PR checks the trusted decision before completion feedback and again when the native status is committed. > - The first task stays in review until a saved human decision permits closure. Ordinary completion and acknowledged refusal keep their current behavior. > - The Product E2E guard also retains closed scalar evidence so future failures show which boundary condition was observed. ## Linked Issues or Issue Description Refs: #17914. Profile compatibility follow-up to #17922. **What happened?** A native onboarding run can submit a valid low-risk Done result after only the opening answer. The native finalizer then marks the first task Done even though no human has accepted or rejected a proposal. **Expected behavior** The opening answer must not permit completion. A trusted saved proposal decision must permit it. A saved rejection permits acknowledgment and closure, but does not authorize new work. A later pending proposal must keep the task in review. **Steps to reproduce** Create a server-owned onboarding first task with an answered opening question, no proposal decision, and a valid low-risk completion contract. Pass a Done result through native completion feedback, the control-plane port, and the finalizer. Before this fix, the persisted status is Done. The regression expects In review. Ordinary completion and acknowledged rejection pass as controls. ## What Changed - Share the trusted latest-proposal and human-response checks with completion admission. Keep task creation acceptance-only. - Refuse premature native completion feedback. Keep an already saved premature result in review during finalization. - Check the decision again under the existing task and run locks before committing Done. - Cover accepted decisions, saved refusals, forged context, pending or changed proposals, and a real database race. - Retain a closed scalar receipt when the existing E2E pre-acceptance guard fails. Validate and display it without task content, IDs, filenames, or raw rows. Preserve the original failure, blocked dependents, and historical reports. - Include the evidence module in the journey definition hash. Bind the receipt to its exact failure code and check its closed keys against the full type. - Repair base-branch profile drift found by the full harness check: include the active Copilot v66 fixture in image hashing and update stale Pi version/hash expectations from #17922. Keep all older image inputs and strict hash checks. - Update the onboarding feature and agent access maps and E2E evidence documentation. ## Verification - Semantic regression before implementation: one failure and two passing controls on PostgreSQL 18. The failed assertion observed Done instead of In review. - The local database uses an owned disposable container with an immutable image digest. Actual migrations, feedback, control-plane port, finalizer, and status commit run against PostgreSQL. A temporary test adapter replaces only the embedded database transport because this host has exhausted its PostgreSQL semaphores. - Native control-plane suite: 55 tests passed, including 17 new controls. Existing task-creation admission: 25 passed. Existing completion feedback: 19 passed. - Credential-free E2E support: 206 Vitest suites passed, 4,407 tests passed, and two skipped. All 137 Node support checks passed. Harness typecheck passed. - The failure-receipt regression had 11 failed assertions before capture was implemented. Two failure-code binding assertions fail without their gate. Eight placement/binding cases first validate the receipt-free control, then reject the misplaced receipt. - The untouched base reproduced the three profile compatibility failures. The narrow repair passed all 83 affected tests before the full harness run. - Private workflow preparation and credential-free onboarding preview: nine compatibility checks passed. Ownership, recovery, evidence, shared-contract and workflow compatibility: 77 passed. The actual provider preview also confirms immediate success deletion, earlier declared failure eligibility, limits, and zero retries without credential access. Map checks and git diff checks passed. - Required repository typecheck passed for the product fix. The combined branch build passed. The local full repository suite was stopped after repeated PostgreSQL startup failures; a separate initdb probe confirmed host SysV semaphore exhaustion. No shared IPC resources were changed. [Hosted CI](https://github.com/paperclipai/paperclip/actions/runs/40591020873) passed the full database suites, typecheck, build, runner checks, serialized suites, and browser shards on final head `1ac4c23605a03bfd1f1f8a017b14ce29fda85f6b` (55 success, two expected skips, one advisory neutral). - Independent source review approved the product guard, atomic race regression, evidence contract, and maps with no actionable findings. Both automated reviews are 5/5 on final head `1ac4c23605a03bfd1f1f8a017b14ce29fda85f6b`; prior findings are fixed and resolved. Final CI is green and the PR is conflict-free. - No Cloud workspace, provider call, release publication, or deployment is part of this verification. These tests do not identify a historical Cloud failure or qualify live onboarding. - Local dependency installation uses the existing CI-generated lockfile from #17811. No lockfile change is included. ## Risks - Existing onboarding runs without a trusted saved proposal decision remain in review even when the agent reports success. This is the intended approval boundary. - Decision changes can race completion. The status commit checks the decision under its existing locks and uses the existing status-race recovery path. - A saved refusal permits closure only. Creation admission still rejects it. - Evidence readers reject malformed receipts and unrelated failure codes. The version-1 key registry is checked against every receipt field at compile time. Existing attempts do not gain evidence after the fact. ## Model Used OpenAI GPT-6.1 Sol (`gpt-6.1-sol`), ultra reasoning, through Codex. Used repository inspection, code execution, and collaborating coding agents. The runtime did not report a context-window size. ## 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 references) - [x] My branch name describes the change and contains no internal ticket 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>
153 lines
9.1 KiB
Markdown
153 lines
9.1 KiB
Markdown
# 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-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 |
|
|
| `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:
|
|
|
|
```json
|
|
{
|
|
"version": 1,
|
|
"id": "local-qa",
|
|
"mode": "managed-self-hosted",
|
|
"journeys": ["task-properties"],
|
|
"executionEnvironment": "local",
|
|
"baseline": { "features": {}, "connections": [] },
|
|
"limits": { "durationMs": 600000, "modelBudgetCents": 0, "maxRuns": 0 }
|
|
}
|
|
```
|
|
|
|
```sh
|
|
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.
|
|
|
|
## 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.
|
|
|
|
Only post-journey screenshots are captured. 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.
|
|
|
|
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.
|