Files
PaperClipAI/tests/runner-e2e/product-journeys/COMPANY-ACCESS.md
T
a497bae83a test(e2e): verify Cloud workspace access isolation (#18655)
## Thinking Path

> - Paperclip keeps agent work inside company boundaries.
> - Cloud users switch workspaces through the tenant account menu.
> - A company-bound credential must work inside its permitted company.
> - A missing resource cannot prove an access denial.
> - This journey uses real controls in two registered test workspaces.
> - It checks reads and task creation in both directions and revokes
both keys.

## Linked Issues or Issue Description

Refs: #18728, #18729, #18652.

**What existing behavior does this improve?**

Product E2E coverage for Cloud workspace selection and company-bound
agent API access.

**Current behavior**

The journey catalog lacks a two-workspace boundary check with real
allowed and denied controls.

**Proposed behavior**

Use the visible Cloud menu to switch workspaces and return. Read a real
task and create one task in each credential's own company. Require real
foreign reads and the same task-creation operation to return 401 or 403
in both directions. Verify that no foreign task was created and that
protected content was not returned. Revoke both credentials and require
their earlier allowed reads to return 401.

## What Changed

- Add a browser journey and a narrow target-provider fixture contract.
- Require independent owner reads, exact agent authorship, zero-to-one
task creation, stable run history, no unexpected work and no foreign
creation.
- Validate observed workspace entry links against the exact registered
targets.
- Add wrong, missing, leaked, foreign and invalid-entry evidence
controls. Saved passes reject collected runs or nonempty model usage.
- Register all seven required checks, definition-hash inputs, feature
associations. The feature-map documents are unchanged, as requested.
- Base the final tree on verified repository master
`7b8aa60b6d79b6cd9e4d46ca1369515c33e2d5d4` plus only 15 access journey
and catalog files. Preserve all other master files and modes. The held
completion journey is outside this PR's diff.

## Verification

- 851 catalog, journey, completion-helper, artifact and browser-action
support tests passed across 40 files.
- E2E TypeScript, credential-free discovery, feature-map validation,
full workspace recursive typecheck, full build and `git diff --check`
passed.
- The broad local `pnpm test:run` attempt stopped in chat integration
shard 1 before any assertion. Embedded Postgres initialization exited
with code null. No chat tests or later unit shards ran. The startup
cause is unresolved, the failure is retained, and the command was not
retried. The database/chat/test-runner source is unchanged from the
fixed master snapshot. The separate hosted full CI result is recorded
below.
- The first local E2E typecheck read stale compiled plugin SDK
declarations after the merge. The SDK source already declared the field.
Rebuilding its declarations cleared the diagnostic without a source
edit.
- The complete tree comparison proves exactly 17 changed paths before
the documentation removal against the fixed master snapshot. Every other
master file and mode is preserved. All four access flow, contract and
test blobs match the previously passing source
`fab9a1fb2fefb41a7248e4fc1aa239d77455f5e2` byte for byte.
- The historical staging attempt on source
`fab9a1fb2fefb41a7248e4fc1aa239d77455f5e2`, the earlier Core42
deployment and the earlier adapter passed all seven checks: owner
resource controls, visible Cloud switch and return, scoped data, both
restricted-agent positive controls, cross-workspace read/task-creation
denials, credential revocation and no agent work. Both credentials were
revoked, owned credential agents had terminal receipts, and evidence
cleanup was complete. No agent or model work was recorded.
- The earlier attempt failed before journey checks while reading the
borrowed fixture agent's historical details. That failed result remains
preserved. The provider then supplied a separately owned primary
identity. The public history assertions remain unchanged.
- The strict root catalog expectation first failed because it omitted
the new access case. Adding only that expected entry made the test pass.
The ordered list and default-selection assertions remain unchanged.
- The first combined-head full CI run failed two unchanged
attention-retention tests at their original 15-second limits. The
catalog fix does not change those tests or their timing. The initial
failures are retained, their cause is unresolved, and that failed run
was not retried.
- Normal full CI passed on the new commit in [run
41595755161](https://github.com/paperclipai/paperclip/actions/runs/41595755161),
attempt 1: 48 successful jobs and one intended skip. The two unchanged
attention-retention cases passed at about 12 seconds; the earlier
timeout cause is still unknown. The fresh review on commit
`4c0ea682444738ad6fa17017a6afd25a49037559` is 5/5 with no open findings.
The fresh staging result is recorded below. This PR changes no daily
plan, private configuration, model or live workspace.


- Fresh staging qualification on commit
`4c0ea682444738ad6fa17017a6afd25a49037559` passed all seven checks and
all five phases. Both ordinary same-workspace task creations returned
201. Both cross-workspace task creations returned 401. Both credential
agents reached terminal state. Both keys were revoked, and both
revoked-key reads returned 401. The manifest records complete cleanup
and no active operations. No model or task run was recorded. All three
workflow jobs passed.
- The coordinator verified TestOps case 719 and result 3761 as passed,
then closed launch 1987. Its report shows model none and the current
qualification run. The automatic Slack report recorded one pass and zero
failures.
- The original normalized result was passed. The first evidence
collector left its own qualification false because its allowlist omitted
the standard `dashboard.html` artifact. The adapter already declared
that file, and the public launcher generated it. An exact-object read
matched the 118,791-byte object, ETag and SHA256 archive receipt. This
completed the three-artifact inventory. The original result, collector
proof and seal remain unchanged. No HTML was executed or rendered, and
no test was rerun or regraded.

| Fresh staging provenance | Verified value |
| --- | --- |
| Public commit | `4c0ea682444738ad6fa17017a6afd25a49037559` |
| Qualification workflow | `41597774599`, attempt 1 |
| Serving Core commit | `09162fe893b464dd2ccdafd14bd3ef1611202a86` |
| Target adapter commit | `7367cf6fd0ee1cf52c2651a8a6671e087728a9c1` |
| Actual workflow bootstrap commit |
`55351ffba2704a4e13c1ead3e50665acd51aef87` |
| Exact 117-file QA contract SHA256 |
`14d95c29059c82563ab0e1d65a01aa4ac6c4f60f77a236803cf06ad5d9e7d27b` |
| Provider-pack configuration version | `9` |

- At the user’s request, follow-up commit
`7e0dbc4bdba8a25ff6486648b0ca0d0db2616a4c` removes both feature-map
additions. The final diff contains 15 paths and no `maps/feature-map/`
changes. An entire-tree comparison against the QA-passed commit proves
these two Markdown files are the only changes; every executable file,
test, fixture, registration and file mode is unchanged. `node
scripts/check-maps.mjs` and `git diff --check` pass after the removal.
The live QA run above was not repeated for this documentation-only
change. Fresh current-head full CI passed in [run
41633385961](https://github.com/paperclipai/paperclip/actions/runs/41633385961),
attempt 1. The current-head Greptile review is 5/5 with zero unresolved
threads. The normal merge preserves every newer master file outside the
15 PR paths and keeps all 15 PR files byte-for-byte intact.

## Risks

The private target provider must register two exact test workspaces and
preserve their separate ownership and cleanup. The case verifies
company-bound agent credentials. It does not certify human membership
roles. It tests task creation because ordinary agent task edits require
a real execution context. No execution context is forged and no product
authorization guard changes.

The local broad unit run could not start embedded Postgres, so it does
not establish a full local unit pass. The earlier hosted
attention-retention timeouts also remain unexplained. Passing a later
run would not prove the source of those timeouts.

The four case files retain their historical passing bytes. The fresh
qualification verifies the executable test code retained in this PR
against the deployed staging stack. Daily admission remains a
coordinator action after the normal source merge. The case does not
cover future deployments without another run.

## Model Used

OpenAI GPT-6 through Codex with reasoning and code execution. The
runtime does not expose a more specific model identifier or
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 `#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>
Co-authored-by: Codex <noreply@openai.com>
2026-11-03 06:24:08 -06:00

3.3 KiB

Company access isolation

company-access-isolation checks a deployment that exposes two separately owned workspaces through its Cloud account organization switcher. The target provider supplies the CompanyAccessFixture contract. A provider without that capability reports the case as unsupported.

The browser uses the mounted tenant account switcher to open the secondary workspace and return to the primary workspace. It clicks an observed entry link for the exact registered slug or ID. Each workspace must return its own single company and exact owned read sentinel after reload.

A real company-scoped agent credential in each workspace reads its own sentinel and creates one new task through POST /api/companies/:companyId/issues. The response must be 201. Independent owner reads must show zero matching tasks before and exactly one afterward, with the exact title, body and company. Both the response and canonical task must name the actual createdByAgentId and a null createdByUserId. The task stays unassigned in Backlog. This is task creation coverage; task edits and comments require a real heartbeat run and are not used as zero-run write controls.

The same credential then attempts to read the real foreign sentinel and create a distinct task in the foreign company through the same POST path. Both actions must return 401 or 403. The foreign sentinel must stay unchanged, and the attempted task must not exist. Denied bodies must not expose protected foreign sentinel content. Echoes of the caller's distinct attempted input are not leaks. Both directions must pass, including a secondary-issued key rejected by the primary target. A 404, redirect, transport error, missing creator evidence or failed allowed control cannot qualify the boundary. This checks company-scoped agent identity; it does not establish human membership-role policy.

The secondary credential belongs to a disposable HTTP-adapter agent paused before key creation. It has no model connection, no model budget and no scheduled heartbeat. The case requires zero task and secondary-agent heartbeat runs. It compares complete, settled primary-agent history snapshots and rejects any new primary run, including work outside the owned tasks. A busy or incomplete primary baseline cannot qualify. The list API omits native retry state, so each historical run must also have a readable detail receipt with a settled native phase (or explicit legacy state). Persisted run revisions and lifecycle timestamps must remain unchanged, including when work resumes under an existing run ID. Saved passes also reject collected runs or nonempty model usage. Credentials stay private to the provider. Both keys must be revoked, and each old key must return 401 on a previously successful read. Cleanup terminates the owned secondary agent and retains only cancelled owned task history.

A dedicated secondary workspace can persist between campaigns. Its read sentinel, created tasks, attempted writes, agent and keys remain campaign-owned. Exact intents are durable before POST, and lost receipts reconcile through owner reads without replaying creation. The provider holds a separate secondary fixture lease and preserves the primary stack lease. Evidence and cleanup must be confirmed before lease release. Registration, assertion calibration and fixture setup do not establish a deployed pass or daily-plan admission.