mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-11-04 11:01:16 +01:00
## 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>
141 lines
8.4 KiB
TypeScript
141 lines
8.4 KiB
TypeScript
import { fileURLToPath } from "node:url";
|
|
import { featureIndex } from "../../scripts/feature-map.mjs";
|
|
import type { ReportExecution } from "./report-catalog.js";
|
|
|
|
export const journeyFeatureMap = featureIndex(fileURLToPath(new URL("../../", import.meta.url)));
|
|
export const journeyFeatures = journeyFeatureMap.sections.flatMap((section) =>
|
|
section.features.map((feature) => ({ ...feature, section: section.title })),
|
|
);
|
|
const capabilities = new Map(journeyFeatures.flatMap((feature) =>
|
|
feature.capabilities.map((capability) => [capability.id, { ...capability, feature }] as const),
|
|
));
|
|
const associations = new Map<string, readonly string[]>();
|
|
|
|
function associate(suites: readonly string[], cases: Record<string, readonly string[]>) {
|
|
for (const [caseId, ids] of Object.entries(cases)) {
|
|
for (const id of ids) if (!capabilities.has(id)) throw new Error(`Unknown journey capability: ${id}`);
|
|
for (const suite of suites) associations.set(`${suite}/${caseId}`, ids);
|
|
}
|
|
}
|
|
|
|
associate(["product-journeys"], {
|
|
"company-access-isolation": ["companies/selection", "access/resources"],
|
|
"repository-continuation": ["projects/task-context", "documents-artifacts/attachments"],
|
|
"routine-scheduled-run": ["routines/definition", "routines/triggers", "routines/history", "routines/management"],
|
|
"file-input-answer": ["documents-artifacts/attachments", "documents-artifacts/documents"],
|
|
"inbox-triage": ["inbox-search/views", "inbox-search/triage"], "task-create": ["tasks/create"], "ask-question": ["questions-and-approvals/questions"],
|
|
"question-answer-resume": ["questions-and-approvals/questions", "questions-and-approvals/continuation"],
|
|
"followup-while-running": ["steering/queue"],
|
|
"hire-delegate-reuse": ["agents/hire", "documents-artifacts/documents"],
|
|
"project-task-context": ["projects/lifecycle", "projects/task-context", "tasks/create"],
|
|
"task-properties": ["tasks/properties"], "task-document-persistence": ["documents-artifacts/documents"], "task-search-filter": ["tasks/organize"], "cloud-onboarding": ["onboarding/first-task"] });
|
|
associate(["antigravity-agent-journey"], {
|
|
"saved-subscription-two-turn": ["agents/hire", "agents/instructions", "runs-adapters/validation", "runs-adapters/session",
|
|
"tasks/create", "tasks/completion", "documents-artifacts/attachments", "documents-artifacts/work-products"],
|
|
});
|
|
associate(["core-compatibility", "openrouter-model-breadth", "extended-harnesses", "grok-qualification", "grok-subscription-qualification", "kimi-qualification"], {
|
|
"message-marker": ["runs-adapters/runs"],
|
|
"hello-complete": ["runs-adapters/runs"],
|
|
"plan-revise-accept": ["questions-and-approvals/plan-review"],
|
|
"plan-approve-complete": ["questions-and-approvals/plan-review"],
|
|
"ask-question": ["questions-and-approvals/questions"],
|
|
"question-resume-complete": ["questions-and-approvals/questions", "questions-and-approvals/continuation"],
|
|
});
|
|
associate(["local-session-integrity", "extended-harnesses", "grok-qualification", "grok-subscription-qualification", "kimi-qualification"], {
|
|
"structured-question-resume": ["questions-and-approvals/continuation", "runs-adapters/session"],
|
|
"structured-question-restart-resume": ["questions-and-approvals/continuation", "runs-adapters/session"],
|
|
});
|
|
associate(["question-resume"], {
|
|
"question-answer-resume": ["questions-and-approvals/questions", "questions-and-approvals/continuation"],
|
|
});
|
|
associate(["feedback-persistence"], {
|
|
"complaint-and-suggestion": ["activity/agent-commentary"],
|
|
});
|
|
associate(["continuation"], {
|
|
"answer-updates-scope": ["questions-and-approvals/questions", "questions-and-approvals/continuation"],
|
|
"clarification-not-approval": ["questions-and-approvals/questions", "questions-and-approvals/confirmations"],
|
|
"revision-preserves-approval": ["questions-and-approvals/plan-review"],
|
|
"completed-action-resume": ["questions-and-approvals/continuation"],
|
|
"question-tool-documentation": ["questions-and-approvals/questions"],
|
|
"provider-question-bridge": ["questions-and-approvals/questions", "questions-and-approvals/continuation"],
|
|
});
|
|
associate(["first-task", "confirmation-replies", "completion-updates"], Object.fromEntries([
|
|
"interview-first-response", "clear-task-first-response", "ambiguous-task-first-response",
|
|
"plain-message-first-response", "plan-first-response", "interview-plan-accept",
|
|
"task-card-accept", "accept-while-running", "task-reply-accept", "clarify-propose-accept",
|
|
"revise-accept", "reject-no-execution",
|
|
].map((id) => [id, ["onboarding/first-task"]])));
|
|
associate(["first-task"], { "ordinary-task-control": ["tasks/completion"] });
|
|
associate(["confirmation-replies"], {
|
|
"confirmation-ambiguous": ["questions-and-approvals/confirmations", "questions-and-approvals/audience"],
|
|
"unanswered-question-return": ["questions-and-approvals/dismiss-and-reopen", "questions-and-approvals/continuation"],
|
|
});
|
|
associate(["agent-chat", "agent-chat-hardening", "grok-qualification", "grok-subscription-qualification", "kimi-qualification", "stock-harness"], {
|
|
"create-backlog": ["tasks/create"],
|
|
"reassign-task": ["tasks/properties"],
|
|
"continuity-restart": ["agent-chat/history", "runs-adapters/session"],
|
|
"new-session": ["agent-chat/history", "runs-adapters/session"],
|
|
"stop-new-resume": ["steering/stop-and-pause", "runs-adapters/session"],
|
|
"stop-startup-new-resume": ["steering/stop-and-pause", "runs-adapters/session"],
|
|
"plan-handoff": ["agent-chat/handoff", "questions-and-approvals/plan-review"],
|
|
"clarify-reuse": ["projects/task-context"],
|
|
"multi-repository": ["projects/repositories"],
|
|
"hire-delegate-reuse": ["agents/hire", "task-coordination/delegation"],
|
|
"committed-send-retry": ["recovery/unknown-outcome"],
|
|
});
|
|
associate(["agent-chat-qualification"], {
|
|
"active-reassignment": ["tasks/properties"],
|
|
"worker-crash-retry": ["recovery/source-action"],
|
|
});
|
|
associate(["agent-chat-stories"], {
|
|
"enable-disable-resume": ["agent-chat/history"],
|
|
"followup-while-running": ["steering/queue"],
|
|
"revise-while-running": ["steering/steer"],
|
|
});
|
|
associate(["completion-updates"], Object.fromEntries([
|
|
"handoff-completion-idle", "handoff-completion-busy", "handoff-completion-multiple", "handoff-completion-restart",
|
|
].map((id) => [id, ["agent-chat/handoff", "tasks/completion"]])));
|
|
associate(["everyday-workflows", "native-connection-guidance", "grok-qualification", "grok-subscription-qualification"], {
|
|
"build-revise": ["documents-artifacts/work-products"],
|
|
"delegate-feedback": ["task-coordination/delegation", "steering/steer"],
|
|
"agent-review-handoff": ["task-coordination/signoff"],
|
|
"hire-reuse": ["agents/hire", "task-coordination/delegation"],
|
|
"service-approve": ["questions-and-approvals/tool-review", "app-permissions/action-policy"],
|
|
"service-decline": ["questions-and-approvals/tool-review", "app-permissions/action-policy"],
|
|
"connection-decline": ["connection-setup/task-request"],
|
|
"provider-decline": ["connection-setup/task-request"],
|
|
"provider-second": ["connection-setup/task-request"],
|
|
"provider-native": ["connection-setup/task-request"],
|
|
"agentmail-setup": ["connection-setup/task-request"],
|
|
"recover-controller": ["recovery/user-continuation", "documents-artifacts/work-products"],
|
|
"stop-redirect": ["steering/stop-and-pause"],
|
|
"create-skill-studio": ["skills/authoring"],
|
|
});
|
|
associate(["hiring-templates"], { "hire-coder-template-reuse": ["agents/hire", "task-coordination/delegation"] });
|
|
associate(["kimi-qualification"], {
|
|
"file-edit-validate": ["documents-artifacts/work-products"],
|
|
"native-write-permission-stop": ["steering/stop-and-pause", "questions-and-approvals/tool-review"],
|
|
"native-write-provider-loss": ["recovery/diagnosis", "questions-and-approvals/tool-review"],
|
|
});
|
|
associate(["task-workspaces"], {
|
|
"task-directory-repository-resume": ["workspaces/binding", "workspaces/provisioning", "projects/repositories"],
|
|
});
|
|
associate(["task-titles"], {
|
|
"prompt-title-standard": ["tasks/create"],
|
|
"prompt-title-ask": ["tasks/create"],
|
|
"preserve-explicit-title": ["tasks/create"],
|
|
});
|
|
associate(["pi-controls", "native-active-stop"], {
|
|
"pending-permission-stop": ["steering/stop-and-pause"],
|
|
"same-turn-steering": ["steering/steer"],
|
|
});
|
|
|
|
export function journeyCapabilities(execution: Pick<ReportExecution, "suite" | "task">) {
|
|
return (associations.get(`${execution.suite.id}/${execution.task.id}`) ?? [])
|
|
.map((id) => capabilities.get(id)!);
|
|
}
|
|
|
|
export function journeyFeatureIds(execution: Pick<ReportExecution, "suite" | "task">) {
|
|
return [...new Set(journeyCapabilities(execution).map((capability) => capability.feature.id))];
|
|
}
|