Commit Graph
7 Commits
Author SHA1 Message Date
1477d1ecea test: remove the no-op sequential describe modifier (#15286)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Paperclip runs the server and runner test suites with Vitest.
> - Vitest 5 removes the deprecated `describe.sequential` property, so
the pending Vitest 5 upgrade fails the type-check and test jobs.
> - `describe.sequential` only changes behaviour inside a
`describe.concurrent` suite, or when `sequence.concurrent` is on.
> - This repository has neither, so the modifier changed nothing at run
time.
> - The benefit is that the Vitest 5 upgrade can land, and the test
files lose a modifier that did no work.

## Linked Issues or Issue Description

Refs: #12969

## What Changed

- Replace every `describe.sequential` use with a plain `describe` call.
- Drop the `{ concurrent: false }` suite option from the two runner test
files.
- Add a comment to `server/vitest.config.ts` that records why these
suites must run one test at a time.
- Leave the package manifests and the lockfile unchanged.

## Why the modifier did nothing

The Vitest documentation states that `describe.sequential` is useful to
run tests in sequence inside a `describe.concurrent` suite, or with the
`--sequence.concurrent` option. `sequence.concurrent` defaults to
`false`.

This repository satisfies neither condition:

- No test file uses `describe.concurrent`, `it.concurrent`, or
`test.concurrent`.
- `server/vitest.config.ts` sets `sequence.concurrent: false`, with
`maxWorkers: 1`, `maxConcurrency: 1`, and `isolate: true`.
- `packages/paperclip-runner/vitest.config.ts` sets no `sequence` block,
so the `false` default applies.

`packages/db` and `cli` already run the same embedded-Postgres suites
with a plain `describe`, and those jobs are green. The server package
was the only outlier.

The modifier did carry one real piece of knowledge: these suites need
their tests to run one at a time. The new comment in
`server/vitest.config.ts` records that reason next to the setting that
enforces it.

## Verification

- `git grep` for `describe.sequential` returns nothing outside
`node_modules`.
- The author ran the changed server test files under the installed
Vitest 4, and the results match the results without this change.
- Two very large embedded-Postgres test files exceeded the author's
local memory limit, so the CI test jobs cover those two.
- The two changed runner test files have pre-existing local failures
caused by a missing Rust toolchain and a missing global `pnpm` binary.
The failures are identical with and without this change.
- The author type-checked the changed files and found no new error.
- CI must pass the typecheck, build, server test, and runner verify
jobs.

## Risks

- Low risk. Suite execution stays serial, because the Vitest config
enforces it.
- The change adds no dependency and changes no package manifest or
lockfile.
- A future change that turns `sequence.concurrent` on would break these
suites. The new config comment warns against it.

## Model Used

- Claude Sonnet 5 — code edits and local verification.
- OpenAI Codex, GPT-5 — the earlier revision of this branch.

## Test plan

- [x] Every CI check reaches a terminal green state. A pending or queued
check is not a pass.
- [x] The `Typecheck + Release Registry` job passes. This change must
not introduce a type error.
- [x] The `Build` job passes.
- [x] The server test jobs and the runner verify jobs pass.
- [x] Greptile re-reviews this commit set and posts a passing verdict.
The dependabot waiver does not apply to this pull request.
- [x] `mergeable` reads `MERGEABLE` as a terminal value.

## 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 linked the related public issue with `Refs: #12969`
- [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 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: Priya Raman <priya.raman@paperclip.ing>

---------

Co-authored-by: Priya Raman <priya.raman@paperclip.ing>
Co-authored-by: Paperclip <noreply@paperclip.ing>
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Co-authored-by: nickyleach <331803+nickyleach@users.noreply.github.com>
2026-10-06 07:19:15 -07:00
DottaandPaperclip 889947c238 feat: add experimental native chat connectors (#13038)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - People also ask agents for work in their existing chat tools.
> - Each external conversation needs one task and a current authorized
source.
> - Retries, Stop, and provider failures must not duplicate work or
expose private data.
> - The first chat PR establishes the opt-in provider and data
contracts.
> - This PR adds experimental channel integration and its durable
control plane.
> - Users can request work from connected channels and inspect delivery
in Paperclip.

## Linked Issues or Issue Description

Refs #13100 and #13092. This is the second of exactly two chat PRs.
Foundation #13100 is merged and changed 143 files. Runner prerequisite
#13092 is also merged. This PR changes 400 files against master, below
the 500-file review limit. It contains no wireframe images or HTML
galleries.

## What Changed

- Add native Slack, GitHub, Microsoft Teams, Telegram, and Discord chat
connections. Keep chat disabled unless the operator enables experimental
chat connectors. Preserve the production GitHub tool connection and its
normal setup path.
- Bind each provider bot identity to one immutable Paperclip agent. Bind
each admitted external conversation to one task. Paperclip owns tasks,
runs, permissions, and audit records.
- Add durable admission, per-conversation queues, questions, task
controls, progress, final replies, images, files, and delivery receipts.
Board comments remain internal unless explicitly sent to the channel.
- Check current identity, provider reach, resource access, credentials,
runtime generation, and exact source before provider effects. Keep
private responses private. Never send raw reasoning, private logs,
credentials, or tool arguments.
- Hold uncertain sends for explicit audited resolution. Make Board
Send-to-channel atomic and idempotent. Keep reconnect and setup
credentials in Paperclip secret storage.
- Preserve current native-runner authority across retries, lost
acknowledgements, and recovery. Keep immutable input and completion
contracts separate from newer user input. Receipt reconciliation cannot
launch a provider.
- Reconcile chat close/new ordering and provider-effect lock order.
Audit resource access changes in the same transaction. Submit only the
selected resource from each UI toggle so stale pages cannot undo
unrelated access changes.
- Drain Codex stdout before certifying process exit. Bound the drain
with the existing shutdown grace. Preserve observed terminal authority
without treating an undrained process as successful or reusable.
- Incorporate master `018ca5da` with its ACP Stop, mobile task layout,
runner packaging, and official lock changes. Preserve dedicated
chat-answer continuations in both directions when ordinary queued
comments are adopted after Stop.
- Fence late adapter readiness behind an earlier Stop for the same run.
Preserve verified cleanup for registered adapters. Handle single Stop,
agent pause, duplicate Stops, and failure release without creating a
false cancellation receipt.
- Incorporate master's `6dd48cad4` wake-queue extraction. Preserve exact
failed-chat retry authorization and lineage, retired question-source
suppression, and the block on generic recovery that would discard the
admitted source. Fresh deferred input retains its separate promotion
path.
- Incorporate master `2a05b5ed3` and its queue-admission extraction,
simplified transaction ports, and separate runner CI job. Preserve exact
durable receipts, actor separation, and dedicated-answer isolation
through the new module. A failed receipt insert rolls back the
accompanying deferred-wake merge.

## Verification

Current head: `afe19299d06253cb628eb398e91d1200ea9f412a`, incorporating
master `2a05b5ed3457ea33efd6895520447d1d97fe98d8`. The conflicts are
resolved. This successor fixes two test-harness boundaries exposed by
CI: per-case route-module preparation and actual durable-save completion
before intentional runner termination. Production code and all existing
test/turn deadlines are unchanged. [Exact-head Greptile
review](https://github.com/paperclipai/paperclip/pull/13038#issuecomment-5587250594)
is **5/5**, completed September 10 at 13:20:55 UTC, with no actionable
findings or open review threads. [Fresh exact-head
CI](https://github.com/paperclipai/paperclip/actions/runs/34481724341)
passes **all 24 jobs**, including Build and both required aggregates.
Normal exact-head guarded merge was attempted and rejected by the
remaining branch approval policy: CODEOWNER review is required and no
human approval is present. Normal **squash auto-merge is enabled** as of
September 10 at 13:36:26 UTC. Requested CODEOWNERS have been notified;
no approval bypass or self-approval was used. Earlier-head results below
remain historical evidence, not qualification of this successor.

- Final exact-head Linux evidence: 995/995 chat integration cases; 36/36
agent-skills routes; 35/35 runner live-session cases, including real
process kill/resume; 1948 runner Vitest cases with three existing
benchmark/platform guards; 870/870 API-authority cases; and 104 browser
cases with four existing optional skips. Rust, conformance/replay, full
repository build, typecheck, canary, all server/workspace shards, and
both required aggregates pass with normal CI concurrency. Earlier failed
attempts remain recorded below.

- Latest test-only qualification: 141/141
route/permissions/authentication cases pass in separate cold forks, with
plain server types and independent review clear. The real-runner suite
passes 35/35, with plain runner types and independent review clear. A
controlled premature-save acknowledgement fails as expected; matching
ownership/effect/process evidence, rejected saves, real turn outcome,
test abort, and pre-kill liveness are covered. No local reproduction of
the original CI scheduling failure is claimed. The preceding [CI
run](https://github.com/paperclipai/paperclip/actions/runs/34479680858)
passes 21/24 jobs, including all 995 Linux chat cases and browser
aggregate (104 passed, four existing optional skips); only Build, the
skills serialized shard, and the required verification aggregate fail.
Its exact-head Greptile review was 5/5. Both failed job logs are
retained.

- Final fixture qualification: all eight focused Discord cases and all
995 chat integration cases pass. The exact modal statement/PID is
observed before taking the real connection lock; the test then proves
its actual blocking relationship before mutation. Original SQL
execution, provider behavior, negative assertions, and 1s/15s timeouts
remain unchanged. Independent review is clear and test/production hashes
remain frozen. The preceding [CI
attempt](https://github.com/paperclipai/paperclip/actions/runs/34477184777)
passed 22 jobs, including Build/runner, typecheck, canary, all other
test shards, and browser aggregate (104 passed, four existing optional
skips); the two fixture failures and failed verification aggregate
remain recorded, not relabeled as a pass.

- Current queue-module composition: 308/308 recovery/batching/queue/Stop
tests; 995/995 full chat integration; 89/89 module tests, including real
PostgreSQL receipt-insert rollback; 24/24 workflow/module-boundary
tests; plain server and UI types. All four actual local process/ACP
browser paths pass in 1.4 minutes. Fresh databases, no skips or retries,
stable reviewed source hashes. The initial boundary failure is retained;
its no-op service wrapper was removed without changing recovery context
or weakening the check. An exploratory standalone test-directory
typecheck fails because its new upstream transformation config is not a
standalone typechecking project; standard CI/build does not invoke it,
and no configuration was weakened to suppress those diagnostics.

- The preceding head `e02a63d462ce5d47433b0aeb632bb6fd20aab1ba` passed
[all 24 CI
jobs](https://github.com/paperclipai/paperclip/actions/runs/34436462958)
and exact-head Greptile review at 5/5. Required CODEOWNER review
prevented its normal merge before master advanced again.

- Final extracted-module composition: 307/307 recovery, batching, queue
and Stop-control tests; 995/995 full chat integration; 49/49 module
tests including eight PostgreSQL adapter cases; and 19/19 issue-update
tests. Plain server types pass. All four actual local process/ACP
browser paths pass in 1.3 minutes. Fresh databases, no skips or retries
in these cohorts, frozen source hashes, and independent review clear.

- The preceding head `3e4e1c1c` passes [all PR CI
jobs](https://github.com/paperclipai/paperclip/actions/runs/34415826820),
including Build and required `ci / verify` and `ci / e2e`. Both the
original Rust failure and the previously load-sensitive lineage fixture
pass with unchanged Linux concurrency. Master advanced afterward and
required this reconciliation.
- Final master composition: 448/448 focused UI tests, 186/186 adapter
tests, 24/24 queue/control tests, and 11/11 packaging tests. Plain UI,
server, shared, and adapter types pass. Token gates and diff checks
pass. Independent server and UI reviews are clear.
- Stop-registration regression: both real-service cases fail against
exact `a95` source and pass with the fix. The full corrected
recovery/control suite passes 265/265. Duplicate-owner and failed-Stop
controls also pass. Plain server types pass. The readiness barrier
prevents provider startup without adding an acknowledgment to an already
terminal run.
- Final qualification strengthens terminal-field equality and repeats
both affected cases successfully on a fresh database. All four actual
local process/ACP browser paths pass again in 1.3 minutes, without skips
or retries. The final screenshot shows Cancelled, a paused subtree,
retained input, and no error toast.
- Two new actual-service regressions fail before the merge fix. They
prove that queued-comment adoption could consume a dedicated chat answer
or add unrelated input to that answer. The fixed four-case cohort
passes, including ordinary upstream continuation and adapter Stop
controls. Full recovery passes 257/257. All four actual local
process/ACP Stop browser flows pass in 1.4 minutes, without skips or
retries, on a fresh database.
- The unchanged runner artifact was qualified with 171/171 transport
tests, 870/870 API-authority tests, conformance 1/1, and replay 11/11.
Six controlled reader tests prove the exit/drain repair. Its local
serial Rust workspace passed 546 top-level cases plus two invoked
helpers; the later passing Linux CI supplies default-concurrency
evidence.
- Prior exact-source full chat integration passes 995/995. Settings
regressions cover concurrent stale pages, 501 destinations, pending
state, rejected updates, and explicit retry. These deterministic tests
do not prove live provider behavior.
- Retained failed attempts and their causes are in the [qualification
log](https://github.com/paperclipai/paperclip/blob/afe19299d06253cb628eb398e91d1200ea9f412a/doc/plans/chat-adapters/2026-09-08-chat-queue-and-webhook-repair.md).
The first merge adapter run timed out while macOS slept for 290 seconds.
Its unchanged repeat passed with a temporary sleep guard. No assertion,
deadline, or CI gate was weakened.

Review commands include `pnpm --filter @paperclipai/server exec vitest
run src/__tests__/heartbeat-process-recovery.test.ts
src/__tests__/issue-queued-comments-routes.test.ts` and `pnpm exec
playwright test --config tests/e2e/playwright.config.ts
tests/e2e/acp-stop-continuation.spec.ts`. Database suites require fresh
disposable databases. See the [browser
runbook](https://github.com/paperclipai/paperclip/blob/afe19299d06253cb628eb398e91d1200ea9f412a/doc/plans/chat-adapters/2026-09-04-chat-adapters-browser-e2e-runbook.md)
for provider setup and separate live acceptance steps.

## Risks

- This remains experimental. Deterministic tests and bounded live
evidence do not establish every provider feature, tenant, permission
layout, or media shape. Teams work-tenant qualification is still open.
- Failed and uncertain provider effects remain visible and can require
operator action. A transport receipt does not prove recipient
visibility.
- Native controller and runner artifacts must remain compatible.
Preserve lease ownership, terminal authority, source binding, and
quarantine during future changes.
- Access and audit rows commit together, but activity notifications
remain best-effort. This is not a new durable event outbox.
- The PR operation does not deploy a live server, replace its runner, or
change provider permissions. Remaining live qualification is documented
in the [temporary
handoff](https://github.com/paperclipai/paperclip/blob/afe19299d06253cb628eb398e91d1200ea9f412a/doc/plans/chat-adapters/2026-09-08-open-qualification-followups.md).

## Model Used

OpenAI Codex assisted with implementation, tool execution, testing, and
review. The work records `gpt-6-astra` assistance. The environment does
not report a context-window size. No private reasoning traces are
included.

## 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-09-10 10:06:45 -05:00
656ecfa585 fix(server): keep Date fields intact through secret redaction; harden chat notice timestamps (#10984)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - The task chat thread renders issue comments, system notices, and run
transcripts
> - The server routes comment payloads through the run-secret redaction
walker before it sends them
> - The walker rebuilds each object with `Object.entries`, and this
collapses `Date` instances to `{}`
> - The chat renderer then calls `.toISOString()` on an invalid date and
throws, and the thread falls back to the error banner
> - This pull request keeps `Date` instances intact in redacted
responses and makes the renderer safe against bad timestamps
> - The benefit is that task threads with system notices render
correctly again

## Linked Issues or Issue Description

**What happened**

Task threads that contain a system notice showed the banner "Chat
renderer hit an internal state error." in place of the conversation.
This occurred on many tasks.

**Expected behavior**

The thread renders all comments and system notices with correct
timestamps.

**Steps to reproduce**

1. Open a task that has at least one system notice comment (for example
a "Workspace ready" notice).
2. `GET /api/issues/{id}/comments` returns `createdAt: {}` for every
comment because the secret-redaction walker collapses `Date` objects.
3. The system-notice row calls `new Date({}).toISOString()`. This throws
`RangeError: Invalid time value` and trips the thread error boundary.

**Version / deployment**

Regression from #9934 (`e43f187ca`). It applies to all deployments that
include that commit.

## What Changed

- `server/src/services/run-secret-redaction.ts`:
`redactRegisteredSecretValues` now returns `Date` instances as-is. Dates
hold no redactable text, and the `Object.entries` rebuild turned them
into `{}`.
- `ui/src/components/IssueChatThread.tsx`: the system-notice row formats
its timestamp with a new `toValidIsoString` helper. A value that does
not parse as a date now degrades to "no timestamp" instead of a render
crash.
- Regression tests at three layers:
- Walker unit tests: `Date` values survive with and without registered
secret values.
- Route test: `GET /issues/:id/comments` serializes `createdAt` /
`updatedAt` as ISO strings.
- Render test: a system notice with a malformed `createdAt` renders
without the error boundary.

## Verification

- `npx vitest run --root server
src/__tests__/run-secret-redaction.test.ts` — 5 passed.
- `npx vitest run --root server
src/__tests__/issue-comment-redaction.test.ts` — 4 passed (embedded
Postgres route test).
- `cd ui && npx vitest run src/components/IssueChatThread.test.tsx
src/components/IssueChatThreadSystemNotice.test.tsx
src/lib/issue-chat-messages.test.ts` — 121 passed.
- Each new test was run against the unfixed code and failed there, which
confirms it guards the regression.
- A local sweep rendered 47 real issue threads through
`IssueChatThread`: 7 tripped the boundary before the fix, 0 after.

## Risks

- Low risk. The server change only preserves `Date` objects that the
walker destroyed before. String redaction behavior does not change, and
the registry-key stripping does not change.
- The UI change only affects the timestamp of system-notice rows and
omits it when the value is invalid.

## Model Used

- Claude Fable 5 (`claude-fable-5`), Anthropic. Agentic coding session
with tool use (file edits, shell, Vitest). No extended-context or
special reasoning mode.

## 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
- [x] I will address all Greptile and reviewer comments before
requesting merge

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-08-06 10:29:07 -05:00
Jannes StubbemannandClaude Opus 4.8 606e74d11f cloud_tenant: company-scoped tenants, never instance-admin (#7525)
## Thinking Path

> - Paperclip orchestrates AI agents for zero-human companies, and a
single server instance can host many companies.
> - The auth middleware (`server/src/middleware/auth.ts`) supports a
`cloud_tenant` mode where a trusted hosting proxy injects per-request
identity headers, designed originally for one-deployment-per-tenant
setups.
> - In that original setup, granting every cloud tenant the
`instance_admin` role was harmless; on a **shared, multi-tenant pool**
it means any paying tenant is admin of the whole instance and can reach
every other tenant's data.
> - A tenant only needs to own its own company — which it already gets
via the company membership the same code path upserts — so
instance-level admin is never appropriate for `cloud_tenant` actors.
> - This PR removes the `instance_admin` grant from the cloud-tenant
path and pins `isInstanceAdmin: false` on the resolved actor.
> - Greptile review then surfaced a follow-up gap: deployments that ran
the pre-hardening build still have stale `instance_admin` rows in
`instance_user_roles`, which other lookups (BetterAuth session path,
board API keys, and the authorization service's own DB re-check) would
still honor.
> - The follow-up commit closes that gap by purging stale rows at the
cloud-tenant auth boundary and by teaching the authorization service
that `cloud_tenant` actors are never instance admins.
> - The benefit is that shared-pool hosting becomes structurally safe:
tenants are company-scoped owners, never instance admins — including on
deployments upgrading from the older behavior.

## Linked Issues

- Refs #966 — managed SaaS multi-tenant hosting is the deployment shape
this hardening protects.
- Refs #5015 — same problem space: instance-admin-scoped credentials are
too broad for multi-company instances; tenants need company-scoped
access.

Neither issue is fully closed by this PR; it removes the instance-admin
grant from the `cloud_tenant` trusted-header path specifically.

## What Changed

- `server/src/middleware/auth.ts`
- Removed the `instanceUserRoles` insert that granted every cloud tenant
`instance_admin`; `resolveCloudTenantActor` now returns
`isInstanceAdmin: false` (was `true`).
- `resolveCloudTenantActor` now **deletes** any stale `instance_admin`
row for the authenticated tenant user on every trusted-header request,
so grants left behind by pre-hardening deployments are purged at the
source (closes the Greptile P2: stale rows could otherwise re-elevate
the user via the BetterAuth session path, board API keys, or the
authorization service).
  - The function is `export`ed so it can be unit-tested directly.
- `server/src/services/authorization.ts`
- `authorizationService` previously re-checked `instanceUserRoles` from
the DB regardless of the actor flag, which would have elevated even
hardened `cloud_tenant` actors while a stale row lingered. Actors with
`source === "cloud_tenant"` are now never elevated to instance admin;
other board actors keep the existing lookup.
- `server/src/services/authorization.ts` +
`server/src/middleware/auth.ts` (follow-up commit `dc57a71c7`)
- CI on the merge ref surfaced that elevation removal alone strands real
cloud tenant users: board actors only ever reached `issue:read` /
`issue:mutate` through instance-admin elevation (`permissionForAction`
maps both to no grant key). `decide()` now grants `cloud_tenant` actors
with an **active membership in the resource company** the same read
surface as a same-company agent (`agent:read`, `company_scope:read`,
`issue:read`, `project:read`) plus `issue:mutate` for non-viewer members
— cross-company access stays denied (new `allow_company_member` reason).
- `resolveCloudTenantActor` seeds the standard role-default permission
grants (`ensureHumanRoleDefaultGrants`) so granted actions (e.g.
`tasks:assign`, `agents:create` for owners) work without elevation.
- Master-side route tests that stubbed cloud tenant actors with
`isInstanceAdmin: true` now seed a real membership and assert under the
hardened contract (`issue-identifier-routes`,
`multilingual-issues-routes`, `issue-comment-redaction`).
- Tests
- `server/src/middleware/cloud-tenant-actor.test.ts` (new): cloud tenant
is never instance-admin, is scoped to exactly the one company from its
stack, still upserts user/company/membership, purges stale
`instance_admin` rows, returns null without the server token, and maps
non-owner stack roles without elevating.
- `server/src/__tests__/auth-session-route.test.ts`: end-to-end
middleware regression — a user with a stale `instance_admin` row stops
being elevated via the session path once they authenticate through the
cloud-tenant path (with a control assertion showing the pre-purge
elevation).
- `server/src/__tests__/authorization-service.test.ts` (embedded
Postgres): a `cloud_tenant` actor with a stale `instance_admin` row in
the real DB cannot cross company boundaries, while a `session` actor
with the same row still resolves `allow_instance_admin`.

## Verification

Run from the repo root after `pnpm install --frozen-lockfile`:

```bash
cd server
npx vitest run src/middleware/cloud-tenant-actor.test.ts src/__tests__/auth-session-route.test.ts
# 9 tests passed
npx vitest run src/__tests__/authorization-service.test.ts
# 16 tests passed (embedded Postgres)
pnpm typecheck
# clean
```

Also ran the broader auth-related suites locally (`auth-routes`,
`authz-company-access`, `better-auth`, `adapter-routes-authz`,
`express5-auth-wildcard`): 8 files, 58 tests, all passing.

## Risks

- **This touches authentication and authorization paths directly.**
Mistakes here are security bugs in both directions; review accordingly.
- **Behavioral change for existing `cloud_tenant` deployments:** tenants
that previously (incorrectly) had instance-admin lose it — including the
ability to see/manage other companies on the instance. This is the
intended hardening, but any single-tenant deployment that relied on the
cloud-tenant identity for instance administration must provision a
separate admin identity.
- **The purge is destructive by design:** if an operator's
instance-admin identity is *also* provisioned through the cloud-tenant
headers (same user id), its `instance_admin` row will be deleted on the
next trusted-header request. Operators should hold admin through a
non-cloud-tenant identity.
- **Residual gap (documented, not fixed here):** a deployment that ran
the old cloud_tenant build and then *disabled* cloud-tenant mode keeps
stale rows until the affected user re-authenticates through the cloud
path. A data migration was considered and deliberately avoided: there is
no reliable SQL predicate for "cloud-tenant-provisioned user" (no source
column), so a migration risks deleting legitimate admins.
- No schema or migration changes; no UI changes.

## Model Used

- Claude Fable 5 (claude-fable-5, 1M context), extended thinking + tool
use, via Claude Code — this revision; original PR authored in an earlier
Claude Code 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 (none duplicate this; related issues Refs #966 / #5015 are
linked in the issue section)
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] If this change affects the UI, I have included before/after
screenshots (no UI changes)
- [x] I have updated relevant documentation to reflect my changes (no
existing docs reference `cloud_tenant` mode)
- [x] I have considered and documented any risks above
- [x] I will address all Greptile and reviewer comments before
requesting merge

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-11 17:59:32 -07:00
Dotta 7fb40264f8 [codex] Revert PR #7678 (#7765)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Issue comment wake handoffs are part of the control-plane execution
loop that decides when agents resume work after comments and issue
updates.
> - PR #7678 changed that wake handoff behavior in server issue routes,
heartbeat context, and related tests.
> - The change broke an important workflow after merge, so the safest
immediate fix is to restore the pre-#7678 wake behavior.
> - This pull request reverts the wake-handoff behavior from PR #7678
while keeping narrow review-requested safeguards that prevent known
runtime/test regressions.
> - The benefit is that Paperclip returns to the last known working wake
behavior without reintroducing avoidable UUID skill lookup and
annotation-resolution test gaps.

## Linked Issues or Issue Description

Refs: #7678

Bug context:
- What happened: PR #7678 was reported to have broken an important
Paperclip workflow after it merged.
- Expected behavior: Paperclip should preserve the prior issue comment
wake handoff behavior until a corrected change is ready.
- Steps to reproduce: Use the workflow affected by PR #7678's issue
comment wake handoff changes.
- Paperclip version/commit: `master` after merge commit
`4da79a88c67e54084d40bd18cada5ee5c8be23da`.
- Deployment mode: Paperclip control-plane server behavior.

## What Changed

- Reverted merge commit `4da79a88c67e54084d40bd18cada5ee5c8be23da` from
PR #7678 to restore pre-#7678 wake-handoff behavior.
- Preserved the safe accepted-plan routing check so `parseObject(...)`
is not used as a boolean.
- Preserved UUID filtering for run-scoped skill mentions so legacy
non-UUID skill IDs do not reach a Postgres UUID lookup.
- Restored the annotation thread-resolution test guard that verifies
resolving a thread does not wake the assignee.

## Verification

- `pnpm run preflight:workspace-links && NODE_ENV=test
PAPERCLIP_HOME=/tmp/... PAPERCLIP_INSTANCE_ID=pap10614-revert
TMPDIR=/tmp/... pnpm exec vitest run --project @paperclipai/server
--no-file-parallelism --maxWorkers=1
server/src/__tests__/document-annotation-routes.test.ts
server/src/__tests__/heartbeat-project-env.test.ts
server/src/__tests__/heartbeat-accepted-plan-workspace-refresh.test.ts
server/src/__tests__/heartbeat-context-summary.test.ts`
- Result: 4 test files passed, 26 tests passed.
- Earlier targeted revert verification also passed: 4 test files, 50
tests.

## Risks

- This intentionally restores behavior from before PR #7678, so intended
wake-handoff improvements from that PR are removed.
- The PR is no longer a byte-for-byte revert because Greptile identified
two narrow safeguards worth preserving.
- Low migration risk: no schema or dependency changes are included.
- Follow-up work may still be needed to reintroduce the desired wake
handoff behavior without the regression.

## Model Used

OpenAI Codex, GPT-5 coding agent in this Paperclip heartbeat, with
shell/tool execution and repository write access.

## 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 run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] If this change affects the UI, I have included before/after
screenshots
- [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
2026-06-08 10:35:58 -05:00
DottaandPaperclip 4da79a88c6 [codex] Refine issue comment wake handoffs (#7678)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - The heartbeat and issue-comment routes decide when an assigned agent
wakes up and what context it receives.
> - Passive comments and annotation notes can currently wake assignees
even when no actionable state changed.
> - Accepted planning confirmations also need to preserve recent plan
comments so child-issue creation does not lose board/user constraints.
> - Runtime skill mentions should only send UUID ids into database
lookups, because legacy slug-like ids are not valid runtime skill ids.
> - This pull request tightens those wake and handoff rules in one
server-side branch.
> - The benefit is fewer noisy agent wakeups and better accepted-plan
continuation context without changing the task model.

## Linked Issues or Issue Description

Internal Paperclip task: [PAP-10535](/PAP/issues/PAP-10535).

Problem or motivation:
Passive comments and annotation notes could wake the current assignee
even when no actionable state changed, and accepted plan continuations
needed recent plan comments preserved in the wake handoff. Runtime skill
mentions also needed to ignore non-UUID ids before database lookup.

Proposed solution:
Tighten server-side wake routing so passive comments do not wake
assignees unless they reopen the issue, preserve mention-targeted
wakeups, include recent non-deleted plan comments in accepted
confirmation wake payloads, and guard runtime skill mention lookup to
UUID-like ids.

Alternatives considered:
Leaving passive assignee wakeups in place was rejected because it keeps
generating noisy non-actionable heartbeats. Treating every skill
mention-like token as a runtime skill id was rejected because legacy
slug-like ids are not valid runtime skill ids.

Roadmap alignment:
This aligns with the V1 control-plane heartbeat contract by making
wakeups more intentional and preserving handoff context for approved
plans.

This PR was split from the local `master` branch on June 7, 2026. It
covers server-side heartbeat and comment-wakeup behavior only. I
searched GitHub for duplicate/related PRs; the results were broader
heartbeat/run PRs, not this exact passive-comment and accepted-plan
handoff change.

## What Changed

- Filter runtime skill mention extraction so only UUID-like skill ids
are looked up.
- Stop ordinary issue comments and document annotation comments from
waking the current assignee unless the comment reopens the issue.
- Keep mention-targeted wakeups intact while removing passive assignee
wakeups.
- Include recent non-deleted issue comments in accepted-plan
confirmation wake payloads and task markdown.
- Updated focused server tests for the new wakeup and accepted-plan
behavior.

## Verification

- `git diff --check origin/master..HEAD`
- `NODE_ENV=test pnpm exec vitest run
server/src/__tests__/heartbeat-project-env.test.ts
server/src/__tests__/document-annotation-routes.test.ts
server/src/__tests__/issue-comment-reopen-routes.test.ts
server/src/__tests__/issue-update-comment-wakeup-routes.test.ts
server/src/__tests__/heartbeat-context-summary.test.ts
server/src/__tests__/issue-comment-redaction.test.ts`
- `NODE_ENV=test pnpm exec vitest run
server/src/__tests__/heartbeat-accepted-plan-workspace-refresh.test.ts
server/src/__tests__/issue-comment-redaction.test.ts
server/src/__tests__/heartbeat-context-summary.test.ts`
- `pnpm --filter /server typecheck`
- PR checks green on head `a379a0264d384510ff8ac4a47fb1e44d7b556f68`
- Greptile rerun green on head
`a379a0264d384510ff8ac4a47fb1e44d7b556f68`: 9 files reviewed, 0 comments
added, 0 unresolved review threads

## Risks

- Medium behavioral risk: agents will no longer wake for passive
comments unless mentioned or unless the comment reopens/resumes the
issue. That is intentional, but any workflow relying on passive assignee
comment wakeups should use explicit mentions or structured resume paths.
- Low migration risk: no schema or migration changes.

> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.

## Model Used

OpenAI Codex coding agent based on GPT-5, with shell, git, GitHub CLI,
and local test execution. Exact hosted model variant and context-window
size were not exposed by the runtime.

## 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 run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] If this change affects the UI, I have included before/after
screenshots
- [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-06-07 06:24:48 -05:00
Dotta 7f70759e61 Redact deleted issue comments 2026-06-05 03:20:46 +00:00