Commit Graph
14 Commits
Author SHA1 Message Date
DottaandPaperclip d9d2147171 fix(auth): keep Cloud tenants on the Cloud sign-in flow (#14407)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Cloud owns human identity and passes a verified identity to each
tenant.
> - The tenant can report no session while the Cloud session is still
valid.
> - The access gate and direct `/auth` route then show the instance
password form.
> - This pull request sends those users through the configured Cloud
entry endpoint.
> - Cloud can renew the tenant session or show its login page, then
return to the original task.

## Linked Issues or Issue Description

**What happened?**

A Cloud tenant can display the self-hosted email/password form after an
instance session check returns no session. This gives Cloud users the
wrong login method.

**Expected behavior**

An active Cloud session renews tenant access automatically. A signed-out
user signs in through Cloud. Staging and production use their own
configured Cloud origins. Self-hosted instances keep their instance
login form.

**Steps to reproduce**

1. Open a Cloud tenant task or an `/auth?next=...` link.
2. Keep the Cloud session active but make the instance session check
return 401.
3. Observe the instance password form instead of Cloud session recovery.

**Deployment mode**

Cloud-managed authenticated instances. No database or server API
changes.

Searched related authentication PRs. Native self-hosted OIDC support in
#10411 is a separate feature; this change uses the existing Cloud entry
contract.

## What Changed

- Wait for deployment metadata before showing an instance login form.
- Use the health response's Cloud origin and stack slug for session
recovery.
- Preserve the tenant path, query, and fragment. Reject external and
recursive login return targets.
- Limit automatic recovery per tab. Show a manual Cloud retry after
failed recovery. Show service failures as errors.
- Keep self-hosted login and local trusted access. Add focused tests,
browser regressions, deployment documentation, and an unavailable-state
design example.

## Verification

- `pnpm -r typecheck` and `pnpm build` passed.
- UI typecheck and `pnpm check:token-gates` passed after the final UI
edits.
- All UI tests passed: 639 files, 6,777 tests.
- 63 focused Vitest tests passed across Auth, CloudAccessGate, Cloud
links, and recovery coordination.
- `pnpm exec playwright test --config tests/e2e/playwright.config.ts
tests/e2e/cloud-auth.spec.ts`: 5 passed. The tests use the real tenant
UI and database with a simulated Cloud HTTP endpoint. They cover both
Cloud origins, direct auth/task links, no password-form flash, preserved
URLs, reload, and self-hosted login.
- Hands-on browser test used the real Cloud gateway and a fresh tenant
build with disposable local data. Active Cloud session plus a forced
missing instance session returned to the task. An expired tenant cookie
also renewed automatically and returned to the task. Removing both
sessions reached the real Cloud email/social login UI. Persistent
failure stopped at the retry screen; retry succeeded after removing the
injected fault. The fixture used a loopback transport adapter and a
simulated signed-out OIDC issuer. No production session or deployment
was changed.
- The default local browser startup hit the host's embedded PostgreSQL
resource limit. The passing run used a separate disposable database on
the test PostgreSQL process.
- The full local `pnpm test:run` sweep was stopped after about 31
minutes once CI completed the full suite. It had reported 33 failures in
the unchanged runner API unit/integration files; both files pass in
isolation (1,749 + 28 tests). The local sweep did not reach the later
workspace/serialized groups. CI completed all of those groups
successfully.
- Greptile reviewed commit `d603fd4e39455de44da9dae81b72197096c0e1e8` at
5/5 with its only thread resolved. All CI gates are green on this
commit, including all general/serialized server groups, workspace tests,
Runner checks, typecheck, build, and all eight browser shards
([run](https://github.com/paperclipai/paperclip/actions/runs/36446230697)).

## Risks

- Recovery depends on valid Cloud origin and stack metadata. Incomplete
metadata shows an unavailable message instead of a password form.
- Browsers with session storage disabled use the manual Cloud link,
since automatic retries cannot be bounded across documents.
- The external identity provider's email/social login was not completed
in this local test. Existing Cloud authentication owns that flow.
- No migration, credential format, membership rule, or production
deployment changes.

## Model Used

OpenAI GPT-6 through Codex. The exact served variant and context-window
limit are not exposed in this session. Used reasoning, repository tools,
code execution, and browser testing.

## 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
#` 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-28 13:45:27 -05:00
TonioandClaude Opus 5 88a0f885e8 Brand lockup, no idle env-check card, no Mission row (#12074)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - New customers meet the product through an onboarding arc that ends
in the tenant wizard
> - A staging walk of that arc found three rough edges: an ad-hoc icon
standing in for the brand, an environment-check card that narrates a
probe the flow already runs on its own, and a review checklist that
still lists a Mission the arc stopped asking for
> - Each one makes the product look less finished than it is at the
exact moment a customer decides what it is
> - This pull request renders the brand lockup, hides the idle
environment-check card while keeping the probe and its failure surface,
and drops the Mission row
> - The benefit is a first-session arc that reads as one product, with
no controls for questions nobody was asked

## Linked Issues or Issue Description

No public issue exists. The changes come from walking the sign-up arc on
a staging fleet.

**What happened:**
The model step shows an "Adapter environment check" card with a "Test
now" button even though pressing Connect runs the same probe and blocks
a failing hire. The review step lists "Mission" in its checklist
although onboarding no longer asks for one. The auth page renders a
sparkles icon beside the word "Paperclip" instead of the brand lockup.

**Expected behavior:**
The model step shows the check only when a probe has found something to
fix. The review checklist lists only what onboarding set up. The brand
renders as the lockup asset used across surfaces.

**Actual behavior:**
An idle card narrates a probe that runs regardless. A permanent
unchecked row marks a question nobody was asked. The brand is a generic
icon plus text.

**Steps to reproduce:**
1. Sign up on a staging fleet and enter the tenant wizard.
2. On "Create your first agent", choose a role and press Next: the model
step shows the "Adapter environment check" card before anything has been
probed.
3. Continue to Review: the checklist lists "Mission" as a permanently
unchecked row.

**Additional context:**
The Mission row outlived the removal of the mission step (#11935). The
environment probe itself still runs on Connect and blocks a failing
hire; only its idle card is at issue. The brand lockup lands across all
three surfaces in the same round — paperclip-cloud#270 and
paperclip-id#58 carry the other halves.

## What Changed

- `PaperclipLockup` renders the brand asset (mark + wordmark, one
geometry, `fill="currentColor"`); the auth page uses it in place of the
sparkles icon.
- The adapter environment check's idle card (explainer + "Test now") no
longer renders. The probe still runs on Connect and still blocks a
failing hire.
- The check's failure content still renders when a probe has found
something — the blocking error points the customer at "the reported
checks", so they stay visible.
- Connect retries a cached failed probe instead of reusing it. With
"Test now" gone, Connect is the only retry, and a stale fail would lock
out a machine the customer has since fixed.
- The review checklist drops its "Mission" row.

## Verification

Run the tenant suite:

```
cd ui && npx vitest run
```

- 4365 tests pass across 471 files; `npx tsc --noEmit` is clean.
- New test drives the wizard to the model step and asserts the
environment-check card is absent, anchored on "Connect a model" so an
unrendered step cannot pass as an absence.
- The review assertions anchor on the remaining rows ("Organization
name", "Agent created", "Model connected").

## Risks

- **Behavioral change:** a cached *failed* probe is re-run on Connect
instead of reused. Pass and warn results are still cached. This only
affects the retry path that "Test now" used to serve.
- **Hidden, not removed:** the environment check machinery is intact;
only the idle card is gone. A failing probe still blocks the hire and
still shows its checks.
- **Brand:** the wordmark now ships inside an SVG; its accessible name
carries the text. Screen readers announce "Paperclip" as before.

## Model Used

Claude Fable 5 (`claude-fable-5`) via Claude Code, with tool use and
code execution; earlier rounds on this branch's predecessor used Claude
Opus 5 (`claude-opus-5`).

## 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
- [x] My branch name describes the change and contains no internal
Paperclip ticket id
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [ ] 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 Opus 5 <noreply@anthropic.com>
2026-08-24 01:35:15 -07:00
TonioandClaude Opus 5 0817fbad92 fix(ui): scope invite membership checks to the signed-in account (#11417)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Which companies a person belongs to is an authorization fact the
server owns, and the UI caches the answer under a single `["companies"]`
key
> - That cache entry carries no account identity, and `main.tsx` sets
`staleTime: 30_000` for every query, so for thirty seconds after a
sign-in the previous account's list is served with no request at all
> - The invite landing page reads that list to decide whether the person
is already a member of the inviting company
> - A list that arrives with no loading state and no error therefore
looks authoritative while describing somebody else
> - This pull request makes the page trust only a list it fetched
itself, for the account signed in now
> - The benefit is that a membership decision stops depending on cache
freshness, which nothing in the app guarantees

## Linked Issues or Issue Description

No public issue exists. Refs #11380, #11382. The problem follows.

**What happened?**

`InviteLanding` read the shared `["companies"]` cache entry as proof of
membership in two places:

- The post-sign-in redirect called
`fetchQuery(companiesListQueryOptions)`, which returns the cached entry
without a request while it is inside the app-wide `staleTime`.
- An effect cleared the pending invite token whenever the cached list
contained the invited company.

Neither checked that the list belonged to the account signed in now. A
second account signing in on a warm tab, or a session that lapses
server-side, is enough to reach both.

**Expected behavior**

The page decides membership from a company list fetched for the current
session.

**Steps to reproduce**

1. On a self-hosted instance in `authenticated` mode, sign in as account
A, which belongs to company X.
2. Within thirty seconds, open an invite link for company X and sign in
as account B, which does not belong to it.
3. The page reads A's cached list, finds company X, and treats B as
already a member.

**Paperclip version or commit**

`master` at `2a4b4bc63`.

## What Changed

- `ui/src/pages/InviteLanding.tsx` — the membership query sets
`staleTime: 0` so it revalidates on mount, and the verdict is withheld
until that fetch lands, keyed on `isFetchedAfterMount`. The
token-clearing effect and the "already a member" branch both read
through that gate.
- `ui/src/pages/InviteLanding.tsx` — the post-sign-in path cancels
anything still in flight for the previous session, then forces a fetch
for the new one with `staleTime: 0`.
- `ui/src/pages/Auth.tsx` — sign-in resets the companies query instead
of invalidating it. Invalidation leaves the previous account's list
readable, and its fetch running, until the refetch returns.
- `ui/src/pages/InviteLanding.test.tsx` — coverage for the warm-cache
case, the token-clearing effect, and the `local_trusted` exemption.

### `local_trusted` is exempt

Those instances have no accounts, so the shared list is the only
identity there is. `membershipIsAccountScoped` is false there and the
gate stays open.

### Rebased onto the account-keyed cache

#11488 landed while this was open and keys the company list by account,
so the page can no longer reach another account's list at all. Two
things changed here as a result:

- The post-sign-in read now calls `fetchCompanyListForCurrentAccount`,
which replaces the `cancelQueries` plus forced `fetchQuery` this PR
originally carried. The helper is strictly stronger: it detaches the
in-flight `/companies` request inside the query function, and it
resolves the account identity past the session invalidation immediately
above rather than trusting the session entry still in the cache.
- The observer reads through `useCompanyListQuery`.

The mount-scoped `isFetchedAfterMount` gate is **kept**, not removed.
Its purpose has narrowed — cross-account leakage is now structurally
impossible, so what remains is holding the verdict until this page has a
list rather than acting on a pending one. It is still load-bearing:
disabling it fails two tests here. Removing a defense in the same change
that rebases onto a new foundation is the wrong order; that is a
follow-up once the keying has proven itself.

`Auth.tsx` can safely reset, because it navigates away on success and
`InviteLanding` mounts fresh afterward. Measurements of exactly when
that rewind does and does not bite are in
[#11380](https://github.com/paperclipai/paperclip/pull/11380#issuecomment-5300984911).

## Verification

- `InviteLanding.test.tsx`, `Auth.test.tsx`, `companies-query.test.ts`,
`CompanyContext.test.tsx` together: **51 passed**, run twice.
- `pnpm tsc -b`: clean.
- `InviteLanding.test.tsx` and `Auth.test.tsx` together: 21 passed.

Both failures are pre-existing and unrelated. Each reproduces on a tree
that does not contain this change, in files this change does not touch:

| Failure | Why it fails |
| --- | --- |
| `IssueProperties.test.tsx` | Timezone-dependent: expects `4:08 PM`,
gets `9:08 AM` |
| `StatusCards/format.test.ts` | Time-of-day dependent: "only counts
updates started today" breaks near midnight |

**Not done:** no manual two-account run in a browser. The path needs two
accounts on an `authenticated` instance, which a local dev instance
cannot exercise.

## Risks

Low. The failure direction is a membership verdict withheld for one
extra round trip, which resolves itself; the direction it removes is one
account's membership granted to another, which does not.

It adds one request per invite-page mount, on a query key the app
already uses.

**This does not close the class.** The shared list is still unscoped for
every other consumer. #11380 clears it on sign-out and #11382 handles
the onboarding draft gate; all three are needed, because an account can
change without passing through any one of those paths.

## Model Used

Claude Opus 5 (`claude-opus-5`), through Claude Code. Extended thinking
enabled. Tool use enabled: file read and edit, shell for typecheck and
test runs.

## 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 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

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-08-16 21:55:50 -07:00
DottaandPaperclip 24aa2f516d feat(ui): add animated paperclip page loaders (#10456)
## Thinking Path

> - Paperclip is the open source control plane people use to manage AI
agents for work
> - Operators regularly pass through full-page loading states while
authentication and company context resolve
> - Those states currently render as small bare text, which is easy to
miss and does not reinforce Paperclip's visual identity
> - A shared loading component gives these transitions one accessible,
consistent representation
> - This pull request introduces an animated paperclip loader and uses
it at the existing full-page loading boundaries
> - The benefit is a clearer, calmer loading experience with
reduced-motion and screen-reader support

## Linked Issues or Issue Description

### Subsystem affected

`ui/` — React + Vite board UI

### Problem or motivation

Full-page authentication, access-gate, and company-context waits use
small bare `Loading…` text that is visually weak and inconsistent.

### Proposed solution

Use one large centered paperclip loader at those boundaries, drawing the
SVG with `currentColor` so it follows the active theme.

### Alternatives considered

Keeping text-only states or adding a generic spinner would preserve less
of Paperclip's product identity and would continue duplicating loading
markup.

### Roadmap alignment

This is tightly scoped UI polish and does not duplicate a planned
roadmap capability.

### Additional context

Internal coordination task PAP-15760 requested this focused change.

## What Changed

- Added `AnimatedPaperclipIcon`, a theme-aware SVG whose stroke draws in
a loop.
- Added `PaperclipLoading`, a large full-viewport centered loader with
`role="status"` and an `sr-only` `Loading…` label.
- Added a static fully drawn fallback under `prefers-reduced-motion:
reduce`.
- Replaced bare loading text in `CloudAccessGate`, the Auth session
check, and three company-context redirects.
- Used token-safe Tailwind utilities throughout the component.
- Added focused coverage for the status semantics and the Auth layout
height override.

## Verification

- `pnpm check:token-gates`
- `pnpm -C ui exec tsc -b`
- `pnpm -C ui exec vitest run
src/components/AnimatedPaperclipIcon.test.tsx`
- `pnpm -C ui exec vitest run src/App.test.tsx
src/App.cases-routing.test.tsx` — 8/8 tests passed
- Visually checked light and dark loading states; the screenshot below
shows both themes.

![Large centered paperclip loading state in light and dark
themes](https://pages.paperclip.ing/pap-15760-loading-state/pap15760-loading.png)

## Risks

- Low risk: this changes presentation only at existing loading branches.
- Motion-sensitive users receive a static, fully drawn paperclip through
the reduced-motion media query.
- Screen readers retain a concise loading announcement through the
status role and visually hidden label.

> 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, GPT-5 (exact service build ID and context-window size
are not exposed in this environment), with reasoning, repository
inspection, shell tool use, code execution, and image inspection.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used with all deployment details
available to this environment
- [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 found none
- [x] I have described the issue in-PR following the feature-request
fields
- [x] I included the task-mandated internal parent reference and no
private instance URL
- [x] I preserved the task-mandated existing branch name without
renaming it
- [x] I have run scoped tests locally and they pass
- [x] I added focused component coverage for the new loading state
- [x] No documentation update is required for this presentation-only
change
- [x] I have considered and documented the 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-07-29 17:58:50 -07:00
DottaandPaperclip 6f204605ad fix(ui): show source SHA for unreleased builds (#9508)
## Thinking Path

> - Paperclip is the open source control plane people use to manage
AI-agent companies
> - Operators need to identify the exact build running from the
persistent account menu
> - Formal releases already have a concise public version, but source
builds include a long derived version string
> - The derived version identifies a commit but does not expose the
source branch or a direct path to inspect the code
> - Server Git metadata is auth-sensitive, so the UI must also refresh
it when the current session changes
> - This pull request shows linked branch and commit metadata for source
builds while preserving `v<version>` for formal releases
> - The benefit is faster build diagnosis with correct metadata across
sign-in and sign-out transitions

## Linked Issues or Issue Description

### Pre-submission checklist

- [x] I searched existing open and closed issues and found no duplicate
implementing this exact account-menu behavior.
- [x] The behavior reproduces on `master`.
- [x] The behavior originates in Paperclip's core UI, not an adapter,
provider, or local configuration.

### What happened?

Source builds displayed the full derived version, such as
`2026.626.0+58.git.518fc71ce`, without linking the operator to the
corresponding source branch or commit.

### Expected behavior

Source builds should show the concise branch and short commit SHA with
links to GitHub, while formal releases should continue showing their
public version. Auth transitions should refresh the health metadata that
supplies those Git details.

### Steps to reproduce

1. Run Paperclip from a commit after a release tag.
2. Open the account menu.
3. Inspect the build label beneath the user identity.
4. Sign in or out and reopen the menu.

### Paperclip version or commit

Any source build whose server version uses the
`<version>+<count>.git.<sha>[.dirty]` format.

### Deployment mode

Local dev (`pnpm dev`) or authenticated deployments.

### Installation method

Built from source.

## What Changed

- Detect source-derived version strings and render the source branch
plus seven-character commit SHA in `SidebarAccountMenu`.
- Link source branches and commits to the canonical
`paperclipai/paperclip` GitHub repository.
- Extend server Git metadata with the full SHA and expose it through
health/OpenAPI contracts.
- Refresh auth-sensitive health metadata after sign-in and every
sign-out entry point.
- Preserve the existing `v<version>` label for formal releases and add
focused regression coverage.

## Verification

- `pnpm --filter @paperclipai/ui exec vitest run src/pages/Auth.test.tsx
src/components/SidebarAccountMenu.test.tsx
src/components/SidebarServerInfo.test.tsx`
- `pnpm --filter @paperclipai/server exec vitest run
src/__tests__/health.test.ts src/__tests__/server-info.test.ts`
- `pnpm --filter @paperclipai/ui typecheck`
- `pnpm check:token-gates`
- `git diff --check public/master...HEAD`

## Risks

- Low risk: formal release rendering retains the existing fallback
behavior when the source-version pattern does not match.
- Source links assume the build came from the canonical public
repository; fork-only branches or commits may not resolve there.
- Health metadata is invalidated after auth transitions, adding one
bounded refetch so the displayed Git details match the new session.

> 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 using GPT-5.4 with medium reasoning, repository/tool
access, shell execution, and code editing; context-window size was 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 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-07-13 20:55:54 -05:00
8ddd735a7a feat(ui): theme toggle on unauthenticated auth page (supersedes part of #3732) (#5874)
## Thinking Path

> - Paperclip orchestrates AI agents for zero-human companies
> - Operators sign in via the `/auth` page, which renders before any
session exists
> - The signed-in app has a theme toggle inside `SidebarAccountMenu`,
but the signed-out `/auth` page has none — first-time visitors are stuck
in whichever theme was hardcoded at boot
> - Master's existing toggle was inline inside `SidebarAccountMenu.tsx`
as a `MenuAction` row, not exported as a reusable widget; round-1 of
this PR added a standalone `ThemeToggle` but punted on unifying the two
surfaces
> - This pull request makes `ThemeToggle` the canonical theme widget
(one source of truth for label, icon, and toggle behaviour), used both
as a compact icon button on `/auth` and as a full-width menu row in
`SidebarAccountMenu`
> - The benefit is a working pre-auth theme switch and zero risk of the
two call sites drifting out of sync as the theme model evolves

## Linked Issues or Issue Description

No existing issue covers this directly — problem described in-PR
(feature-gap shape):

- The signed-in app has a theme toggle inside `SidebarAccountMenu`, but
the signed-out `/auth` page has none — first-time visitors are stuck in
whichever theme was hardcoded at boot.
- The existing toggle lived inline in `SidebarAccountMenu.tsx` as a
`MenuAction` row, not exported as a reusable widget, so the two surfaces
could drift apart as the theme model evolves.
- This PR supersedes part of PR #3732 (the auth-page toggle slice); no
standalone issue was filed for it.

Duplicate-PR search: related open theme PRs #2769 and #4666 add in-app
three-state system-theme toggles — different surface from this PR
(unauthenticated auth page); sibling PR in this series: #5873.

## What Changed

- **`ui/src/components/ThemeToggle.tsx`** — accepts `variant: "icon" |
"menu-action"` (default `"icon"`) and an `onAfterToggle` callback. Both
variants share `useTheme` and the same label/icon derivation. The
`menu-action` variant matches the existing `MenuAction` row styling.
- **`ui/src/components/SidebarAccountMenu.tsx`** — drops its inline
`useTheme()` + `MenuAction`-for-the-theme-row in favor of `<ThemeToggle
variant="menu-action" onAfterToggle={() => setOpen(false)} />`. Sun/Moon
icon imports and theme state move with it.
- **`ui/src/pages/Auth.tsx`** — unchanged from round 0; renders
`<ThemeToggle />` at top-right of the `/auth` page (already using the
default `icon` variant).
- **`ui/src/components/ThemeToggle.test.tsx`** (new) — covers both
variants, the `onAfterToggle` callback, and the label/icon flip across
themes.
- **`ui/src/components/SidebarAccountMenu.test.tsx`** — unchanged; its
`ThemeContext` mock still works because `ThemeToggle` uses the same
hook.

## Verification

- `pnpm --filter @paperclipai/ui run typecheck` — clean.
- `npx vitest run src/components/ThemeToggle.test.tsx
src/components/SidebarAccountMenu.test.tsx` — 5 passed (4 new + 1
existing).
- Manual: launched `pnpm dev` for the UI, mocked `/api/auth/get-session`
401, navigated to `/auth` — toggle is visible top-right, click flips
light/dark. Screenshots committed.

## Risks

Low. The change is structural — both call sites render the same widget
that already worked on each surface independently.
`SidebarAccountMenu`'s popover behaviour is preserved via
`onAfterToggle`, and `ThemeContext` is untouched.

## Model Used

Claude Opus 4.7 (1M context), extended thinking mode.

## Checklist

- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] Thinking path traces from project context to this change
- [x] Model used specified
- [x] Checked ROADMAP.md — not in conflict with planned core work
- [x] Tests run locally and pass
- [x] Added tests for the new ThemeToggle component (both variants)
- [x] UI change — before/after screenshots in
`docs/pr-screenshots/pr-5874/`
- [x] No documentation updates required (purely internal refactor + new
component)
- [x] Documented risks above
- [x] Will address all Greptile and reviewer comments before merge

Supersedes part of #3732.

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: Devin Foley <devin@paperclip.ing>
2026-06-12 12:59:14 -07:00
DottaandClaude Opus 4.8 139cdebe51 [codex] Improve login accessibility and password-manager metadata (#7660)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Human operators sign in through the main auth page and through
invite acceptance flows.
> - Those forms need to be understandable to assistive technology and
password managers.
> - The login fields did not consistently expose stable names, ids,
autocomplete hints, required state, and error relationships.
> - That made it easier for password managers such as 1Password to miss
the username/password pairing and harder for screen readers to associate
validation errors.
> - This pull request tightens the auth and invite form metadata while
keeping the visible flow unchanged.
> - The benefit is a smoother login and invite-acceptance experience
without changing server-side auth behavior.

## Linked Issues or Issue Description

No matching GitHub issue found after searching `login accessibility
1Password username password` in `paperclipai/paperclip` issues and PRs.

## What happened?

Login and invite auth fields were missing password-manager and
accessibility metadata, including stable field identifiers, autocomplete
hints, required semantics, and error-region relationships. That made it
easier for password managers such as 1Password to miss the
username/password pairing and harder for screen readers to associate
validation errors.

## Expected behavior

Auth fields should be discoverable as username/password fields,
distinguish sign-in and sign-up password autocomplete behavior, and
expose validation errors through an alert region referenced by invalid
inputs.

## Steps to reproduce

1. Open the main `/auth` form or an invite auth form.
2. Inspect the email, password, and sign-up name field attributes.
3. Trigger a validation/auth error and inspect whether invalid inputs
reference the displayed error text.

## Paperclip version or commit

Prior to this PR on `master`.

## Deployment mode

Board UI, all deployments using these forms.

## What Changed

- Added stable `id`, `name`, `required`, `aria-required`,
`aria-invalid`, `aria-describedby`, and autocomplete metadata to the
main auth form.
- Added invite auth field metadata so invite sign-up uses
`new-password`, invite sign-in uses `current-password`, and the email
field is recognized as `username`.
- Added alert regions for auth and invite auth errors so invalid fields
can reference the displayed error text.
- Added focused Vitest coverage for the main auth form and invite auth
flow metadata/error behavior.

## Verification

- `pnpm exec vitest run ui/src/pages/Auth.test.tsx
ui/src/pages/InviteLanding.test.tsx` passes: 2 files, 16 tests.
- `pnpm build` passes locally.
- PR CI is green on head `3531d1900`, including Build, Canary Dry Run,
Typecheck + Release Registry, e2e, general/serialized test shards,
policy, commitperclip review, security checks, and aggregate `verify`.
- Greptile Review is passing on head `3531d1900`; the P2
alert/live-region review thread was fixed and resolved.
- Rebasing onto `public-gh/master` completed cleanly; current base ref
is `e50666e4c`.
- Confirmed the branch diff does not include `pnpm-lock.yaml`,
`.github/workflows`, migrations, or design/image assets.
- No screenshots attached: this is a form metadata/accessibility change,
and the task specifically asked not to add design screenshots or images
unless they are part of the work.

## Risks

Low risk. The change is limited to UI form attributes and error wiring.
Main risk is password-manager/browser interpretation differences,
covered by asserting the emitted DOM metadata rather than a specific
vendor integration.

> 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, GPT-5-based coding model with repository tool use and
shell execution. Exact hosted model ID/context window are not exposed in
this 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: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-06 11:40:59 -05:00
b9a80dcf22 feat: implement multi-user access and invite flows (#3784)
## Thinking Path

> - Paperclip is the control plane for autonomous AI companies.
> - V1 needs to stay local-first while also supporting shared,
authenticated deployments.
> - Human operators need real identities, company membership, invite
flows, profile surfaces, and company-scoped access controls.
> - Agents and operators also need the existing issue, inbox, workspace,
approval, and plugin flows to keep working under those authenticated
boundaries.
> - This branch accumulated the multi-user implementation, follow-up QA
fixes, workspace/runtime refinements, invite UX improvements,
release-branch conflict resolution, and review hardening.
> - This pull request consolidates that branch onto the current `master`
branch as a single reviewable PR.
> - The benefit is a complete multi-user implementation path with tests
and docs carried forward without dropping existing branch work.

## What Changed

- Added authenticated human-user access surfaces: auth/session routes,
company user directory, profile settings, company access/member
management, join requests, and invite management.
- Added invite creation, invite landing, onboarding, logo/branding,
invite grants, deduped join requests, and authenticated multi-user E2E
coverage.
- Tightened company-scoped and instance-admin authorization across
board, plugin, adapter, access, issue, and workspace routes.
- Added profile-image URL validation hardening, avatar preservation on
name-only profile updates, and join-request uniqueness migration cleanup
for pending human requests.
- Added an atomic member role/status/grants update path so Company
Access saves no longer leave partially updated permissions.
- Improved issue chat, inbox, assignee identity rendering,
sidebar/account/company navigation, workspace routing, and execution
workspace reuse behavior for multi-user operation.
- Added and updated server/UI tests covering auth, invites, membership,
issue workspace inheritance, plugin authz, inbox/chat behavior, and
multi-user flows.
- Merged current `public-gh/master` into this branch, resolved all
conflicts, and verified no `pnpm-lock.yaml` change is included in this
PR diff.

## Verification

- `pnpm exec vitest run server/src/__tests__/issues-service.test.ts
ui/src/components/IssueChatThread.test.tsx ui/src/pages/Inbox.test.tsx`
- `pnpm run preflight:workspace-links && pnpm exec vitest run
server/src/__tests__/plugin-routes-authz.test.ts`
- `pnpm exec vitest run server/src/__tests__/plugin-routes-authz.test.ts
server/src/__tests__/workspace-runtime-service-authz.test.ts
server/src/__tests__/access-validators.test.ts`
- `pnpm exec vitest run
server/src/__tests__/authz-company-access.test.ts
server/src/__tests__/routines-routes.test.ts
server/src/__tests__/sidebar-preferences-routes.test.ts
server/src/__tests__/approval-routes-idempotency.test.ts
server/src/__tests__/openclaw-invite-prompt-route.test.ts
server/src/__tests__/agent-cross-tenant-authz-routes.test.ts
server/src/__tests__/routines-e2e.test.ts`
- `pnpm exec vitest run server/src/__tests__/auth-routes.test.ts
ui/src/pages/CompanyAccess.test.tsx`
- `pnpm --filter @paperclipai/shared typecheck && pnpm --filter
@paperclipai/db typecheck && pnpm --filter @paperclipai/server
typecheck`
- `pnpm --filter @paperclipai/shared typecheck && pnpm --filter
@paperclipai/server typecheck`
- `pnpm --filter @paperclipai/ui typecheck`
- `pnpm db:generate`
- `npx playwright test --config tests/e2e/playwright.config.ts --list`
- Confirmed branch has no uncommitted changes and is `0` commits behind
`public-gh/master` before PR creation.
- Confirmed no `pnpm-lock.yaml` change is staged or present in the PR
diff.

## Risks

- High review surface area: this PR contains the accumulated multi-user
branch plus follow-up fixes, so reviewers should focus especially on
company-boundary enforcement and authenticated-vs-local deployment
behavior.
- UI behavior changed across invites, inbox, issue chat, access
settings, and sidebar navigation; no browser screenshots are included in
this branch-consolidation PR.
- Plugin install, upgrade, and lifecycle/config mutations now require
instance-admin access, which is intentional but may change expectations
for non-admin board users.
- A join-request dedupe migration rejects duplicate pending human
requests before creating unique indexes; deployments with unusual
historical duplicates should review the migration behavior.
- Company member role/status/grant saves now use a new combined
endpoint; older separate endpoints remain for compatibility.
- Full production build was not run locally in this heartbeat; CI should
cover the full matrix.

## Model Used

- OpenAI Codex coding agent, GPT-5-based model, CLI/tool-use
environment. Exact deployed model identifier and context window 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 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] I will address all Greptile and reviewer comments before
requesting merge

Note on screenshots: this is a branch-consolidation PR for an
already-developed multi-user branch, and no browser screenshots were
captured during this heartbeat.

---------

Co-authored-by: dotta <dotta@example.com>
Co-authored-by: Paperclip <noreply@paperclip.ing>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-17 09:44:19 -05:00
Dottaandgreptile-apps[bot] 7576c5ecbc Update ui/src/pages/Auth.tsx
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2026-03-23 19:00:34 -05:00
cd7c6ee751 Fix login form not being detected by 1Password
Add name, id, and htmlFor attributes to form inputs and a method/action
to the form element so password managers can properly identify the login
form fields.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-23 16:57:27 -05:00
Devin FoleyandPaperclip 88df0fecb0 fix: show validation error on incomplete login submit
Address Greptile review feedback:
- Show "Please fill in all required fields." instead of silently
  returning when form is submitted with missing fields
- Remove pointer-events-none so keyboard users can reach the
  button and receive the same validation feedback

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-03-15 19:39:12 -07:00
Devin FoleyandPaperclip 8a201022c0 Fix Enter key not submitting login form
The submit button's `disabled` attribute prevented browsers from firing
implicit form submission (Enter key) per HTML spec. Move the canSubmit
guard into the onSubmit handler and use aria-disabled + visual styles
instead, so Enter works when fields are filled.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-03-14 00:17:39 -07:00
Dotta 8ee063c4e5 feat(ui): reconcile backup UI changes with current routing and interaction features 2026-03-02 16:44:03 -06:00
ForgottenandClaude Opus 4.6 2ec45c49af feat(ui): add auth pages, company rail, inbox redesign, and page improvements
Add Auth sign-in/sign-up page and InviteLanding page for invite acceptance.
Add CloudAccessGate that checks deployment mode and redirects to /auth when
session is required. Add CompanyRail with drag-and-drop company switching.
Add MarkdownBody prose renderer. Redesign Inbox with category filters and
inline join-request approval. Refactor AgentDetail to overview/configure/runs
views with claude-login support. Replace navigate() anti-patterns with <Link>
components in Dashboard and MetricCard. Add live-run indicators in sidebar
agents. Fix LiveUpdatesProvider cache key resolution for issue identifiers.
Add auth, health, and access API clients.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 14:41:21 -06:00