mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-07 06:25:16 +02:00
cd501499a2fa8fd02b64efca3934f0d72a3087bb
1625
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
38752c4e5e |
fix(ui): clear account-scoped query caches on sign-out (#11380)
Sign-out invalidated two query keys - the auth session and health - and left everything else in the cache. `invalidateQueries` also keeps serving the old data while it refetches, so it was insufficient even for the two it did touch. The company list was never touched at all, which is how one account's companies could still be in hand when the next account signed in. The self-hosted path now resets every account-scoped entry. Scoping is an allowlist of instance-scoped roots rather than a list of things to clear, so a query key added later is account-scoped unless someone deliberately says otherwise - forgetting this file fails closed. `health` is the only exemption. It carries deployment mode, bootstrap state and Cloud metadata, nothing account-scoped, and `useCloudInstance` observes it with `enabled: false` and leaves the fetch to CloudAccessGate. Dropping the entry would strand every such observer on `null` until the gate happened to refetch, flipping Cloud instances into their self-hosted rendering mid sign-out. It is refreshed in place instead. `resetQueries` rather than `removeQueries`: removal empties the cache without notifying the observers already subscribed, so a mounted `useQuery` keeps returning its last result until an unrelated re-render rebuilds it. CompanyProvider sits above the router and stays mounted across the whole sign-out and sign-in cycle, so that is the common case here rather than a corner one. Reset notifies them, so the old data leaves the cache and everything reading it. The cloud path is untouched: it is a top-level navigation, and the document reload builds a new QueryClient with nothing left to clear. This is the root cause behind the onboarding draft-ownership gate added in #11382. That gate stays, and its comment now says why: this fix covers the sign-out button, not the question. An account can change without it - a session lapsing server-side, a second account signing in on a warm tab, a caller supplying the company context from somewhere else - so the gate stays independent rather than deferring to this. ui typecheck clean; full ui suite 4014 pass, with only the timezone-dependent IssueProperties failure already present on master. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
b38d6ddb81 |
fix(onboarding): choose the launcher's step from the company's mission too (#11429)
The last of the three ways into onboarding for a company that already exists.
The route resolver and the dashboard both pick the step from whether the
company already has its mission; the "Add Agent" card on `/{prefix}/onboarding`
hardcoded the mission step. So the one entry point whose own copy reads "Add
another agent to X" was the one that stopped to ask X for the mission it
already had.
It now calls `onboardingStepForCompany`, like the other two. `matchedCompany`
moves above the early return because a hook cannot be called after it.
An unsettled or failed lookup still reads as "no mission" and costs the step,
which the customer can answer - the same fail-open rule the other callers
follow, and safe now that confirming the mission updates the company's existing
goal rather than adding a second one.
`OnboardingRoutePage` is exported so this can be driven directly. The
alternative was the whole `<App>` route table, which is a much heavier harness
for a question about one button's argument.
Four cases, and the first fails against the hardcoded step. The button lookup
asserts it matched something before clicking, because a lookup that silently
matches nothing turns the click into a no-op and the test into decoration.
This is the last piece of #11259 that had not landed.
ui typecheck clean; full ui suite 4010 pass. The one failure, in
IssueProperties, is timezone-dependent and reproduces on master.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
6542ad1f4d |
fix(onboarding): carry an existing company's mission into the wizard (#11416)
Ports the substance of #11259, which predated the recent onboarding work and was still open. One of the parts it solves is a regression #11352 introduced. A company that already has its mission opens on the agent step, which is the point of #11352. What that missed is that the mission field is filled only by the step being skipped, and that the field is not decoration: `composeCeoInstructions` seeds the lead agent's instructions from it, and the Review checklist reads it. So every Cloud-seeded company hired its lead agent with no Mission line at all, having been routed there precisely *because* it had a mission. Before #11352 those companies dead-ended on the mission step; a dead end became a quiet data loss, which is worse, because it completes. `selectExistingCompanyMission` reads the company's own goal back into the shape the mission field holds, and the wizard hydrates from it - only when the field is empty, so a customer editing their mission is never overwritten by the stored copy. The marker recording that hydration travels with the field it describes, cleared wherever `companyGoal` is. `isExistingCompanyMissionUnresolved` holds the hire while that read is outstanding, counting an in-flight refetch over cached goals as unresolved. That is the rule #11382 settled on a day earlier for a different consumer - `isFetching`, not `isLoading`, because retained data is not an answer to the question being asked now. #11259 had it first, on 11 August. `canGoBackFromOnboardingStep` and `canJumpToOnboardingStep` bound how far back a run can walk by the step it entered on. The Back button already applied that rule inline; the progress bar applied only the "already completed" half, so a run holding a company could still jump to step 1 - the step whose job is to create one. The entry step is captured once, when the wizard opens, for the same reason the step itself is. `planMissionPersistence` came with them and turned out to be required rather than tidying. Hydration sets `createdCompanyGoalId` from the company's existing goal, and confirming the mission read that id as "already written" and skipped the write, discarding the customer's edit. That skip was safe only while the id could arrive one way - by writing. A goal in hand now means update it. Each piece was checked by removing it and confirming a specific case fails. The hydration case asserts on `saveInstructionsFile`'s content, the actual consumer, rather than on the mission textarea, because the entry path never renders that field and the navigation bound now prevents reaching it. One caveat recorded rather than smoothed over: the reopen case fails only when both marker-clears are removed, since `reset()` also clears the company id and the next introduction routes through `clearCompanyScopedState`. They are kept as one invariant rather than one guard plus a coincidence. ui typecheck clean; full ui suite 4004 pass. Two failures remain, in IssueProperties and StatusCards/format; both are date-dependent, both reproduce on master with these changes stashed, and neither file is touched here. Co-Authored-By: Jannes Stubbemann <stubbi@users.noreply.github.com> Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
484b1f626c |
fix(onboarding): verify draft ownership against a list fetched this session (#11382)
#11370 stopped onboarding restoring a saved draft when the company list had *errored*. It still trusted the list when the list looked healthy — and the wider door was exactly that. `main.tsx` sets `staleTime: 30_000` app-wide and `Auth.tsx` invalidates rather than resets on sign-in, so `invalidateQueries` keeps serving the previous account's companies with `isLoading` false and no error at all. On a self-hosted instance, where sign-out does not reload the page, signing in as a second account in the same warm tab could restore the first account's draft. No request had to fail. The wizard now judges ownership against a list it fetched for the current session: its own `useQuery` on the shared key with `staleTime: 0`, gated so it runs only when a parseable draft exists and adds no request otherwise. Every clause of that gate earns its place, and each was verified by removing it and watching a specific case fail: - `isSuccess` ties the answer to this session. React Query retains the last good `data` when a refetch fails, so after an account switch the retained value is the previous account's list; a failed refetch flips status to error and this rejects it. - The `unauthorized` check catches the opposite error. `companiesListQueryOptions` folds 401 and 403 into `{ companies: [], unauthorized: true }` rather than throwing, so an auth blip arrives as a *successful* empty list and would otherwise read as "this account owns nothing" and delete the draft. - The mount gate keys on `isFetching`, not `isLoading`. `isLoading` is false whenever retained data exists, so a refetch over a warm cache mounted the wizard undecided — and with the wizard open, the persist effect overwrote the customer's own draft with defaults before the answer arrived. It still releases on failure, so the "Get Started" dead end stays fixed. - An unreadable draft is judged, and cleared, before any of the above, and does not enable the query at all. `isFetchedAfterMount` was in an earlier revision and is deliberately not here: it is true after a failed refetch too, so it rejects nothing `isSuccess` has not, and no test could distinguish it. Worth recording how the first defect survived a check. I fault-injected it, saw a test fail, and concluded the guard worked. It was failing for an unrelated reason — the inner wizard mounted during the fetch and locked its state initializers to defaults, so the draft could not appear whatever the gate decided. Fixing the mount gate exposed the real behaviour. An injection is only evidence if the failure it produces is the one being claimed. This narrows onboarding only. The general fault is that a sign-out leaves account-scoped caches in place, and account changes that skip the button — a session lapsing server-side, a second account in a warm tab — reach the same stale list. Tracked separately; this defence should not be removed as redundant when that lands. ui typecheck clean; full ui suite 3963 pass, with only the timezone-dependent IssueProperties failure already present on master. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
fdb9a4880d |
fix(security): route paperclipai CLI guidance through safe npx form (CWE-78) (#11400)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - Paperclip provides CLI commands and guidance for operators and agents > - The `pnpm paperclipai` script can pass argument values through a shell > - Shell re-parsing can execute command substitutions inside quoted values > - This pull request routes guidance through inert-argv `npx paperclipai` commands and adds regression coverage > - The benefit is safer operator guidance across documentation and runtime hints ## Linked Issues or Issue Description This pull request fixes a command-injection-class defect in Paperclip CLI guidance. **What happened?** The `pnpm paperclipai <sub> --flag "$VALUE"` form can re-parse argument values through a shell. A command substitution inside a quoted value can execute on the host. **Expected behavior** Paperclip guidance must pass CLI values as inert argument values. Host-derived values must not appear in copyable commands. **Steps to reproduce** 1. Run a Paperclip guidance command that uses the `pnpm paperclipai` script. 2. Provide a quoted value that contains a command substitution. 3. Observe that the shell can evaluate the substitution before the CLI starts. 4. Compare the result with the `npx paperclipai` form. **Paperclip version or commit** `5670984b75d109950c968542a0111ebb6967f4da` **Deployment mode** All deployment modes that show or use the affected CLI guidance. **Installation method** Built from source and installed CLI guidance. **Agent adapter(s) involved** Not adapter-specific (core bug). **Database mode** Not database-related. **Access context** Both. **Additional context** The earlier merged PR [#11343](https://github.com/paperclipai/paperclip/pull/11343) used the unsafe `pnpm exec paperclipai` form. This fresh PR replaces that guidance with the safe `npx paperclipai` form. ## What Changed - Standardize documentation and runtime hints on `npx paperclipai`. - Remove the broken `pnpm exec paperclipai` guidance. - Use a static `<host>` placeholder in private-hostname guidance. - Add regression tests for unsafe forms, continued lines, static hosts, and offline guidance. ## Verification - `git diff --check origin/master...origin/fix/paperclipai-cli-npx-safe-invocation` passes. - The branch adds `server/src/__tests__/cli-invocation-safety.test.ts` and updates private-hostname tests. - CI must run the new tests, typecheck, lint, and build checks. - Local Vitest execution was not available because this worktree has no installed Vitest binary. ## Risks - The change affects operator and agent documentation text. - The runtime hints now show `<host>` instead of a request-derived host value. - No database schema or migration changes exist. - CI will detect any missed unsafe invocation or type error. ## Model Used OpenAI GPT-5, exact model ID `gpt-5`, with tool use and code-review assistance. The model used repository inspection, Git operations, and PR preparation. ## 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] CI ran the test suites and they pass; local test execution was unavailable in this worktree - [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 addressed all Greptile and reviewer comments before requesting merge --------- Co-authored-by: Paperclip <noreply@paperclip.ing> |
||
|
|
8ee1fb21a6 |
feat(ui): badge the review policy when it constrains approval (#10938)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - Agents move their work into review, and a reviewer must then give a verdict on it > - By default anyone with write access can give that verdict, including the agent that did the work > - The server can constrain that default per issue with a `reviewPolicy` column, but no screen showed the value > - A reviewer could therefore press Approve on a review that the server refuses, and get a 403 > - This pull request shows the policy as a badge on the two surfaces where a person gives a verdict > - It also makes an agent verdict read as a verdict in the activity timeline > - The benefit is that a reviewer sees who can approve before they try ## Linked Issues or Issue Description No public GitHub issue exists for this change. The description below follows `.github/ISSUE_TEMPLATE/enhancement.yml`. **What existing behavior does this improve?** The issue review flow. A reviewer cannot see the approval constraint on an issue before they give a verdict. **Subsystem affected** Web UI (`ui/`), with one supporting change in the server attention service. **Current behavior** The server stores an optional approval constraint for each issue in a `reviewPolicy` column. The column has three meaningful states: the default (`NULL` or `anyone`), `not_creator`, and `human_only`. The server enforces the constraint when it receives a verdict. No screen shows the value. Two problems follow: 1. A reviewer presses Approve on a review that the server refuses. The server answers 403, and the reason is not visible on the card. 2. An agent that accepts or rejects a review renders in the activity timeline as the raw action id, for example "issue thread interaction accepted". A person who reads the timeline cannot tell that a verdict was given. **Proposed behavior** Show the constraint as a read-only badge on the two surfaces where a person gives a verdict. Show no pixels for the default state, because the default is what every issue already does. Make an agent verdict read as a verdict in the timeline. Only agents set the column today, so this change adds no control to set it. **Reason and benefit** A reviewer sees the constraint before they act. This prevents the 403, and it removes the need to explain the 403 afterwards. The timeline also becomes complete, because it now shows agent verdicts and human verdicts in the same way. **Breaking changes** None. The change adds a badge and changes copy. It adds no column, no endpoint, and no request. **Additional context** The server-side column and the verdict enforcement landed earlier in #10931. This pull request is the user interface for that column. The default state stays unchanged on screen, so the badge appears on a small number of issues. ## What Changed - **A read-only "Approvals" row** in the issue Execution properties. The row renders *only* for a constrained policy: "Anyone else" (`not_creator`) or "Human only" (`human_only`). A `NULL` or `anyone` column adds no row, so the panel is untouched on the overwhelming majority of issues. - **The same badge on the stalled-review card** in `/decisions`, above the three review verbs. A reviewer now sees the constraint before they press Approve. The condition is the same, so the default card is unchanged. - **Agent verdicts read as verdicts in the activity timeline.** An agent that accepted or rejected a review request previously rendered the raw action id ("issue thread interaction accepted"). It now reads "approved the request". A stalled-review decision names the verb that the actor chose. - **A cleared policy reports as "anyone", not "none",** in the field-change receipt. The `reviewPolicy` column is nullable by default, so an absent value is a real setting rather than a missing one. - **All copy comes from `ui/src/lib/review-policy.ts`.** Its badge lookup returns `null` for the default. This makes "no pixels for the default" one enforced decision instead of a condition repeated at each call site. It also keeps the badge, the activity line, and the receipt reading alike. - **The server attention service carries the policy** on the review attention subject, so the stalled-review card can read it. ## Verification Automated tests: - `ui/src/lib/review-policy.test.ts` — the default returns no badge, however the column spells it (`null`, `undefined`, `"anyone"`). An unrecognised policy from the wire shows nothing rather than leaking an enum value. - `ui/src/components/AttentionQueueRow.test.tsx` — no badge on the default card, and the verbs still render. Suppression of the badge must not suppress the card. - `ui/src/components/IssueProperties.test.tsx` — no Approvals row on the default. The constrained row contains no `button`, so nothing there can PATCH. - `server/src/__tests__/attention-service.test.ts` — the review attention subject carries the policy, and subjects built from narrower selects do not claim one. Run them with: ```sh pnpm vitest run ui/src/lib/review-policy.test.ts \ ui/src/components/AttentionQueueRow.test.tsx \ ui/src/components/IssueProperties.test.tsx \ server/src/__tests__/attention-service.test.ts ``` Manual steps: 1. Open an issue that has no `reviewPolicy`. Confirm that the Execution properties panel shows no Approvals row. 2. Set the column to `not_creator`. Reload the issue. Confirm that the Approvals row reads "Anyone else", and that the row has no control. 3. Move that issue into review. Open `/decisions`. Confirm that the stalled review card shows the same badge above the review verbs. 4. Let an agent approve the review. Confirm that the activity timeline reads "approved the request" and not "issue thread interaction accepted". Screenshots were captured at 1440x900 and 390x844, in light mode and dark mode, with the three policy states side by side. The leftmost column in each capture is the default. It carries no badge and no extra row. ## Risks Low risk. - The change is additive on screen. Every new surface is behind a constrained policy, so the default path renders exactly as before. - The badge is read-only. It has no control and sends no request, and a test asserts that the row contains no `button`. - An unknown policy value from the wire renders nothing. It does not render the raw enum. - No migration, no schema change, and no endpoint change. ## Model Used Claude Opus 5 (Anthropic), model id `claude-opus-5[1m]`, 1M context window, with extended thinking and tool use enabled. Used through Claude Code for the implementation, the tests, and this description. ## 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 - [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 5 (1M context) <noreply@anthropic.com> |
||
|
|
35a9b98733 |
fix(ui): theme the onboarding wizard decorative panel instead of hardcoding dark (#11379)
The onboarding wizard's decorative right-hand panel, which holds the ASCII paperclip illustration, hardcoded a near-black surface. `ThemeContext` supports light and dark and follows `prefers-color-scheme`, so in light mode a new customer met the product as a pale form beside a solid black rectangle — on the one screen meant to introduce it. The glyphs inside already used `text-muted-foreground`, so the panel was the only part ignoring the theme. It now uses the paired `bg-muted` surface, which is defined in both themes (`oklch(0.97 0 0)` light, `oklch(0.269 0 0)` dark), so the illustration reads as ink on a surface either way and follows any future theme without another fix. The guard for it asserts the complete set of `bg-` classes on the panel is `["bg-muted"]`, anchored to the `<AsciiArtAnimation />` wrapper rather than scanning the file. Forbidding specific spellings is what failed here originally: the first version checked `bg-[#rrggbb]` and silently stopped guarding anything once master migrated the class to `bg-(--hex-1d1d1d)`. Naming what is allowed cannot decay that way, and it catches named colours like `bg-black` that no spelling list covered. Lands the work from #8982 by @stubbi, whose two commits are included unchanged with their authorship. The rebase and the guard are mine. Tested: ui typecheck clean; both theme cases fail against four spellings of the regression — `bg-(--hex-1d1d1d)`, `bg-[#1d1d1d]`, `bg-black`, `bg-zinc-900` — where the original caught only one and my first widening caught two. Full ui suite 3958 pass, with one timezone-dependent IssueProperties failure present on master. All CI gates green; Greptile 5/5. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
8cb0ce0de5 |
fix(ui): restore queued message interrupt action (#11374)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - The task thread lets an operator add guidance while an agent run is active > - A new message can wait behind that active run as a queued message > - The classic task view lets the operator interrupt the target run from that queued message > - The redesigned task view did not expose the same action > - This pull request restores the action and keeps it bound to the exact target run > - The benefit is that operators can apply urgent guidance without switching task views ## Linked Issues or Issue Description **What happened?** The redesigned task view showed `Queued` for a queued operator message, but it did not show the existing interrupt action. **Expected behavior** The queued message must show `Interrupt` next to `Queued`. The action must stop the exact run that the message is waiting behind. **Steps to reproduce** 1. Open a task in the redesigned task view while an agent run is active. 2. Send a new operator message so it enters the queued state. 3. Observe that the queued message has no interrupt action. **Paperclip version or commit** `bc0b5a1642` **Deployment mode** All deployment modes that use the redesigned task view. ## What Changed - Preserve persisted queued state and the target run ID in the redesigned thread model. - Render a token-compliant `Interrupt` action beside the queued state. - Reuse the existing exact-run interrupt callback and show a disabled `Interrupting…` state during the request. - Keep an assigned queue target immutable so an in-flight comment cannot rebind its interrupt action to a replacement run. - Add regression tests for persisted queued messages, replacement-run races, and the in-progress action state. - No documentation update was required because this restores existing behavior. ## Verification - `pnpm exec vitest run ui/src/components/TaskChatThread.test.tsx` - `pnpm exec vitest run ui/src/components/TaskChatThread.test.tsx ui/src/components/task-chat/task-chat-adapter.test.ts ui/src/pages/IssueDetail.test.tsx -t 'queued message actions|queues messages against a queued live run and interrupts that exact run|commentsToTaskChatItems'` - `pnpm exec vitest run ui/src/pages/IssueDetail.test.tsx -t 'queued message|queues messages'` - `pnpm exec vitest run ui/src/pages/IssueDetail.test.tsx` (52 passed) - `pnpm -r typecheck` - `pnpm test:run` (all server and UI groups passed; the CLI group passed after inherited static AWS credential variables were omitted) - `env -u AWS_ACCESS_KEY_ID -u AWS_SECRET_ACCESS_KEY pnpm exec vitest run cli/src/__tests__/secrets.test.ts` - `pnpm build` - `pnpm check:token-gates` ## Risks Low risk. The change only adds an action to queued messages that have a target run and an interrupt callback. Messages without both values keep the current rendering. > 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 with GPT-5. The runtime does not expose a more specific deployment ID or context-window size. The model used reasoning, repository tools, code execution, and test execution. ## 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> |
||
|
|
66515582e4 |
fix(onboarding): do not restore wizard state for a company the user does not own (#11370)
Onboarding persists a draft to `localStorage`, including `createdCompanyId`. That key is scoped per browser origin, not per account, so a browser that has already run onboarding hands the stored company id to the next session on the same origin — whoever is signed in. Every downstream call then targets a company that account may not own: goals, agents and issues are created there, and requests fail with authorization errors. `restoreOnboardingState` now returns a saved draft only when the signed-in account owns the company it names. Otherwise the draft is discarded and the stale blob removed. The wizard splits into a gate and an inner component because the inner one has ~20 `useState(saved?.x ?? default)` initializers, and an initializer runs only on the first render. Mounting before the restored draft is final locks every field to its default with no way back, so the gate waits for the company list while it is loading. Ownership is judged only against a list that actually answered. Any company query error makes it undecidable, whatever the list contains — the companies cache is not account-scoped and survives sign-out, so a failed refetch after an account switch can leave the previous account's companies in hand, and trusting a non-empty list there would hand one account's draft to the next. Nothing is restored and nothing is deleted in that state; the next successful load decides. Judging the draft and mounting the wizard are separate questions. The gate withholds the wizard only while the list is *loading*, never on error: the companies query sets `retry: false`, and with no companies the dashboard offers a "Get Started" button wired to onboarding, so blocking there would make that button do nothing at all. Mounting costs the draft nothing, because the persist effect is itself gated on the wizard being open. All four draft-storage call sites — read, write, cleanup, reset — go through one guarded helper. Storage access throws outright where a browser denies it, and each site sits in a render, an effect or a close handler, so an escaping exception took down something the customer was using. Lands the work from #9900 by @stubbi, whose two commits are included unchanged with their authorship. The rebase, the error-path handling and the storage guards are mine. Follow-up filed separately: sign-out should remove account-scoped cached data rather than invalidating two keys. This change is defensive and protects onboarding only. Tested: ui typecheck clean; 73 pass across the seven onboarding suites; full ui suite 3952 pass, with one timezone-dependent IssueProperties failure present on master in a file this does not touch. All CI gates green; Greptile 5/5. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
bc0b5a1642 |
fix(ui): onboarding wizard keeps an invisible disabled adapter selected (#11371)
The wizard defaults `adapterType` to `claude_local`, and a saved draft can name any adapter. The grid only renders adapters the server has enabled, so on an instance where the held adapter is disabled nothing appears selected while the wizard still holds it — and the first agent is hired on an adapter the deployer turned off, which can never acquire a lease. The selection now snaps to the first enabled, non-coming-soon adapter whenever the held one is not visible, and adapter-specific model defaults follow it. The snap waits for the adapter registry to load. External adapter types are registered into the UI registry only once the adapters query resolves, so before that a saved external adapter is indistinguishable from a disabled one — snapping on that transient list would replace the customer's choice with a built-in and the persist effect would write it down. This gate fails closed, unlike the fail-open gates in onboarding, because the directions of harm are opposite: acting early silently rewrites a saved answer, while waiting merely leaves the selection alone, which is the behaviour that existed before the snap did. The test file is named `OnboardingWizard.adapters.test.tsx` rather than `OnboardingWizard.test.tsx`, which is the name #11370 uses for its restore gate. Both merged cleanly onto master alone but collided with each other on add/add, and nothing in either status showed it. Lands the work from #9900's sibling, #9501, by @stubbi, whose commit is included unchanged with their authorship. The rename and the registry gate are mine. Tested: ui typecheck clean; 51 pass across the adapter, hook, dialog, config-form and wizard-step suites, including the other callers of the adapter hook since that module changed. All CI gates green; Greptile 5/5. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
d2665ff6b4 |
fix(ui): align the mobile task chat composer with the thread (#11296)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work. > - The task thread is where people read work and guide agents. > - The mobile composer should use the same content width as the thread. > - The composer kept the desktop 80% width on mobile, so its edges did not align with the thread. > - Long assignee-aware placeholder text could also clip inside the mobile editor. > - Some extracted style tokens used legacy HSL wrappers around complete semantic colors, which made those declarations invalid. > - This pull request makes the composer full width on mobile, preserves the narrower desktop layout, wraps the placeholder, and repairs the invalid color compositions. > - The benefit is a stable mobile composer that aligns with the task thread and keeps its intended visual styles. ## Linked Issues or Issue Description Related work: Refs #11263. **What happened?** At mobile widths, the task chat composer used the same 80% width as the desktop composer. Its horizontal edges did not align with the full task thread. A long assignee-aware placeholder could clip on one line. The composer's extracted shadow also used a legacy `hsl(var(...))` wrapper around complete semantic color values, so the browser could reject the declaration. **Expected behavior** The composer must match the task thread width on mobile. It must stay narrower on larger screens. Long placeholder text must wrap inside the editor. Semantic color tokens must form valid shadows and gradients. **Steps to reproduce** 1. Open a task with the chat-style thread on a mobile viewport. 2. Compare the composer edges with the task thread edges. 3. Select an assignee whose placeholder text wraps to two lines. 4. Inspect the computed composer shadow and the extracted semantic color styles. **Paperclip version or commit** The change is based on `dc6fcd1ff1` from `master`. **Deployment mode** Local build from source. The behavior also applies to packaged web builds. ## What Changed - Made the task chat composer full width below the medium breakpoint and kept the 80% desktop width. - Matched the composer dock padding to the task thread padding. - Allowed long composer placeholders to wrap and reserved enough mobile editor height for two lines. - Replaced invalid legacy HSL wrappers around full semantic colors in extracted shadows, gradients, and approval styles. - Added a token gate that prevents legacy `hsl(var(--token))` wrappers from returning. - Added focused regression tests for responsive width, padding, placeholder wrapping, mobile height, and semantic shadow validity. ## Verification - `pnpm check:token-gates` — all four gates pass. - `pnpm --filter @paperclipai/ui exec vitest run src/components/TaskChatThread.test.tsx src/components/task-chat/TaskChatComposer.test.tsx src/components/task-chat/TaskChatComposerStyles.test.ts` — 37 tests pass. - `pnpm --filter @paperclipai/ui typecheck` — passes. - `pnpm --filter @paperclipai/ui build` — passes. The build prints existing CSS optimizer and bundle-size warnings. ## Risks - Low risk. The width change is limited to the mobile breakpoint. The desktop 80% layout remains in place. - The semantic token fixes can affect shadows and gradients that were previously invalid. The new gate prevents the invalid wrapper pattern from returning. > 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 with `gpt-5.6-sol`. The context-window size is not exposed in this environment. The model used reasoning, repository tools, code execution, and GitHub tools. ## 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> |
||
|
|
d95340b0b8 |
feat(ui): send a company with no agent into onboarding, at the right step (#11352)
A company with no agent cannot do anything: no runs, no tasks, nothing to show. The dashboard says so in a banner with a link, which asks the customer to notice a problem the product can fix for them. It is worse for a company created by Paperclip Cloud: Cloud creates the company before the tenant boots, so the companyless redirect never runs, and the customer arrives on an empty dashboard straight out of a signup flow that already asked for a mission. The dashboard now opens onboarding when the agent list has loaded and is empty, and onboarding opens on the agent step when the company already has its mission — read from the company-level goal the seed writes, under the query key the launch path already uses, so it shares a cache entry rather than adding a fetch. The step is decided once. `initialStep` is derived from the company list and the goal list, so it changes on any retry, refetch or cache invalidation. An effect that took it as a dependency called `setStep` on every one of those and moved a customer who was already mid-flow. Gating the input only narrowed that window; it could not close it. The step now belongs to the request that opened the wizard: the effect reads it through a ref and is keyed on the wizard opening or the company changing. `createdCompanyIdRef` beside it already used this pattern for the same reason. That exposed a path nothing had ever taken. A company reached the mission step only by creating itself on step 1, so opening an existing company there found code that had never run: `companyName` is only typed on step 1, and both ways forward require it, so the step could not be completed at all; and confirming advanced without writing anything, so the mission the customer typed was discarded. Both fixed, and the write now reconciles against the goal list rather than adding a second company goal, since the mission lookup fails open and can send a company that has one back to that step. Company-scoped state now stays with its company. `clearCompanyScopedState` runs when the route replaces a company and when it withdraws one — the same event, and clearing half of it left a goal id that made the next company skip a mission it had never given. `stillTheSameCompany` guards all five async writes, after the server work rather than before it, so a company switch mid-flight cannot hand the new company the old one's goal, project, issue or agent, and cannot leave a hired agent without its instructions file. The keyboard path honours `loading` like every button already did. `claimOnboardingOffer` makes onboarding an offer that stays declined for the visit. Route ownership is now recorded whenever the route names a company, including one the wizard already holds. This changes a documented rule deliberately: without it a self-created company was never withdrawn, so `/onboarding` would show "create a company" while still holding the previous one and write the customer's new mission into it. Tested at the seam, because every defect on this branch lived between a value and its consumer and the predicate tests passed at every stage. `OnboardingWizard.step.test.tsx` renders the real wizard against the real resolver and the real mission hook across 18 cases, and each was fault-injected against the code it replaces rather than trusted on a green run. That caught a case that passed against the broken code, and a race in one of the guards. ui typecheck clean; full ui suite 3923 pass, with one timezone-dependent IssueProperties failure present on this branch's base in a file this change does not touch. All CI gates green; Greptile 5/5. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
dc6fcd1ff1 |
fix(ui): move agent secret access to searchable secrets tab (#11283)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work. > - The agent configuration UI controls each agent and its allowed secrets. > - The environment variable editor already has a secret selector with search and folder navigation. > - The secret access editor used a basic list and made large secret stores hard to use. > - The secret access controls also occupied the main Configuration tab. > - This pull request reuses the rich selector and moves secret access to a dedicated Secrets tab. > - The benefit is one consistent secret selection workflow with clearer agent configuration navigation. ## Linked Issues or Issue Description **What existing behavior does this improve?** The agent detail configuration view and its secret access editor. **Subsystem affected** `ui/` — React and Vite board UI. **Current behavior** The secret access editor uses a basic select control. It does not provide the search and folder navigation available in the environment variable editor. The editor also appears inside the Configuration tab. **Proposed behavior** The secret access editor uses the shared secret picker. Users can search secrets and browse slash-delimited folders. Agent details provide a dedicated Secrets tab for this editor. **Reason and benefit** Large secret stores are slow to scan in a flat list. Reusing one selector reduces UI differences and makes scoped secret access easier to manage. **Breaking changes** None. The API and saved secret access data do not change. ## What Changed - Reused the environment variable secret picker in the agent secret access editor. - Preserved secret version selection and the create-secret action, including nested-popover focus handling. - Added a route-backed Secrets tab to agent details and removed secret access controls from Configuration. - Guarded unsaved configuration across tab, link, browser-history, and action-triggered navigation. - Rechecked dirty state when navigation-producing agent actions finish, covering edits made while a request is pending. - Added component, page, and Storybook coverage for the workflow. ## Verification - `pnpm --filter @paperclipai/ui exec vitest run src/components/AgentActionButtons.test.tsx src/components/AgentConfigForm.render.test.tsx src/components/AgentSecretAccessEditor.test.tsx src/components/environment-variables-editor/EnvironmentVariablesEditor.test.tsx src/pages/AgentDetail.progress.test.ts` — 82 tests passed. - `pnpm --filter @paperclipai/ui typecheck` - `pnpm check:token-gates` - All GitHub PR checks passed on `5209c5b787`, including build, canary, general and serialized tests, and all three e2e shards. - Greptile completed at 5/5 with zero unresolved review threads. ## Risks - Low risk. The API and persisted binding format are unchanged; this changes agent configuration navigation and secret selection UI. - Dirty-state guards now cover direct navigation, Back/Forward history, and navigation-producing agent actions, including pending-request races. - Tests cover tab separation, secret access updates, search, folder navigation, focus restoration, and navigation rejection. - No documentation change is required because commands, contracts, and setup steps do not change. > 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 with GPT-5. This runtime did not expose a more specific model ID or context window. The model used agentic reasoning, repository tools, and code execution. ## 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> |
||
|
|
aac6ce82e1 |
fix(ui): read the onboarding company prefix from the path, not the route match (#11351)
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - New users meet the product through an onboarding wizard that creates
their company, their first agent, and a starter task
> - The wizard also serves an existing company, at
`/{PREFIX}/onboarding`, to add another agent to it
> - On that route the wizard ignores the company in the URL and opens at
"create a company" instead
> - It reads the prefix with `useParams()`, but it renders beside
`<Routes>` rather than inside it, so there is no route match to read
> - This pull request reads the prefix from the pathname, which is
available without a match
> - The benefit is that the URL a user follows decides what the wizard
asks them
## Linked Issues or Issue Description
No public issue exists for this. The problem follows.
**What happened?**
Open `/{PREFIX}/onboarding` for a company that already exists. The
wizard opens at step 1 and asks the user to create a company. The
company named in the URL is ignored.
**Expected behavior**
The wizard recognises the company in the URL and opens at step 2, so the
user adds an agent to that company instead of creating a second one.
**Steps to reproduce**
1. Create a company, so it has an issue prefix.
2. Go to `/{PREFIX}/onboarding`.
3. Read the first screen. It asks for a company name.
**Paperclip version or commit**
`master` at `5ca7b4c1f`.
**Deployment mode**
Any. This is client-side routing and does not depend on the server.
## What Changed
- `ui/src/lib/onboarding-route.ts` — adds
`companyPrefixFromOnboardingPath()`, which reads the prefix from the
pathname.
- `ui/src/components/OnboardingWizard.tsx` — uses that value when the
route match supplies none. One line, plus the import.
- `ui/src/lib/onboarding-route.test.ts` — six cases for the new
function.
`OnboardingWizard` renders beside `<Routes>` in `App.tsx`, so
`useParams()` returns nothing and `companyPrefix` was always
`undefined`. `resolveRouteOnboardingOptions` then took its no-prefix
branch every time. `useLocation()` needs only the router, not a match,
and the wizard already calls it.
The route match is still read first. If the wizard later moves inside
the route tree, this code does not need to change.
The new parser accepts the same shape as `isOnboardingPath()`: the
prefix is the first of exactly two segments. One test asserts the two
agree, because a disagreement would either open the wizard where no
company resolves, or resolve a company where onboarding is not served.
### Why the change is this small
Three pull requests are open against `OnboardingWizard.tsx` — #9900,
#9501 and #8982. A larger change there would collide with all three.
Almost all of this lands in `onboarding-route.ts`, a small file of pure
functions with existing tests.
## Verification
- `npx tsc --noEmit -p ui/tsconfig.json` — clean.
- `npx vitest run ui/src/lib/onboarding-route.test.ts` — 18 pass.
- `npx vitest run ui/src` — 3883 pass, 445 files.
One test shows the defect and the fix together. With `companyPrefix:
undefined`, which is what the wizard supplied before,
`resolveRouteOnboardingOptions` returns `{ initialStep: 1 }`. With the
parsed prefix it returns `{ initialStep: 2, companyId: "c1" }`.
**Pre-existing failures, unrelated:** `IssueProperties.test.tsx` and
`StatusCards/format.test.ts` fail on clean `origin/master` with these
changes stashed. Both look date-dependent.
**Not done:** no manual browser check. The behaviour is covered by unit
tests at the function boundary, and the wizard's own suite passes.
## Risks
Low. The route match is still preferred, so behaviour changes only where
`useParams()` gave nothing — which today is every render of this
component.
The parser returns a prefix only for a two-segment path ending in
`onboarding`, so no other route can start matching. An unknown prefix
already falls back to step 1 in `resolveRouteOnboardingOptions`, and
that path is unchanged.
To revert, remove the fallback in the wizard. The new function has no
other caller.
## Model Used
Claude Opus 5 (`claude-opus-5`), through Claude Code. Extended thinking
enabled. Tool use enabled: file read and edit, shell command execution
for typecheck and the test runs, and the GitHub CLI.
## 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
- [ ] 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>
Co-authored-by: Paperclip <noreply@paperclip.ing>
|
||
|
|
5ca7b4c1fe |
fix(security): standardize paperclipai CLI guidance on safe npx path (#11343)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work. > - Paperclip provides CLI guidance to agents and operators through documentation and runtime messages. > - Content-bearing `pnpm paperclipai` examples send arguments through a shell. > - Shell evaluation can execute command substitutions in untrusted argument content. > - Runtime hostname guidance can also place request-derived content inside a shell command. > - This pull request uses `npx paperclipai` for content-bearing guidance and uses a static hostname placeholder. > - The benefit is safer copy-paste guidance for agents and operators. ## Linked Issues or Issue Description **Issue type** Incorrect information **Where is the issue?** CLI guidance in `doc/CLI.md`, `skills/paperclip/SKILL.md`, documentation, and runtime-generated hints. **What's wrong?** Content-bearing `pnpm paperclipai` commands can pass argument text through `/bin/sh`. Shell command substitution in an argument can execute before the CLI receives the value. **Suggested fix** Use `npx paperclipai` for content-bearing commands. Use a static `<host>` placeholder when runtime guidance displays the allowed-hostname command. ## What Changed - Replace content-bearing `pnpm paperclipai` examples with `npx paperclipai` across the documentation and agent-facing guidance. - Update runtime-generated CLI hints to use a static `<host>` placeholder. - Add safety notes to `doc/CLI.md` and `skills/paperclip/SKILL.md`. - Add scans and regression tests for unsafe invocation and hostile hostname headers. - Keep fixed lifecycle commands and `pnpm --filter @paperclipai/*` build commands unchanged. ## Verification - Run `tsc --noEmit` for the changed server files. - Run `cli-invocation-safety.test.ts`. - Run `private-hostname-guard.test.ts`. - Confirm that hostile hostname headers do not enter shown shell commands. - Confirm that the three commits contain the required Paperclip co-author trailer. ## Risks - This change updates documentation and diagnostic text across many surfaces. - Fixed lifecycle and setup commands remain unchanged. - The tests fail if content-bearing `pnpm paperclipai` guidance returns. - The change does not alter the CLI argument parser. ## Model Used OpenAI Codex, GPT-5, tool use, code execution, and repository review assistance. ## 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> |
||
|
|
eabecc6f77 |
feat(annotations): include issue document annotations in agent review context (#11332)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - Reviewers annotate plans and issue documents with inline comments, and assigned agents act on that feedback > - The server already builds a bounded review context from open plan annotations and includes it in agent wake payloads > - Non-plan issue documents did not get the same treatment: their open annotation threads never reached the agent, and the properties pane did not surface their annotations > - This pull request extends the review-context path and the properties-pane UI to issue documents, at parity with plans > - The benefit is that agent feedback on any issue document reaches the assigned agent, not only feedback on the plan ## Linked Issues or Issue Description **What existing behavior does this improve?** The review-context pipeline that delivers inline annotation feedback to assigned agents, and the properties pane that surfaces those annotations to reviewers. **Subsystem affected** The server review-context path (`server/src/services/plan-review-context.ts`, wake payload assembly in `server/src/services/heartbeat.ts`, `server/src/routes/issues.ts`), shared wake-payload types (`packages/shared`, `packages/adapter-utils`), and the issue properties pane (`ui/src/components/issue-properties/`). **Current behavior** A reviewer can annotate any issue document, not only the plan. The agent wake payload includes open annotation threads for the plan document only. Feedback left on other issue documents is invisible to the assigned agent. In the properties pane, the Artifacts tab also gives no way to see or open a document's annotations. **Proposed behavior** Add `buildDocumentReviewContext` beside the existing plan builder. It collects open annotation threads for all non-plan issue documents, applies the same thread, comment, and character budgets across documents, and reports truncation. Include the result as a new `documentReviewContext` field in agent wake payloads and in the issue wake-context route. Keep the plan context on its legacy builder and field so plan-only wakes stay byte-for-byte compatible. Render the new context in the adapter wake-payload text, and surface annotation counts and the annotation panel for documents in the properties pane's Plans and Artifacts tabs. **Reason and benefit** The floating annotation popover and persistent highlight UI landed earlier; this change completes the loop so agent feedback on any issue document reaches the assigned agent, not only feedback on the plan. **Breaking changes** None. The wake payload gains a new optional `documentReviewContext` field; the existing plan context field and its legacy builder are unchanged, so plan-only wakes stay byte-for-byte compatible. ## What Changed - Add `buildDocumentReviewContext` in `server/src/services/plan-review-context.ts`: bounded review context (shared thread/comment/character budgets, per-document legacy limits) over all non-plan issue documents - Include `documentReviewContext` in agent wake payloads (`server/src/services/heartbeat.ts`) and in the issue wake-context response (`server/src/routes/issues.ts`) - Add shared `DocumentReviewContext` / `DocumentReviewContextDocument` types in `packages/shared` - Normalize and render the new context in adapter wake-payload text (`packages/adapter-utils/src/server-utils.ts`), with tests - Show a `DocumentAnnotationsCountChip` and the annotation panel for documents in the properties pane Plans and Artifacts tabs, with tests - Extend server document-annotations service tests to cover the new context builder ## Verification - Run `npx vitest run packages/adapter-utils/src/server-utils.test.ts server/src/__tests__/document-annotations-service.test.ts` from the repo root — 104 tests pass - Run `TZ=UTC npx vitest run ui/src/components/issue-properties/IssuePropertiesDocumentAnnotations.test.tsx ui/src/components/IssueProperties.test.tsx ui/src/components/IssueDocumentAnnotations.test.tsx ui/src/components/DocumentAnnotationPopover.test.tsx` from the repo root — 75 tests pass (one pre-existing monitor-row case asserts UTC timestamps, so use `TZ=UTC` locally; CI runs in UTC) - `pnpm run typecheck` in `server/` passes - Manual: annotate a non-plan issue document, then wake the assigned agent with a comment — the wake payload lists the open document annotation threads; the Artifacts tab shows the annotation count chip and opens the panel ## Risks - The wake payload gains a new optional `documentReviewContext` field; consumers that ignore unknown fields are unaffected, and the plan context field is unchanged - The context is new input to agent wakes; shared budgets (same limits as the plan context) bound token cost across all documents - Low UI risk: the properties-pane changes reuse the existing annotation components > 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 - Claude (Anthropic), model ID `claude-fable-5` (Claude Fable 5), with extended thinking and agentic tool use (Claude Code harness) ## 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> |
||
|
|
04432f82c6 |
build(deps): align Lexical packages at 0.49.0 (#10728)
Bumps [@lexical/link](https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link) from 0.46.0 to 0.49.0. <details> <summary>Release notes</summary> <p><em>Sourced from <a href="https://github.com/facebook/lexical/releases">@lexical/link's releases</a>.</em></p> <blockquote> <p>v0.49.0 is a monthly release headlined by the completion of the <code>$config()</code> protocol migration for Lexical's built-in node classes (<a href="https://redirect.github.com/facebook/lexical/pull/8640">#8640</a>), a breaking change that replaces per-node <code>getType()</code>/<code>clone()</code>/<code>importJSON()</code>/<code>importDOM()</code>/<code>transform()</code> boilerplate with runtime-synthesized behavior. It ships with a cluster of follow-up fixes hardening the synthesized <code>getType()</code> and <code>clone()</code> (including a stack overflow in compiled builds), an opt-in sticky horizontal scrollbar for overflowing tables, and a broad batch of fixes across tables, Markdown, HTML, code highlighting, and selection.</p> <h2>Breaking Changes</h2> <ul> <li><a href="https://lexical.dev/docs/api/modules/lexical"><code>lexical</code></a> — Built-in node classes are ported to the <a href="https://lexical.dev/docs/concepts/nodes"><code>$config()</code></a> protocol. The static <code>importJSON()</code>, <code>importDOM()</code>, <code>clone()</code>, and <code>transform()</code> methods are no longer present on ported nodes; use the higher-level equivalents instead (<code>LexicalEditor.parseEditorState</code>/<code>$generateNodesFromDOM</code>, the <code>$cloneWithProperties</code> helper, and extensions or <code>$transform</code> in <code>$config</code>). <code>getType()</code> is unchanged and safe to keep using, <code>__type</code> is now <code>readonly</code>, and node constructors require zero-argument defaults. Custom nodes may keep their static methods, but adopting <code>$config()</code> is recommended (<a href="https://redirect.github.com/facebook/lexical/pull/8640">#8640</a>)</li> <li><a href="https://lexical.dev/docs/api/modules/lexical"><code>lexical</code></a> — <code>LexicalCommand<T></code>'s payload type is now invariant to prevent unsafe structural compatibility between commands. Previously correct code (including explicit generics) needs no changes; only unsafe <code>LexicalCommand<unknown></code> usage must switch to the newly exported <code>AnyLexicalCommand</code> alias. <code>dispatchCommand</code>'s payload argument is now optional when a command's payload type is <code>undefined</code> or <code>void</code>, and redundant explicit type parameters were dropped from the <code>registerCommand</code>/<code>registerNodeTransform</code> call sites (<a href="https://redirect.github.com/facebook/lexical/pull/8877">#8877</a>)</li> </ul> <h2>New APIs & Features</h2> <ul> <li><a href="https://lexical.dev/docs/api/modules/lexical_table"><code>@lexical/table</code></a> — Opt-in sticky horizontal scrollbar for tables wider than their container, so the scrollbar stays anchored at the viewport bottom instead of only being reachable at the end of the table. Enable it with <code>hasStickyScrollbar: true</code> in the table config; style it via the new <code>tableStickyScrollbar</code> theme key (<a href="https://redirect.github.com/facebook/lexical/pull/8790">#8790</a>)</li> <li><a href="https://lexical.dev/docs/api/modules/lexical_table"><code>@lexical/table</code></a> — The <code><colgroup></code> element is now omitted from the table DOM when column widths are undefined, producing cleaner markup and letting the browser auto-size columns; it is created or removed dynamically as widths change (<a href="https://redirect.github.com/facebook/lexical/pull/8850">#8850</a>)</li> <li><a href="https://lexical.dev/docs/api/modules/lexical"><code>lexical</code></a> — Added an editor operation benchmark suite (paragraph split, bold formatting, range deletion, paste, select-all format) measured through full DOM reconciliation, to help contributors track core editing performance (<a href="https://redirect.github.com/facebook/lexical/pull/8856">#8856</a>)</li> </ul> <h2>Deprecations</h2> <ul> <li><a href="https://lexical.dev/docs/api/modules/lexical_table"><code>@lexical/table</code></a> — <code>$createTableSelection</code> is deprecated in favor of <code>$createTableSelectionFrom</code>, which takes the table node plus anchor/focus cells and validates them, instead of returning a blank selection with placeholder <code>'root'</code> keys that must be overwritten (<a href="https://redirect.github.com/facebook/lexical/pull/8855">#8855</a>)</li> <li><a href="https://lexical.dev/docs/api/modules/lexical_table"><code>@lexical/table</code></a> — <code>getShape()</code> is deprecated because it computes incorrect bounds for merged cells; use the now-exported <code>$computeTableCellRectBoundary</code> (with <code>$computeTableMap</code>) instead (<a href="https://redirect.github.com/facebook/lexical/pull/8853">#8853</a>)</li> </ul> <h2>Notable Fixes</h2> <p><strong><code>$config()</code> follow-ups (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8640">#8640</a>)</strong></p> <ul> <li>The synthesized <code>clone()</code> now applies <code>afterCloneFrom()</code> when called directly (e.g. <code>NodeClass.clone(node)</code>), fixing silent property loss across 25+ core nodes (<a href="https://redirect.github.com/facebook/lexical/pull/8864">#8864</a>)</li> <li>The synthesized <code>getType()</code> no longer returns the superclass type when inherited, fixing node-type collisions during editor registration (<a href="https://redirect.github.com/facebook/lexical/pull/8867">#8867</a>)</li> <li>The synthesized <code>getType()</code> no longer recurses infinitely when a bundler copies it onto a subclass as an own static, fixing stack overflows in compiled/minified builds (<a href="https://redirect.github.com/facebook/lexical/pull/8869">#8869</a>)</li> </ul> <p><strong>Tables</strong></p> <ul> <li><code>DELETE_LINE_COMMAND</code> (Cmd/Ctrl-based delete-line shortcuts) now works inside table cells instead of being silently swallowed (<a href="https://redirect.github.com/facebook/lexical/pull/8851">#8851</a>)</li> <li>Table alignment now works when cells are selected in any direction, not just top-left→bottom-right (<a href="https://redirect.github.com/facebook/lexical/pull/8883">#8883</a>)</li> </ul> <p><strong>Selection & editing</strong></p> <ul> <li>Editor updates dispatched from a read-only context (e.g. inside <code>editor.read()</code>) now run in a fresh writable update instead of being silently dropped, with a dev warning (<a href="https://redirect.github.com/facebook/lexical/pull/8863">#8863</a>)</li> <li>Firefox now creates a selection and shows the block cursor when clicking in the gap between block decorators (<a href="https://redirect.github.com/facebook/lexical/pull/8862">#8862</a>)</li> <li>Pressing ArrowUp before a leading non-inline decorator no longer moves the selection out of the editor (<a href="https://redirect.github.com/facebook/lexical/pull/8887">#8887</a>)</li> </ul> <p><strong>Markdown, HTML & code</strong></p> <ul> <li>Typing a list marker at the start of a heading no longer converts the heading into a list (<a href="https://redirect.github.com/facebook/lexical/pull/8879">#8879</a>)</li> <li>HTML import now evaluates unrestricted CSS selector groups (e.g. <code>p, .foo</code>) against all elements instead of only tag-matched ones (<a href="https://redirect.github.com/facebook/lexical/pull/8873">#8873</a>)</li> <li>Concurrent async language/theme loads in the Shiki highlighter are deduplicated and merged into a single history entry, avoiding spurious undo states (<a href="https://redirect.github.com/facebook/lexical/pull/8854">#8854</a>)</li> <li><code>QuoteNode.updateDOM</code> now accepts the <code>EditorConfig</code> argument the reconciler passes to every other node, for API consistency (<a href="https://redirect.github.com/facebook/lexical/pull/8882">#8882</a>)</li> </ul> <p><strong>Playground & website</strong></p> <ul> <li>Playground scroll padding now accounts for the sticky toolbar so selections scrolled into view from above aren't hidden behind it (<a href="https://redirect.github.com/facebook/lexical/pull/8849">#8849</a>)</li> <li>Fixed a homepage crash caused by a minified dev build, and isolated each embedded example in its own error boundary so one failure no longer takes down the page (<a href="https://redirect.github.com/facebook/lexical/pull/8861">#8861</a>)</li> </ul> <h2>What's Changed</h2> <!-- raw HTML omitted --> </blockquote> <p>... (truncated)</p> </details> <details> <summary>Changelog</summary> <p><em>Sourced from <a href="https://github.com/facebook/lexical/blob/main/CHANGELOG.md">@lexical/link's changelog</a>.</em></p> <blockquote> <h2>v0.49.0 (2026-07-29)</h2> <ul> <li>lexical-markdown Bug Fix Preserve headings when typing list shortcuts (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8879">#8879</a>) Steven Dang</li> <li>lexical-rich-text Bug Fix Forward editorConfig to QuoteNode.updateDOM (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8882">#8882</a>) Sha Halimi</li> <li>lexical-table Bug Fix Table alignment fails when selecting in non-TL-BR direction (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8883">#8883</a>) sahir</li> <li>Breaking Changelexical Chore Remove redundant registerCommandregisterNodeTransform generics (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8877">#8877</a>) mayrang</li> <li>lexical-html Bug Fix Dispatch unrestricted CSS selector groups (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8873">#8873</a>) Madan kumar</li> <li>lexical-playground Bug Fix Deterministic history coalescing for flaky webkit undo tests (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8874">#8874</a>) Bob Ippolito</li> <li>lexical Performance Skip redundant selection restoration in removeTextFromCaretRange (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8872">#8872</a>) mayrang</li> <li>lexical Refactor Simplify RangeSelection.insertText via removeText decomposition (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8870">#8870</a>) mayrang</li> <li>lexical-list Chore Remove redundant decorator-adjacent backspace handler (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8871">#8871</a>) mayrang</li> <li>lexical-table Feature Skip colgroup element in table DOM with undefined col widths (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8850">#8850</a>) Zachary Gallafent</li> <li>lexical Bug Fix Create selection when clicking between block decorators on Firefox (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8862">#8862</a>) mayrang</li> <li>fix config() synthesized getType() recurses infinitely when inherited as own static (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8867">#8867</a> follow-up) (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8869">#8869</a>) Sherry</li> <li>lexicallexical-website Bug Fix Homepage crash from optimized dev build (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8861">#8861</a>) Bob Ippolito</li> <li>lexical Bug Fix run editor updates dispatched from a read-only context in a fresh writable update (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8863">#8863</a>) Sherry</li> <li>lexical Feature Editor operation benchmarks (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8856">#8856</a>) mayrang</li> <li>lexical Chore Migrate LexicalSelection tests to buildEditorFromExtensions (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8865">#8865</a>) mayrang</li> <li>fix config() synthesized getType() inherited by subclasses causes node-type collision (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8640">#8640</a>) (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8867">#8867</a>) Sherry</li> <li>fix config() auto-synthesized clone() loses properties when called directly (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8640">#8640</a>) (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8864">#8864</a>) Sherry</li> <li>lexical-tablelexical Feature Sticky scrollbar for overflowing tables (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8790">#8790</a>) mayrang</li> <li>lexical-code-shiki Bug Fix Deduplicate async loads and merge into history (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8854">#8854</a>) Alexis</li> <li>lexical-table Chore Remove dead code in table command handlers (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8857">#8857</a>) mayrang</li> <li>lexical-tablelexical-playground Chore Deprecate getShape() and migrate playground (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8853">#8853</a>) mayrang</li> <li>lexical-list Chore Rename isNestedListNode to isNestedListNode (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8843">#8843</a>) Bob Ippolito</li> <li>lexical-playground Bug Fix account for sticky toolbar in scroll padding (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8849">#8849</a>) Bob Ippolito</li> <li>Breaking Changeslexical Refactor Port node classes to the config() protocol (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8640">#8640</a>) Bob Ippolito</li> <li>lexical-table Chore Deprecate createTableSelection in favor of createTableSelectionFrom (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8855">#8855</a>) mayrang</li> <li>lexical-table Bug Fix Enable DELETELINECOMMAND in table cells (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8851">#8851</a>) mayrang</li> <li>lexical Bug Fix Scope bench vitest projects to exclude regular test files (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8852">#8852</a>) mayrang</li> <li>v0.48.0 (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8847">#8847</a>) Bob Ippolito</li> <li>v0.48.0 Lexical GitHub Actions Bot</li> </ul> <h2>v0.48.0 (2026-07-16)</h2> <ul> <li>lexical-reactlexical-table Bug Fix Enable table copy in read-only mode (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8845">#8845</a>) mayrang</li> <li>lexical-extensionlexical-mdastdev-mdast-editor-example Feature Add MdastHtmlExtension and Markdown custom-construct examples (collapsible, kbd, alerts, footnotes) (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8826">#8826</a>) Bob Ippolito</li> <li>Fix fail closed in LinkNode.sanitizeUrl() on unparseable URLs (XSS) (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8846">#8846</a>) xiezhenjia-meta</li> <li>lexical Chore Fix serialize-javascript package dependency vulnerability (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8803">#8803</a>) vijay ojha</li> <li>lexical-react Bug Fix Count block separators in character limit overflow wrapping (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8840">#8840</a>) mayrang</li> <li>lexical-yjslexical-react Feature Customizable Yjs shared-type root name (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8841">#8841</a>) mayrang</li> <li>lexical-list Bug Fix Backspace at start of list item outdents or converts to paragraph (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8829">#8829</a>) mayrang</li> <li>lexical-table Feature Add moveTableRow function Add missing export for unmergeCellNode (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8833">#8833</a>)</li> <li>lexical-link Bug Fix disable link opening for disabled autolink in (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8839">#8839</a>) Olivier Chevallier</li> <li>lexical-rich-textlexical-plain-text Bug Fix dont cancel dragover for text drags so native drops work again (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8842">#8842</a>) Bob Ippolito</li> <li>Open playground links in a new tab (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8837">#8837</a>) Sherry</li> <li>lexical-react Bug Fix Merge adjacent OverflowNodes in useCharacterLimit (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8831">#8831</a>) mayrang</li> <li>lexical-code-shiki Bug Fix force re-tokenize after async language load (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8830">#8830</a>) Olivier Chevallier</li> <li>lexical-tablelexical-playground Bug Fix Auto-scroll while drag-selecting cells past the visible edge (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8822">#8822</a>) Oleksandr Trukhnii</li> <li>lexical-mdastlexical-markdown Bug Fix Roundtrip overlapping inline formats (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8825">#8825</a>) Bob Ippolito</li> </ul> <!-- raw HTML omitted --> </blockquote> <p>... (truncated)</p> </details> <details> <summary>Commits</summary> <ul> <li><a href="https://github.com/facebook/lexical/commit/ffe90924bd55b5d450c88de0f9f1c8b228c4a221"><code>ffe9092</code></a> v0.49.0</li> <li><a href="https://github.com/facebook/lexical/commit/5fc8eae3cdbd934ec3d0422ecaef7037771e38c2"><code>5fc8eae</code></a> [Breaking Changes][lexical] Refactor: Port node classes to the $config() prot...</li> <li><a href="https://github.com/facebook/lexical/commit/ba4d6d0f9b99268c58df853542e309bf4f2021ac"><code>ba4d6d0</code></a> v0.48.0 (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8847">#8847</a>)</li> <li><a href="https://github.com/facebook/lexical/commit/365516c5fcdcc141561dcbb2eb43b707b48dd5b8"><code>365516c</code></a> Fix: fail closed in LinkNode.sanitizeUrl() on unparseable URLs (XSS) (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8846">#8846</a>)</li> <li><a href="https://github.com/facebook/lexical/commit/71562324c7d2154f64d79f6d20803f67b9bd9c11"><code>7156232</code></a> [lexical-link] Bug Fix: disable link opening for disabled autolink in… (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8839">#8839</a>)</li> <li><a href="https://github.com/facebook/lexical/commit/e4b7cc3f420226059c8aa30df6e89bd5fadbea90"><code>e4b7cc3</code></a> v0.47.0 (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8821">#8821</a>)</li> <li><a href="https://github.com/facebook/lexical/commit/a7666ab11f5e8c674a3f5ca8a83d2e92f1b171d0"><code>a7666ab</code></a> [*][lexical-devtools][lexical-playground] Chore: Update flow, hermes, and bab...</li> <li><a href="https://github.com/facebook/lexical/commit/e649ab28b7e2dd58c1b4798c446e611f54356518"><code>e649ab2</code></a> [lexical][lexical-eslint-plugin] Feature: Add $getDocument() API and Shadow D...</li> <li><a href="https://github.com/facebook/lexical/commit/7b76175cc96d99489c2f3c792db193cf2d9bc127"><code>7b76175</code></a> [lexical-playground] Bug Fix: Support Unicode URLs in autolink matcher (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical-link/issues/8787">#8787</a>)</li> <li><a href="https://github.com/facebook/lexical/commit/62a4b30f382b4dc60cacd1a9d753a2d1f44d9f5e"><code>62a4b30</code></a> [lexical][*] Feature: registerEventListener / registerEventListeners DOM help...</li> <li>Additional commits viewable in <a href="https://github.com/facebook/lexical/commits/v0.49.0/packages/lexical-link">compare view</a></li> </ul> </details> <br /> --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Cody <noreply@paperclip.ing> |
||
|
|
031003c5e1 |
fix(ui): search parent-issue picker on the server (#11334)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - The board UI lets an operator choose a parent issue for an issue > - The parent picker loads a priority-first page and filters that page in the browser > - A medium-priority or low-priority issue past the page limit never enters the picker > - This pull request sends typed parent-picker text to the server and keeps the picker exclusions > - The benefit is that the operator can select valid parent issues beyond the default page ## Linked Issues or Issue Description This pull request supersedes [#6193](https://github.com/paperclipai/paperclip/pull/6193), whose old file path no longer matches the current component tree. **What happened?** The parent picker fetched one default issue page and filtered it in the browser. The default page sorts by priority and caps the result at 500 issues. Valid medium-priority and low-priority parent issues beyond that page stayed hidden. **Expected behavior** The parent picker must search the server when the operator types text. It must show matching issues beyond the default page while it keeps the current issue and descendant exclusions. **Steps to reproduce** 1. Open an issue in a company with more than 500 issues. 2. Open the parent picker and type the name of a medium-priority or low-priority issue beyond the default page. 3. Observe that the picker does not show the matching issue. **Paperclip version or commit** Commit `c6965bd0237fd9536b41f1495e2a4bb252afcde7`. **Deployment mode** Local dev (`pnpm dev`). ## What Changed - Send parent-picker searches to the issue list endpoint with `q` and a bounded `limit` of 50. - Keep the empty-search list, cycle exclusions, and current sort behavior. - Add a component test for a low-priority match hidden by the default page. ## Verification - Run `pnpm vitest run ui/src/components/IssueProperties.test.tsx`. - Confirm that all 53 tests pass. - Confirm that the new test checks `{ q, limit: 50 }` and the matching issue. ## Risks - Low risk. The change affects only parent-picker search requests. - The server search uses the existing issue list query and does not change stored data. ## Model Used Codex, GPT-5, with tool use and code execution. The model assisted with the change and test. ## 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> |
||
|
|
403fcefb97 |
build(deps-dev): bump vite from 6.4.1 to 6.4.3 (#11317)
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 6.4.1 to 6.4.3. <details> <summary>Release notes</summary> <p><em>Sourced from <a href="https://github.com/vitejs/vite/releases">vite's releases</a>.</em></p> <blockquote> <h2>v6.4.3</h2> <p>Please refer to <a href="https://github.com/vitejs/vite/blob/v6.4.3/packages/vite/CHANGELOG.md">CHANGELOG.md</a> for details.</p> <h2>v6.4.2</h2> <p>Please refer to <a href="https://github.com/vitejs/vite/blob/v6.4.2/packages/vite/CHANGELOG.md">CHANGELOG.md</a> for details.</p> </blockquote> </details> <details> <summary>Changelog</summary> <p><em>Sourced from <a href="https://github.com/vitejs/vite/blob/v6.4.3/packages/vite/CHANGELOG.md">vite's changelog</a>.</em></p> <blockquote> <h2><!-- raw HTML omitted -->6.4.3 (2026-06-01)<!-- raw HTML omitted --></h2> <ul> <li>fix: backport <a href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/22572">#22572</a>, reject windows alternate paths (<a href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/22576">#22576</a>) (<a href="https://github.com/vitejs/vite/commit/96b0c10162e9c55485d922db2cfc6b8227cbc176">96b0c10</a>), closes <a href="https://redirect.github.com/vitejs/vite/issues/22572">#22572</a> <a href="https://redirect.github.com/vitejs/vite/issues/22576">#22576</a></li> <li>fix(deps): backport <a href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/22571">#22571</a>, reject UNC paths for launch-editor-middleware (<a href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/22575">#22575</a>) (<a href="https://github.com/vitejs/vite/commit/8fed5cf540c0d475266787f52072f258478cd42f">8fed5cf</a>), closes <a href="https://redirect.github.com/vitejs/vite/issues/22571">#22571</a> <a href="https://redirect.github.com/vitejs/vite/issues/22575">#22575</a></li> </ul> <h2><!-- raw HTML omitted -->6.4.2 (2026-04-06)<!-- raw HTML omitted --></h2> <ul> <li>fix: apply server.fs check to env transport (<a href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/22159">#22159</a>) (<a href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/22163">#22163</a>) (<a href="https://github.com/vitejs/vite/commit/fe28e47e9463e4c9619f94bfa06d2f8f1411b44b">fe28e47</a>), closes <a href="https://redirect.github.com/vitejs/vite/issues/22159">#22159</a> <a href="https://redirect.github.com/vitejs/vite/issues/22163">#22163</a></li> <li>fix: avoid path traversal with optimize deps sourcemap handler (<a href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/22161">#22161</a>) (<a href="https://github.com/vitejs/vite/commit/ca4da5d1fb45c9cfdce606aa30825095791b164b">ca4da5d</a>), closes <a href="https://redirect.github.com/vitejs/vite/issues/22161">#22161</a></li> </ul> </blockquote> </details> <details> <summary>Commits</summary> <ul> <li><a href="https://github.com/vitejs/vite/commit/6c2c881f15495738ff03bc1d67cc052c07e0cac4"><code>6c2c881</code></a> release: v6.4.3</li> <li><a href="https://github.com/vitejs/vite/commit/96b0c10162e9c55485d922db2cfc6b8227cbc176"><code>96b0c10</code></a> fix: backport <a href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/22572">#22572</a>, reject windows alternate paths (<a href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/22576">#22576</a>)</li> <li><a href="https://github.com/vitejs/vite/commit/8fed5cf540c0d475266787f52072f258478cd42f"><code>8fed5cf</code></a> fix(deps): backport <a href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/22571">#22571</a>, reject UNC paths for launch-editor-middleware (<a href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/2">#2</a>...</li> <li><a href="https://github.com/vitejs/vite/commit/6b3fad02abd550bd7b79934ff92c58dbd7f33045"><code>6b3fad0</code></a> release: v6.4.2</li> <li><a href="https://github.com/vitejs/vite/commit/ca4da5d1fb45c9cfdce606aa30825095791b164b"><code>ca4da5d</code></a> fix: avoid path traversal with optimize deps sourcemap handler (<a href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/22161">#22161</a>)</li> <li><a href="https://github.com/vitejs/vite/commit/fe28e47e9463e4c9619f94bfa06d2f8f1411b44b"><code>fe28e47</code></a> fix: apply server.fs check to env transport (<a href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/22159">#22159</a>) (<a href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/22163">#22163</a>)</li> <li><a href="https://github.com/vitejs/vite/commit/5487f4f641f70c47ea05fd101a4319897df048b3"><code>5487f4f</code></a> release: v6.4.1</li> <li><a href="https://github.com/vitejs/vite/commit/1114b5d7ea03e26572708715343bec69db4536e8"><code>1114b5d</code></a> fix(dev): trim trailing slash before <code>server.fs.deny</code> check (<a href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/20968">#20968</a>) (<a href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/20969">#20969</a>)</li> <li><a href="https://github.com/vitejs/vite/commit/f12697c0f64b9a37196b9ab218a0911829d5b103"><code>f12697c</code></a> release: v6.4.0</li> <li><a href="https://github.com/vitejs/vite/commit/ca6455ee9eb6111a9caa9810506a1b9ac96a520a"><code>ca6455e</code></a> feat: allow passing down resolved config to vite's createServer (<a href="https://github.com/vitejs/vite/tree/HEAD/packages/vite/issues/20932">#20932</a>)</li> <li>Additional commits viewable in <a href="https://github.com/vitejs/vite/commits/v6.4.3/packages/vite">compare view</a></li> </ul> </details> <br /> Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> |
||
|
|
062271e934 |
build(deps): bump mermaid from 11.16.0 to 11.16.1 (#11306)
Bumps [mermaid](https://github.com/mermaid-js/mermaid) from 11.16.0 to 11.16.1. <details> <summary>Release notes</summary> <p><em>Sourced from <a href="https://github.com/mermaid-js/mermaid/releases">mermaid's releases</a>.</em></p> <blockquote> <h2>mermaid@11.16.1</h2> <h3>Patch Changes</h3> <ul> <li> <p><a href="https://redirect.github.com/mermaid-js/mermaid/pull/8022">#8022</a> <a href="https://github.com/mermaid-js/mermaid/commit/12d472c9ed43f94814b110da8d7a9ae6dd5266ed"><code>12d472c</code></a> Thanks <a href="https://github.com/aloisklink"><code>@aloisklink</code></a>! - fix: handle CSS sibling combinators in compileCSS</p> </li> <li> <p><a href="https://redirect.github.com/mermaid-js/mermaid/pull/8022">#8022</a> <a href="https://github.com/mermaid-js/mermaid/commit/2cd6dcf735533b323507e3e889ffdea870540b43"><code>2cd6dcf</code></a> Thanks <a href="https://github.com/aloisklink"><code>@aloisklink</code></a>! - fix: increase protections against prototype pollution</p> <p>User-controlled input already has protections against prototype pollution.</p> <p>Fixes: GHSA-c4c3-pg64-4m4v</p> </li> <li> <p><a href="https://redirect.github.com/mermaid-js/mermaid/pull/8022">#8022</a> <a href="https://github.com/mermaid-js/mermaid/commit/99af3fc35ef0a9a9c8c6314521344d67523ddccf"><code>99af3fc</code></a> Thanks <a href="https://github.com/aloisklink"><code>@aloisklink</code></a>! - fix(architecture): use <code>Map</code>s and <code>Set</code>s to store groups/services</p> <p>Services are now rendered in the order they are defined and more service IDs are now supported.</p> </li> <li> <p><a href="https://redirect.github.com/mermaid-js/mermaid/pull/8022">#8022</a> <a href="https://github.com/mermaid-js/mermaid/commit/2cd6dcf735533b323507e3e889ffdea870540b43"><code>2cd6dcf</code></a> Thanks <a href="https://github.com/aloisklink"><code>@aloisklink</code></a>! - deprecate: Deprecate the <code>mermaidAPI.setConfig()</code> function</p> <p>Calling this function has no observable effect, as the next time a <code>render()</code> or <code>parse()</code> is called, the <code>currentConfig</code> is cleared.</p> </li> <li> <p><a href="https://redirect.github.com/mermaid-js/mermaid/pull/8022">#8022</a> <a href="https://github.com/mermaid-js/mermaid/commit/630aa7e5dd417e1f56bff2a1ce8df2c5ad08d289"><code>630aa7e</code></a> Thanks <a href="https://github.com/aloisklink"><code>@aloisklink</code></a>! - fix(xychart): support zero-width x-axis ranges</p> </li> <li> <p><a href="https://redirect.github.com/mermaid-js/mermaid/pull/8022">#8022</a> <a href="https://github.com/mermaid-js/mermaid/commit/59b22fad2b3bb04f87a476c84a8a2b24679e607e"><code>59b22fa</code></a> Thanks <a href="https://github.com/aloisklink"><code>@aloisklink</code></a>! - fix(radar): limit number of ticks to 32</p> <p>Setting a ticks value higher than this would only show 32 ticks.</p> </li> </ul> </blockquote> </details> <details> <summary>Commits</summary> <ul> <li><a href="https://github.com/mermaid-js/mermaid/commit/7ecca0cd7f1658ef74f4e7e91f925724ef403bbf"><code>7ecca0c</code></a> Version Packages (<a href="https://redirect.github.com/mermaid-js/mermaid/issues/8023">#8023</a>)</li> <li><a href="https://github.com/mermaid-js/mermaid/commit/95b1b9cdba40e7954aee7d8a265577fd3839710c"><code>95b1b9c</code></a> docs: change <code>mermaidAPI.setConfig()</code> changeset (<a href="https://redirect.github.com/mermaid-js/mermaid/issues/8024">#8024</a>)</li> <li><a href="https://github.com/mermaid-js/mermaid/commit/acc69f172b5ad5405ca8de8f4f79a80505092b3f"><code>acc69f1</code></a> Merge pull request <a href="https://redirect.github.com/mermaid-js/mermaid/issues/8022">#8022</a> from mermaid-js/release/11.16.1</li> <li><a href="https://github.com/mermaid-js/mermaid/commit/eba72879e94755433403e571d724ca7a448800af"><code>eba7287</code></a> docs: point changesets to correct commit hashes</li> <li><a href="https://github.com/mermaid-js/mermaid/commit/12d472c9ed43f94814b110da8d7a9ae6dd5266ed"><code>12d472c</code></a> Merge commit from fork</li> <li><a href="https://github.com/mermaid-js/mermaid/commit/2cd6dcf735533b323507e3e889ffdea870540b43"><code>2cd6dcf</code></a> Merge commit from fork</li> <li><a href="https://github.com/mermaid-js/mermaid/commit/630aa7e5dd417e1f56bff2a1ce8df2c5ad08d289"><code>630aa7e</code></a> Merge commit from fork</li> <li><a href="https://github.com/mermaid-js/mermaid/commit/59b22fad2b3bb04f87a476c84a8a2b24679e607e"><code>59b22fa</code></a> Merge commit from fork</li> <li><a href="https://github.com/mermaid-js/mermaid/commit/99af3fc35ef0a9a9c8c6314521344d67523ddccf"><code>99af3fc</code></a> Merge commit from fork</li> <li><a href="https://github.com/mermaid-js/mermaid/commit/2337f7ecd629c8e777684a2edff341976230ad67"><code>2337f7e</code></a> Merge branch 'test/improve-example.html' into release/11.16.1</li> <li>Additional commits viewable in <a href="https://github.com/mermaid-js/mermaid/compare/mermaid@11.16.0...mermaid@11.16.1">compare view</a></li> </ul> </details> <br /> Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> |
||
|
|
68ea3fcf53 |
test(ui): stabilize annotation popover submit-shortcut test (#11330)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - The board UI lets users add comments to document annotations > - The annotation popover test submits a comment with a keyboard shortcut > - React can delay the controlled textarea update under load > - The test can then send the shortcut before the handler sees the typed value > - This pull request waits for the value update before it sends the shortcut > - The benefit is a stable test that checks the real submit path ## Linked Issues or Issue Description **What happened?** The annotation popover test typed a comment and sent the submit shortcut in one synchronous step. Under load, React sometimes had not committed the typed value when the handler ran. The mutation then ran zero times. **Expected behavior** The test should wait for the controlled textarea value before it sends the submit shortcut. The handler should read the comment and call the create mutation. **Steps to reproduce** 1. Run `npx vitest run src/components/DocumentAnnotationPopover.test.tsx` from `ui/`. 2. Repeat the test under system load. 3. Observe intermittent failures where the create mutation runs zero times. **Paperclip version or commit** The test runs against commit `9a08def5752bb13e4cbcb304c6295e175c92db3c`. **Deployment mode** This change affects the UI test suite only. It does not depend on a deployment mode. ## What Changed - Wait for the Comment button to enable after the controlled value updates. - Send the submit shortcut after React commits the typed value. - Keep the test focused on the compose-mode submit path. ## Verification - The author ran `npx vitest run src/components/DocumentAnnotationPopover.test.tsx` from `ui/` with 3 tests passing. - The current handoff worktree could not repeat the test because its installed dependencies lack `react/jsx-runtime`. - GitHub Actions will run the required project checks. ## Risks Low risk. The change updates one UI test file and does not change product code. ## Model Used OpenAI Codex, GPT-5. The model used tool calls and code execution. The context window size was not provided. ## 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> |
||
|
|
eb6c6a8e9b |
build(deps): bump lexical from 0.46.0 to 0.49.0 (#10724)
Bumps [lexical](https://github.com/facebook/lexical/tree/HEAD/packages/lexical) from 0.46.0 to 0.49.0. <details> <summary>Release notes</summary> <p><em>Sourced from <a href="https://github.com/facebook/lexical/releases">lexical's releases</a>.</em></p> <blockquote> <p>v0.49.0 is a monthly release headlined by the completion of the <code>$config()</code> protocol migration for Lexical's built-in node classes (<a href="https://redirect.github.com/facebook/lexical/pull/8640">#8640</a>), a breaking change that replaces per-node <code>getType()</code>/<code>clone()</code>/<code>importJSON()</code>/<code>importDOM()</code>/<code>transform()</code> boilerplate with runtime-synthesized behavior. It ships with a cluster of follow-up fixes hardening the synthesized <code>getType()</code> and <code>clone()</code> (including a stack overflow in compiled builds), an opt-in sticky horizontal scrollbar for overflowing tables, and a broad batch of fixes across tables, Markdown, HTML, code highlighting, and selection.</p> <h2>Breaking Changes</h2> <ul> <li><a href="https://lexical.dev/docs/api/modules/lexical"><code>lexical</code></a> — Built-in node classes are ported to the <a href="https://lexical.dev/docs/concepts/nodes"><code>$config()</code></a> protocol. The static <code>importJSON()</code>, <code>importDOM()</code>, <code>clone()</code>, and <code>transform()</code> methods are no longer present on ported nodes; use the higher-level equivalents instead (<code>LexicalEditor.parseEditorState</code>/<code>$generateNodesFromDOM</code>, the <code>$cloneWithProperties</code> helper, and extensions or <code>$transform</code> in <code>$config</code>). <code>getType()</code> is unchanged and safe to keep using, <code>__type</code> is now <code>readonly</code>, and node constructors require zero-argument defaults. Custom nodes may keep their static methods, but adopting <code>$config()</code> is recommended (<a href="https://redirect.github.com/facebook/lexical/pull/8640">#8640</a>)</li> <li><a href="https://lexical.dev/docs/api/modules/lexical"><code>lexical</code></a> — <code>LexicalCommand<T></code>'s payload type is now invariant to prevent unsafe structural compatibility between commands. Previously correct code (including explicit generics) needs no changes; only unsafe <code>LexicalCommand<unknown></code> usage must switch to the newly exported <code>AnyLexicalCommand</code> alias. <code>dispatchCommand</code>'s payload argument is now optional when a command's payload type is <code>undefined</code> or <code>void</code>, and redundant explicit type parameters were dropped from the <code>registerCommand</code>/<code>registerNodeTransform</code> call sites (<a href="https://redirect.github.com/facebook/lexical/pull/8877">#8877</a>)</li> </ul> <h2>New APIs & Features</h2> <ul> <li><a href="https://lexical.dev/docs/api/modules/lexical_table"><code>@lexical/table</code></a> — Opt-in sticky horizontal scrollbar for tables wider than their container, so the scrollbar stays anchored at the viewport bottom instead of only being reachable at the end of the table. Enable it with <code>hasStickyScrollbar: true</code> in the table config; style it via the new <code>tableStickyScrollbar</code> theme key (<a href="https://redirect.github.com/facebook/lexical/pull/8790">#8790</a>)</li> <li><a href="https://lexical.dev/docs/api/modules/lexical_table"><code>@lexical/table</code></a> — The <code><colgroup></code> element is now omitted from the table DOM when column widths are undefined, producing cleaner markup and letting the browser auto-size columns; it is created or removed dynamically as widths change (<a href="https://redirect.github.com/facebook/lexical/pull/8850">#8850</a>)</li> <li><a href="https://lexical.dev/docs/api/modules/lexical"><code>lexical</code></a> — Added an editor operation benchmark suite (paragraph split, bold formatting, range deletion, paste, select-all format) measured through full DOM reconciliation, to help contributors track core editing performance (<a href="https://redirect.github.com/facebook/lexical/pull/8856">#8856</a>)</li> </ul> <h2>Deprecations</h2> <ul> <li><a href="https://lexical.dev/docs/api/modules/lexical_table"><code>@lexical/table</code></a> — <code>$createTableSelection</code> is deprecated in favor of <code>$createTableSelectionFrom</code>, which takes the table node plus anchor/focus cells and validates them, instead of returning a blank selection with placeholder <code>'root'</code> keys that must be overwritten (<a href="https://redirect.github.com/facebook/lexical/pull/8855">#8855</a>)</li> <li><a href="https://lexical.dev/docs/api/modules/lexical_table"><code>@lexical/table</code></a> — <code>getShape()</code> is deprecated because it computes incorrect bounds for merged cells; use the now-exported <code>$computeTableCellRectBoundary</code> (with <code>$computeTableMap</code>) instead (<a href="https://redirect.github.com/facebook/lexical/pull/8853">#8853</a>)</li> </ul> <h2>Notable Fixes</h2> <p><strong><code>$config()</code> follow-ups (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical/issues/8640">#8640</a>)</strong></p> <ul> <li>The synthesized <code>clone()</code> now applies <code>afterCloneFrom()</code> when called directly (e.g. <code>NodeClass.clone(node)</code>), fixing silent property loss across 25+ core nodes (<a href="https://redirect.github.com/facebook/lexical/pull/8864">#8864</a>)</li> <li>The synthesized <code>getType()</code> no longer returns the superclass type when inherited, fixing node-type collisions during editor registration (<a href="https://redirect.github.com/facebook/lexical/pull/8867">#8867</a>)</li> <li>The synthesized <code>getType()</code> no longer recurses infinitely when a bundler copies it onto a subclass as an own static, fixing stack overflows in compiled/minified builds (<a href="https://redirect.github.com/facebook/lexical/pull/8869">#8869</a>)</li> </ul> <p><strong>Tables</strong></p> <ul> <li><code>DELETE_LINE_COMMAND</code> (Cmd/Ctrl-based delete-line shortcuts) now works inside table cells instead of being silently swallowed (<a href="https://redirect.github.com/facebook/lexical/pull/8851">#8851</a>)</li> <li>Table alignment now works when cells are selected in any direction, not just top-left→bottom-right (<a href="https://redirect.github.com/facebook/lexical/pull/8883">#8883</a>)</li> </ul> <p><strong>Selection & editing</strong></p> <ul> <li>Editor updates dispatched from a read-only context (e.g. inside <code>editor.read()</code>) now run in a fresh writable update instead of being silently dropped, with a dev warning (<a href="https://redirect.github.com/facebook/lexical/pull/8863">#8863</a>)</li> <li>Firefox now creates a selection and shows the block cursor when clicking in the gap between block decorators (<a href="https://redirect.github.com/facebook/lexical/pull/8862">#8862</a>)</li> <li>Pressing ArrowUp before a leading non-inline decorator no longer moves the selection out of the editor (<a href="https://redirect.github.com/facebook/lexical/pull/8887">#8887</a>)</li> </ul> <p><strong>Markdown, HTML & code</strong></p> <ul> <li>Typing a list marker at the start of a heading no longer converts the heading into a list (<a href="https://redirect.github.com/facebook/lexical/pull/8879">#8879</a>)</li> <li>HTML import now evaluates unrestricted CSS selector groups (e.g. <code>p, .foo</code>) against all elements instead of only tag-matched ones (<a href="https://redirect.github.com/facebook/lexical/pull/8873">#8873</a>)</li> <li>Concurrent async language/theme loads in the Shiki highlighter are deduplicated and merged into a single history entry, avoiding spurious undo states (<a href="https://redirect.github.com/facebook/lexical/pull/8854">#8854</a>)</li> <li><code>QuoteNode.updateDOM</code> now accepts the <code>EditorConfig</code> argument the reconciler passes to every other node, for API consistency (<a href="https://redirect.github.com/facebook/lexical/pull/8882">#8882</a>)</li> </ul> <p><strong>Playground & website</strong></p> <ul> <li>Playground scroll padding now accounts for the sticky toolbar so selections scrolled into view from above aren't hidden behind it (<a href="https://redirect.github.com/facebook/lexical/pull/8849">#8849</a>)</li> <li>Fixed a homepage crash caused by a minified dev build, and isolated each embedded example in its own error boundary so one failure no longer takes down the page (<a href="https://redirect.github.com/facebook/lexical/pull/8861">#8861</a>)</li> </ul> <h2>What's Changed</h2> <!-- raw HTML omitted --> </blockquote> <p>... (truncated)</p> </details> <details> <summary>Changelog</summary> <p><em>Sourced from <a href="https://github.com/facebook/lexical/blob/main/CHANGELOG.md">lexical's changelog</a>.</em></p> <blockquote> <h2>v0.49.0 (2026-07-29)</h2> <ul> <li>lexical-markdown Bug Fix Preserve headings when typing list shortcuts (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical/issues/8879">#8879</a>) Steven Dang</li> <li>lexical-rich-text Bug Fix Forward editorConfig to QuoteNode.updateDOM (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical/issues/8882">#8882</a>) Sha Halimi</li> <li>lexical-table Bug Fix Table alignment fails when selecting in non-TL-BR direction (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical/issues/8883">#8883</a>) sahir</li> <li>Breaking Changelexical Chore Remove redundant registerCommandregisterNodeTransform generics (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical/issues/8877">#8877</a>) mayrang</li> <li>lexical-html Bug Fix Dispatch unrestricted CSS selector groups (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical/issues/8873">#8873</a>) Madan kumar</li> <li>lexical-playground Bug Fix Deterministic history coalescing for flaky webkit undo tests (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical/issues/8874">#8874</a>) Bob Ippolito</li> <li>lexical Performance Skip redundant selection restoration in removeTextFromCaretRange (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical/issues/8872">#8872</a>) mayrang</li> <li>lexical Refactor Simplify RangeSelection.insertText via removeText decomposition (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical/issues/8870">#8870</a>) mayrang</li> <li>lexical-list Chore Remove redundant decorator-adjacent backspace handler (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical/issues/8871">#8871</a>) mayrang</li> <li>lexical-table Feature Skip colgroup element in table DOM with undefined col widths (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical/issues/8850">#8850</a>) Zachary Gallafent</li> <li>lexical Bug Fix Create selection when clicking between block decorators on Firefox (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical/issues/8862">#8862</a>) mayrang</li> <li>fix config() synthesized getType() recurses infinitely when inherited as own static (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical/issues/8867">#8867</a> follow-up) (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical/issues/8869">#8869</a>) Sherry</li> <li>lexicallexical-website Bug Fix Homepage crash from optimized dev build (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical/issues/8861">#8861</a>) Bob Ippolito</li> <li>lexical Bug Fix run editor updates dispatched from a read-only context in a fresh writable update (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical/issues/8863">#8863</a>) Sherry</li> <li>lexical Feature Editor operation benchmarks (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical/issues/8856">#8856</a>) mayrang</li> <li>lexical Chore Migrate LexicalSelection tests to buildEditorFromExtensions (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical/issues/8865">#8865</a>) mayrang</li> <li>fix config() synthesized getType() inherited by subclasses causes node-type collision (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical/issues/8640">#8640</a>) (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical/issues/8867">#8867</a>) Sherry</li> <li>fix config() auto-synthesized clone() loses properties when called directly (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical/issues/8640">#8640</a>) (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical/issues/8864">#8864</a>) Sherry</li> <li>lexical-tablelexical Feature Sticky scrollbar for overflowing tables (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical/issues/8790">#8790</a>) mayrang</li> <li>lexical-code-shiki Bug Fix Deduplicate async loads and merge into history (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical/issues/8854">#8854</a>) Alexis</li> <li>lexical-table Chore Remove dead code in table command handlers (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical/issues/8857">#8857</a>) mayrang</li> <li>lexical-tablelexical-playground Chore Deprecate getShape() and migrate playground (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical/issues/8853">#8853</a>) mayrang</li> <li>lexical-list Chore Rename isNestedListNode to isNestedListNode (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical/issues/8843">#8843</a>) Bob Ippolito</li> <li>lexical-playground Bug Fix account for sticky toolbar in scroll padding (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical/issues/8849">#8849</a>) Bob Ippolito</li> <li>Breaking Changeslexical Refactor Port node classes to the config() protocol (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical/issues/8640">#8640</a>) Bob Ippolito</li> <li>lexical-table Chore Deprecate createTableSelection in favor of createTableSelectionFrom (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical/issues/8855">#8855</a>) mayrang</li> <li>lexical-table Bug Fix Enable DELETELINECOMMAND in table cells (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical/issues/8851">#8851</a>) mayrang</li> <li>lexical Bug Fix Scope bench vitest projects to exclude regular test files (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical/issues/8852">#8852</a>) mayrang</li> <li>v0.48.0 (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical/issues/8847">#8847</a>) Bob Ippolito</li> <li>v0.48.0 Lexical GitHub Actions Bot</li> </ul> <h2>v0.48.0 (2026-07-16)</h2> <ul> <li>lexical-reactlexical-table Bug Fix Enable table copy in read-only mode (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical/issues/8845">#8845</a>) mayrang</li> <li>lexical-extensionlexical-mdastdev-mdast-editor-example Feature Add MdastHtmlExtension and Markdown custom-construct examples (collapsible, kbd, alerts, footnotes) (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical/issues/8826">#8826</a>) Bob Ippolito</li> <li>Fix fail closed in LinkNode.sanitizeUrl() on unparseable URLs (XSS) (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical/issues/8846">#8846</a>) xiezhenjia-meta</li> <li>lexical Chore Fix serialize-javascript package dependency vulnerability (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical/issues/8803">#8803</a>) vijay ojha</li> <li>lexical-react Bug Fix Count block separators in character limit overflow wrapping (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical/issues/8840">#8840</a>) mayrang</li> <li>lexical-yjslexical-react Feature Customizable Yjs shared-type root name (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical/issues/8841">#8841</a>) mayrang</li> <li>lexical-list Bug Fix Backspace at start of list item outdents or converts to paragraph (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical/issues/8829">#8829</a>) mayrang</li> <li>lexical-table Feature Add moveTableRow function Add missing export for unmergeCellNode (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical/issues/8833">#8833</a>)</li> <li>lexical-link Bug Fix disable link opening for disabled autolink in (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical/issues/8839">#8839</a>) Olivier Chevallier</li> <li>lexical-rich-textlexical-plain-text Bug Fix dont cancel dragover for text drags so native drops work again (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical/issues/8842">#8842</a>) Bob Ippolito</li> <li>Open playground links in a new tab (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical/issues/8837">#8837</a>) Sherry</li> <li>lexical-react Bug Fix Merge adjacent OverflowNodes in useCharacterLimit (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical/issues/8831">#8831</a>) mayrang</li> <li>lexical-code-shiki Bug Fix force re-tokenize after async language load (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical/issues/8830">#8830</a>) Olivier Chevallier</li> <li>lexical-tablelexical-playground Bug Fix Auto-scroll while drag-selecting cells past the visible edge (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical/issues/8822">#8822</a>) Oleksandr Trukhnii</li> <li>lexical-mdastlexical-markdown Bug Fix Roundtrip overlapping inline formats (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical/issues/8825">#8825</a>) Bob Ippolito</li> </ul> <!-- raw HTML omitted --> </blockquote> <p>... (truncated)</p> </details> <details> <summary>Commits</summary> <ul> <li><a href="https://github.com/facebook/lexical/commit/ffe90924bd55b5d450c88de0f9f1c8b228c4a221"><code>ffe9092</code></a> v0.49.0</li> <li><a href="https://github.com/facebook/lexical/commit/82cdae1d87c4bfdc84c39a195fc79b15fafb65e6"><code>82cdae1</code></a> [Breaking Change][lexical] Chore: Remove redundant registerCommand/registerNo...</li> <li><a href="https://github.com/facebook/lexical/commit/0a84aef949bf673a7f52cd6f4fd63b680f5f7fb7"><code>0a84aef</code></a> [lexical] Performance: Skip redundant selection restoration in $removeTextFro...</li> <li><a href="https://github.com/facebook/lexical/commit/97fef1f27bbc3be9dbbc8d62ced1bbe7afc8e948"><code>97fef1f</code></a> [lexical] Refactor: Simplify RangeSelection.insertText via removeText decompo...</li> <li><a href="https://github.com/facebook/lexical/commit/443012cdbb59be3accfece77e60db0eaea715bea"><code>443012c</code></a> [lexical] Bug Fix: Create selection when clicking between block decorators on...</li> <li><a href="https://github.com/facebook/lexical/commit/fccc283e574b9561b849b70d38e4cc3fa1bc289c"><code>fccc283</code></a> fix: $config() synthesized getType() recurses infinitely when inherited as ow...</li> <li><a href="https://github.com/facebook/lexical/commit/3429b575bbf188dedbd1aa466cd69ec312ef4df0"><code>3429b57</code></a> [lexical][lexical-website] Bug Fix: Homepage crash from optimized dev build (...</li> <li><a href="https://github.com/facebook/lexical/commit/aabfaaad4bf5710d707406d50bedb2f896c525fb"><code>aabfaaa</code></a> [lexical] Bug Fix: run editor updates dispatched from a read-only context in ...</li> <li><a href="https://github.com/facebook/lexical/commit/e5e345d7e107eac6e0c0e169b1a31a5b3e5e0da1"><code>e5e345d</code></a> [lexical] Feature: Editor operation benchmarks (<a href="https://github.com/facebook/lexical/tree/HEAD/packages/lexical/issues/8856">#8856</a>)</li> <li><a href="https://github.com/facebook/lexical/commit/385a93c0098ab2dbdaea305065142902be1233d5"><code>385a93c</code></a> [lexical] Chore: Migrate LexicalSelection tests to buildEditorFromExtensions ...</li> <li>Additional commits viewable in <a href="https://github.com/facebook/lexical/commits/v0.49.0/packages/lexical">compare view</a></li> </ul> </details> <br /> Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> |
||
|
|
d0d242e843 |
feat(server): reopen an archived isolated execution workspace in place (#11322)
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - Issue execution uses isolated workspaces that hold the issue
worktree
> - A terminal issue can leave its isolated workspace archived and
unable to resume
> - The existing closed-workspace guards returned a conflict and gave
the user no self-serve recovery
> - This pull request reopens the same isolated workspace row and
rebuilds its worktree
> - The benefit is that resume, checkout, and comment actions can
continue without a new workspace row
## Linked Issues or Issue Description
**Problem or motivation**
A terminal issue can point to an archived isolated execution workspace.
Resume, checkout, and comment actions then stop with a conflict.
**What happened?**
A terminal issue kept its issue-to-workspace link after the isolated
workspace reached a closed status. The guarded actions returned HTTP 409
instead of restoring access.
**Expected behavior**
The next authorized resume, checkout, or comment action reopens the same
isolated workspace row. The action rebuilds the worktree and then
continues.
**Steps to reproduce**
1. Create an issue that uses an isolated execution workspace.
2. Move the issue to a terminal state and let the workspace archive.
3. Try to resume the issue or add a comment.
4. Observe the closed-workspace conflict.
**Paperclip version or commit**
|
||
|
|
a09d7dcc06 |
feat(ui): bounce cold arrivals off archived company URLs, add Unarchive (#11302)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - Archiving a company hides it from the sidebar switcher, but remembered last-visited paths, browser history, bookmarks, and restored tabs keep depositing users onto its URLs long after archiving > - Since the selection ping-pong fix (#11300) those arrivals render, but the user is stranded inside a workspace the sidebar refuses to show — and unarchiving had no UI anywhere, so the only way back was a hand-typed settings URL > - This pull request bounces cold arrivals at archived company URLs to an active company (with a toast naming why), lets deliberate visits stick, and adds an Unarchive action to the companies list > - The benefit is that stale URLs stop stranding users in retired workspaces, and archived companies become restorable from the one page that still lists them ## Linked Issues or Issue Description Follow-up to #11300. No existing issue for the remaining gap; description follows the enhancement template: **What happened?** After #11300, opening an archived company's URL (stale tab, history, bookmark, remembered path) renders that company's pages — but the sidebar switcher does not list it, so the user is stranded in a workspace they retired, and every stale URL pulls them back in. Separately, unarchiving a company has no UI: the archive button lives in company settings, which becomes unreachable through normal navigation once the company is archived. **Expected behavior** Arriving cold at an archived company's URL lands the user in an active workspace, with a toast explaining the redirect. Explicitly choosing the archived company (from the companies list) still works, so its pages remain reachable. Archived companies can be restored from the companies list. **Steps to reproduce** 1. Create two companies; archive one. 2. Open `/{archivedPrefix}/dashboard` directly — before: renders the archived workspace with no sidebar presence; after: bounces to the active company's dashboard with a toast. 3. On the companies list, open the archived company's row menu — before: no restore action anywhere; after: Unarchive. ## What Changed - `ui/src/lib/company-selection.ts`: `resolveArchivedCompanyBounce` — pure policy: bounce when the URL names an archived company that is not the current selection and an active company exists; prefer the currently selected active company as the destination. - `ui/src/components/Layout.tsx`: the route-sync effect applies the bounce (toast + selection + `replace` navigation) before syncing selection from the route. - `ui/src/pages/Companies.tsx`: Unarchive action (`PATCH status: "active"`) in the row menu for archived companies. - Tests: unit cases for the bounce policy; the e2e now drives all three behaviors (direct-load bounce with toast, re-arrival bounce, deliberate visit sticks) on top of the existing crash regression. ## Verification - `pnpm vitest run src/lib/company-selection.test.ts src/context/CompanyContext.test.tsx src/pages/Companies.test.tsx` in `ui/` — 20 tests pass. - `npx playwright test --config tests/e2e/playwright.config.ts archived-company-url` — passes, covering bounce, toast, and deliberate-visit paths. - `pnpm typecheck` in `ui/` — clean. ## Risks Low risk. The bounce only fires for archived-company URLs when the archived company is not already selected and an active company exists; all-archived instances render as before. Deliberate selection from the companies list is unaffected (selection equals the matched company, so no bounce). Unarchive reuses the existing `PATCH /api/companies/:id` status transition the server already supports. ## Model Used - Claude (Anthropic), Claude Fable 5 (`claude-fable-5`), via Claude Code CLI with extended thinking and tool use (code search, edit, test execution, Playwright e2e). ## 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 |
||
|
|
68c699687e |
fix(ui): keep selected run lifecycle and logs current (#10169)
## Thinking Path > - Paperclip's agent-run detail view combines live lifecycle events, detail queries, and persisted shell logs. > - Queued runs do not have a log reference yet, so polling the log endpoint returns expected-but-noisy 404s. > - Simply disabling queued log polling can strand the selected detail cache at `queued` or leave terminal lifecycle fields stale. > - The detail cache therefore needs both live lifecycle patches and an HTTP fallback while the run is active. > - Both the selected-run query and the shared transcript hook must avoid reading persisted logs before execution starts. > - This pull request disables queued log polling in both paths, patches lifecycle fields, invalidates authoritative detail data, and polls run state only while active. > - The benefit is quiet queued runs, reliable queued-to-running transitions, and complete final log handoff. ## Linked Issues or Issue Description No exact duplicate found. I searched open PRs for `queued run`, `log polling`, and `run lifecycle`; related results addressed stale queued execution or dashboards rather than selected-run log/detail synchronization. **What happened?** Opening or rendering a queued run caused repeated `GET /api/heartbeat-runs/:id/log` 404s from both the selected detail and shared transcript hydration paths. A queued-only detail guard could also leave the selected run stuck at `queued` when live delivery was missed, or stop before fetching final `logRef` and terminal fields. **Expected behavior** Queued runs should not request unavailable logs. The selected detail should transition through running to terminal via live events or bounded HTTP fallback, refresh authoritative detail data, fetch final logs, and then stop polling. **Steps to reproduce** 1. Open the detail page for a run waiting behind the agent concurrency limit. 2. Observe repeated log-endpoint 404s while status is queued. 3. Let the run start with WebSocket delivery unavailable or delayed. 4. Observe stale detail state without a run-detail fallback. **Environment** - Paperclip base: `14f20be92b86a49ff2c35495e5b0fa4d719998ef` - Deployment: self-hosted, built from source - Adapter scope: visible with Hermes-backed agents but not adapter-specific - [x] I searched open PRs for queued-run log polling and selected-run lifecycle synchronization; no exact duplicate was found. ## What Changed - Poll shell logs only while a run is `running`, never while `queued`. - Defer shared persisted-transcript hydration and its live WebSocket until a queued run becomes `running`; terminal runs still receive one persisted-log hydration attempt. - Patch selected run status, invocation metadata, errors, and start/finish times from lifecycle events. - Invalidate selected run detail on lifecycle events to hydrate authoritative `logRef`, result, usage, and excerpts. - Poll run detail every 5 seconds while queued and every 15 seconds while running, then stop at terminal status. - Add regression coverage for queued/running/terminal polling and lifecycle cache handoff. ## Verification - `pnpm exec vitest run ui/src/context/LiveUpdatesProvider.test.ts ui/src/pages/AgentDetail.progress.test.ts` — 42 passed. - `pnpm exec vitest run ui/src/components/transcript/useLiveRunTranscripts.test.tsx` — 11 passed. - `pnpm --filter @paperclipai/ui typecheck` - `pnpm --filter @paperclipai/ui build` - Exact final diff independently reviewed: **APPROVE**, no blocking findings. - UI-only local cutover completed with the Paperclip server PID unchanged. ## Risks - The selected run performs one lightweight detail GET every 5 seconds while queued and every 15 seconds while running if it remains open. Polling stops at terminal status. - Lifecycle events still patch immediately; polling is only the fallback and authoritative hydration path. - No API, schema, or migration changes. > This is a bug fix, not roadmap feature work. ## Model Used OpenAI Codex `gpt-5.6-sol`, with tool use, code execution, repository inspection, and independent read-only review agents. ## 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/described the search above - [x] I have described the issue in-PR following the bug template - [x] I have not referenced internal/instance-local Paperclip issues or links - [x] My branch name describes the change and contains no internal ticket id - [x] I have run tests locally and they pass - [x] I have added or updated tests where applicable - [x] I have considered documentation; no user-facing documentation change is required - [x] I have considered and documented 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: cucurigoo <cucurigoo@users.noreply.github.com> |
||
|
|
106955bdfd |
fix(ui): gate summary built-in requests by feature flag (#10170)
## Thinking Path > - Paperclip's web UI conditionally exposes experimental summary and built-in-agent capabilities. > - Summary cards depend on the built-in Summarizer agent, but the two capabilities have independent feature flags. > - `SummarySlotCard` and the reusable `BuiltInAgentGate` enabled built-in-agent lookups without requiring `enableBuiltInAgents`. > - When built-in agents were disabled, those surfaces called a server route that was intentionally unavailable and generated avoidable 404s. > - The client query should obey both server-side feature gates. > - This pull request adds the missing gate and a cross-flag regression. > - The benefit is consistent feature-flag behavior and no request loop against a disabled endpoint. ## Linked Issues or Issue Description No exact duplicate found. I searched open PRs for `SummarySlotCard`, `BuiltInAgentGate`, `enableBuiltInAgents summaries`, and `built-in agents 404`. Related PR #10116 gates `SidebarAgents`; this PR deliberately excludes that file and covers the remaining summary/gate callers. **What happened?** `SummarySlotCard` called `builtInAgentsApi.list` whenever summaries were enabled, and `BuiltInAgentGate` called it whenever a company was selected. The server rejects that route when `enableBuiltInAgents` is false, so the disabled configuration produced repeated 404 requests. **Expected behavior** Built-in-agent queries should run only when built-in agents are enabled; the summary-specific query also requires summaries to be enabled. **Steps to reproduce** 1. Enable summaries. 2. Disable built-in agents. 3. Render a page containing `SummarySlotCard` or `BuiltInAgentGate`. 4. Observe a request to the disabled built-in-agents route. **Environment** - Paperclip web UI - Cross-flag configuration: summaries enabled, built-in agents disabled - [x] I searched open PRs for the affected component, feature flags, and 404 behavior; no exact duplicate was found. ## What Changed - Require both `enableSummaries` and `enableBuiltInAgents` in `SummarySlotCard`. - Make `BuiltInAgentGate` resolve experimental settings before enabling its built-in-agent query and fail open when the feature is disabled. - Add cross-flag regressions for both callers. - Leave `SidebarAgents` to related PR #10116 rather than duplicating it. ## Verification - `pnpm exec vitest run ui/src/components/SummarySlotCard.test.tsx ui/src/components/BuiltInAgentGate.test.tsx` — passed as part of a 41-test built-in UI group. - `pnpm --filter @paperclipai/ui typecheck` — passed. - `pnpm --filter @paperclipai/ui build` — passed in combined deployment staging. - UI-only local cutover completed with the Paperclip server PID unchanged. - The complete UI fix was staged after a summary-only cutover exposed the remaining reusable-gate caller. ## Risks - Low risk: this changes only whether one query is enabled under a feature-flag combination where the server route is unavailable. - No API, schema, migration, authentication, or persistence changes. - Rollback is a single commit revert. > This is a bug fix, not roadmap feature work. ## Model Used OpenAI Codex `gpt-5.6-sol`, with tool use, code execution, repository inspection, and read-only review-agent evidence. ## 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 described the search above - [x] I have described the issue in-PR following the bug template - [x] I have not referenced internal/instance-local Paperclip issues or links - [x] My branch name describes the change and contains no internal ticket id - [x] I have run tests locally and they pass - [x] I have added or updated tests where applicable - [x] I have considered documentation; no user-facing documentation change is required - [x] I have considered and documented 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: cucurigoo <cucurigoo@users.noreply.github.com> |
||
|
|
b42efe2063 |
fix(ui): gate built-in agent sidebar query (#10116)
## Thinking Path > - Paperclip is the open-source control plane people use to manage AI agents and their work. > - The sidebar renders company agents and, when enabled, their built-in lifecycle state. > - The built-in-agents API is intentionally unavailable when the experimental feature is disabled. > - `SidebarAgents` nevertheless queried that endpoint whenever a company was selected, producing routine 404s in normal feature-off installations. > - The query must be gated by the shared instance setting, including its unresolved state, without exposing stale cached lifecycle badges. > - This pull request adds that gate and focused disabled, unresolved, and enabled regressions. > - The benefit is a quiet sidebar network path with unchanged behavior for installations that enable built-in agents. ## Linked Issues or Issue Description ### Pre-submission checklist - [x] I searched existing open and closed issues/PRs and found no exact duplicate. Related but not duplicate: #4149. - [x] I reproduced this on current `master`. - [x] I confirmed the error originates in Paperclip's sidebar query gating, not an adapter, provider, or local configuration. ### What happened? With `enableBuiltInAgents: false`, mounting `SidebarAgents` for a selected company still called `GET /api/companies/:companyId/built-in-agents`. The server correctly returned 404 because the experimental feature was disabled. ### Expected behavior The sidebar must not call the built-in-agent endpoint until the shared experimental setting resolves to exactly `true`. Cached built-in lifecycle state must also remain hidden while disabled. ### Steps to reproduce 1. Set `enableBuiltInAgents` to `false`. 2. Open any company so the sidebar agent list mounts. 3. Observe a request to `/api/companies/:companyId/built-in-agents` and a 404 response. ### Environment - Paperclip commit: `f2f168f6a10a24c924516808f414baba52b1c080` - Deployment mode: self-hosted server - Installation method: built from source - Adapter: not adapter-specific (core UI bug) - Database mode: not database-related - Access context: board (human operator) - Node.js: `v22.22.3` - Operating system: Linux - Relevant config: `{"enableBuiltInAgents": false}` - Relevant output: redacted HTTP 404 from `/api/companies/:companyId/built-in-agents` - Privacy: all instance-local identifiers, paths, and output were omitted or redacted. ## What Changed - Read instance experimental settings in `SidebarAgents` through the shared React Query key. - Enable the built-in-agent list query only when `enableBuiltInAgents` is explicitly `true`. - Ignore cached built-in lifecycle data while the feature is disabled. - Add regressions for disabled, unresolved, and enabled settings states. ## Verification - RED before implementation: the disabled-feature regression failed because `builtInAgentsApi.list` was called once. - `pnpm --filter @paperclipai/ui exec vitest run src/components/SidebarAgents.test.tsx` — 24 tests passed. - `pnpm --filter @paperclipai/ui exec tsc -p tsconfig.json --pretty false` — passed. - `git diff --check` — passed. - The existing test file emits pre-existing React `act(...)` warnings while passing. ## Risks - Low risk: request gating only; no API, schema, migration, or visible UI contract changes. - A stale experimental-settings cache could delay enabling the query until the normal settings invalidation/refetch path runs; this is the same shared query key already used elsewhere. - Cached built-in statuses are deliberately hidden whenever the setting is not literal `true`. > 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.6-sol` was used with reasoning, repository/file tools, shell command execution, and delegated read-only code review. The runtime did not expose a context-window size. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have either (a) linked existing issues with `Fixes: #` / `Closes #` / `Refs #` OR (b) described the issue in-PR following the relevant issue template - [x] I have not referenced internal/instance-local Paperclip issues or links (only public GitHub `#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 (no documentation change needed; this enforces the existing feature flag) - [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: cucurigoo <cucurigoo@users.noreply.github.com> |
||
|
|
8b1483e601 |
fix(ui): render board approval payload prose as markdown (#9817)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - The approval queue is how agents surface decisions that need a human, so the approval card is often the only thing an operator reads before approving or rejecting > - Agents author those payloads in markdown, because markdown is what they produce everywhere else in the product > - `ApprovalPayload.tsx` renders the four prose fields of a board approval as bare text nodes, while `CommentThread` on the same page renders through `MarkdownBody` — same authors, two different renderers > - So the operator sees literal `##`, `**bold**`, backticks and `[link](url)` in the payload, and correctly formatted text in the comments directly below it > - This pull request routes those four fields through the existing `MarkdownBody` component > - The benefit is that the highest-stakes text in the product becomes readable, with no new dependency and no schema change ## Linked Issues or Issue Description Refs #4911 — prior art, see the note at the bottom of this description. No open issue covers this, so per (B) here is the bug report: **What happened:** On a board approval, the `summary`, `recommendedAction`, `nextActionOnApproval` and `risks` fields display raw markdown source. Headings appear as literal `##` mid-paragraph, inline code keeps its backticks, links show as `[text](url)`, and both levels of a nested bullet list collapse into one run-on paragraph. **What was expected:** The same rendering the comment thread further down the same page already gives, since both are agent-authored markdown. **Steps to reproduce:** Open any `request_board_approval` whose `summary` contains markdown — headings, a nested list, code spans or links. **Where:** `ui/src/components/ApprovalPayload.tsx`, `BoardApprovalPayloadContent`. ## What Changed - `ui/src/components/ApprovalPayload.tsx`: import `MarkdownBody` and render `summary`, `recommendedAction`, `nextActionOnApproval` and each `risks` entry through it instead of `<p>` / `<span>` text nodes. `MarkdownBody` defaults `softBreaks` to `true`, which is the same behaviour `CommentThread` opts into explicitly, so paragraph handling matches the comments. - `stripLeadingListMarker`: risks already render inside a custom bullet row, so an authored leading `-` / `*` / `•` would nest a second bullet inside the first. One leading marker is stripped per entry. - The risk bullet dot gains `shrink-0` so it keeps its shape next to block-level markdown content. - `title` stays plain text — it is a one-liner and markdown there is noise. - `proposedComment` stays a verbatim `<pre>` block — it is draft text intended to be posted elsewhere, so it must not be reinterpreted. - `ui/src/components/ApprovalPayload.test.tsx`: tests for markdown rendering in all four fields, the leading-list-marker strip, and that `title` and `proposedComment` remain verbatim. ## Verification - `npx vitest run ui/src/components/ApprovalPayload.test.tsx` — 5 passed. - `npx vitest run ui/src/components/ApprovalPayload.test.tsx ui/src/components/CommentThread.test.tsx` — 12 passed, confirming the shared `MarkdownBody` path is not disturbed. - Manual, measured rather than eyeballed: I ran a patched build in a throwaway container beside an unpatched one and pointed both at the same real approval payload, then counted nodes in the rendered DOM. | | unpatched | patched | |---|---|---| | `.paperclip-markdown` nodes | 0 | 3 | | raw backticks in visible text | yes | no | | rendered `h2` | 0 | 5 | | rendered `li` | 0 | 16 | ## Risks Low, and confined to the board approval card. - Rendering scope widens from text to markdown on four fields. A payload that contains markdown-significant punctuation but was authored as prose could render differently than before. This is the intended change, and it matches how the same author's text is already rendered in comments on the same page. - `stripLeadingListMarker` removes one leading list marker per risk entry. A risk that genuinely begins with a literal hyphen followed by a space loses that hyphen. Chosen over the alternative of a visible double bullet on the common case. - No schema change, no migration, no new dependency. `MarkdownBody` is already used elsewhere in the same directory. ## Model Used Claude Opus 4.8 (`claude-opus-4-8`), via Claude Code, with extended thinking and tool use (repository search, file editing, local test execution, and headless-browser DOM measurement of the before/after renders). ## 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 - [ ] I have updated relevant documentation to reflect my changes — no docs describe this rendering behaviour, so there was nothing to update - [x] I have considered and documented any risks above - [ ] All Paperclip CI gates are green — pending first CI run on this PR - [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups — pending first review - [x] I will address all Greptile and reviewer comments before requesting merge --- ### On the prior PR @alxhrzg opened #4911 for this same bug first, and reached the same conclusion I did: route the four fields through `MarkdownBody`. Credit for spotting it and for the diagnosis goes there. That PR has been conflicting against base and untouched since May. Rather than let the fix sit, this PR reapplies the idea on current `master` and adds what #4911 was missing: test coverage, the nested-bullet fix for `risks`, and the `shrink-0` on the bullet dot. I could not push to #4911 directly as it is on another contributor's fork. @alxhrzg, if you would rather finish #4911 yourself, I am happy to close this and hand over the tests and the two risk-row fixes for you to take across. --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
61a5b7c6f9 |
fix(ui): stop the selection ping-pong on archived company URLs (#11300)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - The UI keeps a selected company in `CompanyProvider` with two writers: a bootstrap effect that repairs invalid selections, and a Layout route-sync effect that selects the company the URL prefix names > - The route-sync matches the URL against the full company list (archived included), while the bootstrap resolver only accepted companies from the sidebar-filtered non-archived list > - On any archived company's URL the two effects overwrite each other's selection in a synchronous loop until React throws error #185 ("Maximum update depth exceeded") and unmounts the root to a blank page — armed by remembered last-visited paths, back/forward navigation, or bookmarks, on first load and client navigation alike > - This pull request makes an already-selected company only need to exist, keeping the sidebar filter for fresh-boot resolution where no explicit selection exists > - The benefit is that archived company URLs render instead of blanking the entire app ## Linked Issues or Issue Description No existing issue. Description follows the bug template: **What happened?** Opening (or back-navigating to) a URL whose company prefix belongs to an archived company blanked the whole app with `Minified React error #185`. Console in dev mode: "Maximum update depth exceeded. This can happen when a component calls setState inside useEffect…". A workspace whose first/seeded company was archived hit this on every load of its remembered URL. **Expected behavior** An archived company's URL renders its pages (the company still exists and its API routes serve data). The sidebar simply does not feature archived companies, and fresh boots still land on a non-archived company. **Steps to reproduce** 1. Create two companies; archive one (`PATCH /api/companies/:id` with `status: "archived"`). 2. Navigate to `/{archivedPrefix}/dashboard` — direct load or client-side back-navigation. 3. Before this fix: React #185 and an unmounted blank page (reproduced deterministically by the new e2e test). ## What Changed - `ui/src/context/CompanyContext.tsx`: `resolveBootstrapCompanySelection` keeps an explicitly selected company that exists in the full company list; stored-id and default resolution still prefer sidebar (non-archived) companies. - `ui/src/context/CompanyContext.test.tsx`: resolver keeps an archived-but-existing selection; a truly deleted selection is still replaced. - `tests/e2e/archived-company-url.spec.ts`: end-to-end regression driving both field shapes (direct load and back-navigation onto an archived company URL); it failed with the exact #185 console errors before the fix and passes after. ## Verification - `pnpm vitest run src/context …` in `ui/` — 122 tests pass (includes the new resolver cases). - `npx playwright test --config tests/e2e/playwright.config.ts archived-company-url` — fails before the fix (captured "Maximum update depth exceeded" console errors), passes after. - `pnpm typecheck` in `ui/` — clean. ## Risks Low risk. The only behavioral change is that a selection naming an archived-but-existing company survives the bootstrap repair — previously that state was unreachable without crashing. Boots with no valid selection behave exactly as before (non-archived preferred), covered by the existing and new resolver tests. ## Model Used - Claude (Anthropic), Claude Fable 5 (`claude-fable-5`), via Claude Code CLI with extended thinking and tool use (code search, edit, test execution, Playwright-driven crash reproduction). ## 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 |
||
|
|
0ee0543e5b |
feat(ui): refine the chat-style task workflow (#11263)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work. > - The issue detail view is the main place where people read work and guide agents. > - The chat-style task view needs clear messages, controls, properties, and document feedback. > - Dense metadata and disconnected controls make active work harder to scan. > - This pull request refines the existing chat-style task workflow across desktop and mobile layouts. > - The benefit is a clearer issue thread with faster access to the controls that guide work. ## Linked Issues or Issue Description **What existing behavior does this improve?** The change improves the chat-style issue detail view that was introduced in [#10606](https://github.com/paperclipai/paperclip/pull/10606) and expanded in [#10707](https://github.com/paperclipai/paperclip/pull/10707). **Current behavior** The issue thread spreads task controls across the page. Agent-turn metadata competes with the message content. Document annotation comments use inline placement that limits the document reading area. The mobile composer can overlap the bottom navigation. **Proposed behavior** The issue view keeps the thread focused on message content. It moves supporting controls into the properties area, adds searchable assignment, restores the sub-task tree, docks document comments in a side gutter, and keeps the mobile composer clear of navigation. **Reason and benefit** People can scan active work faster and find task controls without leaving the issue. The layout also gives documents and mobile conversations more usable space. **Breaking changes** None. The change updates presentation and interaction behavior in the existing issue UI. ## What Changed - Refined task-chat message spacing, metadata, agent bubbles, and composer alignment. - Added searchable assignment and restored sub-task navigation in the properties pane. - Moved document annotation comments into a right-side gutter. - Kept the mobile composer above the auto-hiding bottom navigation. - Added and updated focused component tests for the changed interactions. ## Verification - `pnpm check:token-gates` - `TZ=UTC pnpm --filter @paperclipai/ui exec vitest run src/components/InlineEntitySelector.test.tsx src/components/IssueDocumentAnnotations.test.tsx src/components/IssueProperties.test.tsx src/components/TaskChatThread.test.tsx src/pages/IssueDetail.test.tsx` - The token gates report 3/3 clean. - The focused test run passes 124 tests in 5 files. - Visual snapshot baselines were not updated. This follows the `doc/design/DECISION-SHEET.md` entry "Per-change snapshot verification demoted to dormant (Jul 13 2026)." ## Risks - The changes affect several related issue-detail layouts. A browser review should cover desktop and mobile widths before merge. - The monitor-row test formats time in the host timezone. The verification command sets `TZ=UTC` to match CI. > 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 with GPT-5. The context-window size is not exposed in this environment. The model used reasoning, repository tools, code execution, and GitHub tools. ## 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 Opus 4.8 <noreply@anthropic.com> Co-authored-by: Paperclip <noreply@paperclip.ing> |
||
|
|
ff5fd62d07 |
Resolve the environment secret companyId context on first save (#11291)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - Environments give agent runs an execution target, and their env vars can carry secrets, so writes need a company scope for secret bindings > - `PATCH /environments/:id` resolves that scope from a query param, existing bindings, or a single-membership actor — and fails closed otherwise > - A fresh environment has no bindings yet, so an admin whose memberships cannot pin one company gets a 422 on the very first env var save and can never create the first binding > - #11200 made this reachable from the UI: it opened the envVars-only edit on the managed sandbox row, but the UI never sends the company it already knows > - This pull request sends the page's selected company on environment updates and adds a server fallback to the instance's only company > - The benefit is that the first env var save works on fresh environments, while multi-company ambiguity still fails closed ## Linked Issues or Issue Description Refs #11200 **What happened?** On a fresh platform-managed sandbox environment, the first "Save environment variables" in the UI fails with HTTP 422: "Environment secret management requires a companyId context during the instance-scoped transition." The same failure hits any environment update that touches `envVars` or `config` when the environment has no secret bindings yet and the actor's memberships do not name exactly one company (for example an instance admin provisioned without a membership row, or a user in two companies). **Expected behavior** The save succeeds. The client tells the server which company scopes the secret bindings, and on a single-company instance the server can resolve the only possible scope by itself. **Steps to reproduce** 1. Provision a platform-managed sandbox environment (managed-config `environments` entry) so the row exists with no secret bindings. 2. Sign in as an instance admin whose memberships do not resolve to exactly one company. 3. Open Environments, edit the managed row, add an env var, and save. 4. The PATCH returns 422 with the companyId-context error. ## What Changed - `ui/src/api/environments.ts`: `environmentsApi.update` accepts an optional `companyId` and sends it as the `companyId` query param the server already reads. Renamed the `customImageCompanyQuery` helper to `companyIdQuery` since it now serves plain updates and probes too. - `ui/src/pages/CompanyEnvironments.tsx`: both the managed envVars-only save and the general environment save pass `selectedCompanyId`. - `server/src/routes/environments.ts`: `resolveEnvironmentSecretContextCompanyId` falls back to the instance's only company when exactly one exists, mirroring the existing fallback in `resolveCustomImageCompanyId`. Multi-company instances still fail closed. - Tests: three new route tests (explicit query context for a multi-company actor, single-company-instance fallback for a membership-less admin, fail-closed regression on a multi-company instance), and updated the SSH-probe and probe-ambiguity tests for the new fallback. ## Verification - `cd server && pnpm vitest run src/__tests__/environment-routes.test.ts` — 78 pass. - `cd ui && pnpm vitest run src/pages/CompanyEnvironments.test.tsx` — 22 pass. - Full `server` and `ui` vitest suites and `tsc --noEmit` on both packages pass locally. - Manual: on a managed instance, edit the managed sandbox environment, add an env var, save. Before: 422. After: the save persists and the PATCH carries `?companyId=`. ## Risks - Low risk. The explicit query param path already existed on the server; the UI now uses it. - Behavioral shift: on single-company instances, environment secret-context resolution (including `POST /environments/:id/probe`) now resolves the only company instead of returning no context. That lets probes resolve secret-backed config where they previously returned a 422 asking for an explicit companyId. Multi-company instances keep the fail-closed behavior, covered by a regression test. - Self-hosted single-company instances gain the same first-save fix; no schema or config changes. ## Model Used Claude Fable 5 (`claude-fable-5`, Anthropic) via Claude Code — extended thinking, agentic tool use. ## 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 |
||
|
|
01112c350c |
fix(ui): always give respondWith a real Response in the sw fetch fallback (#11292)
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - The UI registers a service worker (`ui/public/sw.js`) with a
network-first fetch handler whose cache is an offline fallback
> - The fallback hands `event.respondWith` the result of
`caches.match(...)`, which resolves `undefined` on a cache miss — and in
the navigation branch, `caches.match("/") || offlineResponse` never uses
the fallback because `caches.match` returns a promise, which is always
truthy
> - When the network fetch rejects (server restart, deploy, brief
outage) and the cache misses, the browser fails the request with
`Uncaught (in promise) TypeError: Failed to convert value to
'Response'`, so navigation breaks outright instead of degrading to the
offline page
> - This pull request awaits the cache lookups and guarantees a real
`Response` on every path
> - The benefit is that brief server unavailability degrades to the
offline fallback instead of a dead navigation
## Linked Issues or Issue Description
No existing issue. Description follows the bug template:
**What happened?**
Navigating while the server was briefly unavailable (mid-restart)
produced `The FetchEvent for "…" resulted in a network error response:
the promise was rejected.` and `sw.js:1 Uncaught (in promise) TypeError:
Failed to convert value to 'Response'.` The navigation failed instead of
showing the offline fallback.
**Expected behavior**
A failed navigation serves the cached app shell when present, otherwise
the "Offline" 503 response. A failed asset fetch serves its cache entry
when present, otherwise a proper network-error response. `respondWith`
always receives a real `Response`.
**Steps to reproduce**
1. Load the app so `sw.js` is active; ensure `/` is not in the service
worker cache (fresh cache version).
2. Restart or stop the backend.
3. Navigate to any page: the fetch rejects, `caches.match` misses, and
the browser logs the conversion TypeError with a failed navigation.
## What Changed
- `ui/public/sw.js`: the fetch fallback awaits `caches.match(...)` and
returns the "Offline" 503 for navigations and `Response.error()` for
assets when the cache misses.
- `ui/src/lib/sw-offline-fallback.test.ts`: evaluates the real `sw.js`
in a sandboxed scope and covers the three fallback paths; the two
miss-path tests fail against the previous code.
## Verification
- `pnpm vitest run src/lib/sw-offline-fallback.test.ts` in `ui/` — 3
tests pass.
- Verified both miss-path tests fail against the unmodified `sw.js`.
## Risks
Low risk. The change only affects the fetch-rejection path; successful
fetches and cache hits behave exactly as before. `Response.error()`
mirrors what the browser would produce for an unhandled failed no-cors
fetch.
## Model Used
- Claude (Anthropic), Claude Fable 5 (`claude-fable-5`), via Claude Code
CLI with tool use (code search, edit, test execution).
## 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
|
||
|
|
1a377424db |
fix(ui): add mobile blocker actions (#11282)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work. > - Operators use task properties to inspect and change task relationships. > - A blocked-by chip linked directly to the blocking task. > - Its remove control appeared only on hover, so touch users could not reach it. > - This pull request opens a small action menu when a user taps the chip on mobile. > - The menu lets the user visit the task or start the existing blocker-removal confirmation. > - The benefit is that touch users can manage blockers without changing the fast desktop flow. ## Linked Issues or Issue Description **What happened?** On a phone-width layout, a tap on a blocked-by chip opened the blocking task immediately. The remove control appeared only on hover, so a touch user could not remove the blocker. **Expected behavior** A tap on a blocked-by chip on mobile opens a menu. The menu offers `Visit task` and `Remove blocker` actions. **Steps to reproduce** 1. Open a task that has a blocker. 2. Use a viewport below the mobile breakpoint. 3. Open the task properties. 4. Tap the blocked-by chip. **Paperclip version or commit** Reproduced on `e5a7fd7038` from `master`. **Deployment mode** Built from source with the local development workflow. ## What Changed - Added a mobile-only action menu to blocked-by chips. - Kept the direct task link and hover/focus remove control on desktop. - Reused the existing removal confirmation before the relation update. - Added focused regression coverage for the mobile visit and remove choices. - Added a phone-width Storybook state with the action menu open. ## Verification - `pnpm --filter @paperclipai/ui exec vitest run src/components/IssueProperties.test.tsx` - `pnpm --filter @paperclipai/ui typecheck` - `pnpm check:token-gates` - `pnpm build-storybook` - Opened the new Storybook state in Playwright Chromium with a Pixel 5 viewport. Confirmed that both actions are visible and fit in the viewport. ## Risks - Low risk. The behavior change is limited to the existing mobile breakpoint. - Desktop navigation and blocker removal keep their current behavior. - The menu uses the shared dropdown and dialog primitives. > 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.6-sol`, xhigh reasoning. The Codex CLI managed the context window for this run. The model used repository tools, code execution, tests, and browser automation. ## 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> |
||
|
|
e5a7fd7038 |
Add sandbox device-login for the Codex adapter (#11237)
## Thinking Path > - Paperclip helps people manage AI agents for work. > - Agent adapters connect Paperclip to tools such as the Codex command line tool. > - A sandboxed Codex agent may start without a credential. > - The operator needs a safe sign-in flow that does not expose credentials to the shared package or the sandbox. > - This pull request adds a company-scoped device-login flow with a temporary Daytona sandbox. > - The flow promotes the credential only after readiness checks pass and removes the temporary sandbox after use. > - The result lets an operator sign in to a sandboxed Codex agent from the agent form. ## Linked Issues or Issue Description **Subsystem affected** Cross-cutting (multiple of the above) **Problem or motivation** A Codex adapter that runs in a sandbox cannot authenticate when the company has no pre-provisioned Codex credential. **Proposed solution** Add a company-scoped device-login session. Start a temporary sandbox, run `codex login --device-auth`, stream the code and URL, verify readiness, promote the credential, and delete the sandbox. **Alternatives considered** Pre-provisioning a credential does not support first-time sandbox login. Keeping the credential in the login sandbox does not provide a durable company credential. **Roadmap alignment** This supports the roadmap item for cloud and sandbox agents. **Additional context** The flow uses a five-minute cleanup reaper, compare-and-set status changes, and a PostgreSQL advisory lock to protect promotion and cleanup. ## What Changed - Add the adapter login-session contract, database table, and migration. - Add company-scoped server routes and a service for sandbox device login. - Add credential promotion, readiness checks, and cleanup after login. - Add restart-safe cleanup for abandoned login sandboxes. - Add sandbox login controls to the agent creation and edit forms. - Keep device-login and vendor identifiers out of public shared and adapter UI symbols. ## Verification - `pnpm --filter @paperclipai/adapter-codex-local exec vitest run` passed with 310 tests at the submitted commit. - The server login route, service, and reaper tests passed with 45 tests at the submitted commit. - The agent form render tests passed with 26 tests at the submitted commit. - The public-symbol leak check passed at the submitted commit. - A live Daytona sign-in flow still requires confirmation by a user with a live sandbox. ## Risks The migration adds a new company-scoped table. A promotion or cleanup race could remove a credential or leave a sandbox active, so the service uses claims, compare-and-set transitions, and an advisory lock. The live Daytona flow needs operator confirmation because local tests do not provide a real browser sign-in. ## Model Used OpenAI Codex, GPT-5, tool use and code execution, extended reasoning. ## 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> |
||
|
|
9c941169a6 |
fix(ui): keep new task dialog visible above mobile keyboard (#11281)
<!-- Write all pull request text in Simplified Technical English (ASD-STE100): short sentences, one instruction per sentence, simple approved vocabulary, and the active voice. --> ## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work. > - Operators create tasks in a dialog that includes the assignee and project fields. > - Mobile browsers reduce and offset the visual viewport when the on-screen keyboard opens. > - The dialog used layout viewport units, so its upper fields could move off-screen while the user typed. > - This pull request makes the dialog follow the live visual viewport and keeps the focused editor visible. > - The benefit is that operators can see the task context and the field they edit on mobile devices. ## Linked Issues or Issue Description **What happened?** On mobile browsers, opening the keyboard in the new-task dialog could move the assignee and project fields above the visible screen. The active editor could also become difficult to see. **Expected behavior** The full dialog must stay inside the visible browser area. The active editor and task controls must remain reachable while the on-screen keyboard is open. **Steps to reproduce** 1. Open Paperclip on a mobile browser. 2. Open the new-task dialog. 3. Focus the title or description editor to open the on-screen keyboard. 4. Observe that the upper fields can move outside the visible viewport. **Paperclip version or commit** Reproduced before commit `838cdbb325` on `master`. **Deployment mode** Local dev (`pnpm dev`) in a mobile browser viewport. ## What Changed - Read `window.visualViewport` while the dialog is open. - Apply token-based dialog geometry when the visual viewport is constrained. - Keep the focused editor visible after viewport resize and scroll events. - Add unit coverage for visual viewport updates and focus scrolling. - Add Playwright coverage for mobile, tablet, desktop keyboard, and unconstrained desktop layouts. ## Verification - `pnpm exec vitest run ui/src/components/NewIssueDialog.test.tsx` — 27 tests passed. - `pnpm --filter @paperclipai/ui typecheck` — passed. - `pnpm check:token-gates` — passed with all gates clean. - `pnpm --filter @paperclipai/ui build-storybook` — passed. - `pnpm exec playwright test tests/storybook-visual/new-issue-dialog-viewport.spec.ts --config tests/storybook-visual/playwright.config.ts` — 4 tests passed. ## Risks - Low risk. The custom geometry only activates when `visualViewport.height` is less than `window.innerHeight`. - Browsers without the Visual Viewport API keep the existing dialog primitive behavior. - The browser test checks hit targets and visible bounds at mobile, tablet, and desktop widths. > 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. The session used reasoning, repository tools, shell execution, and browser automation. The service did not expose the 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> |
||
|
|
f16071a290 |
fix(ui): use a fictional tailnet hostname in the vite proxy test (#11248)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - The UI dev server proxies `/api` to the backend and injects `x-forwarded-host`; a unit test asserts that injection with a sample Host header > - The sample Host header is a contributor's real machine and tailnet hostname, committed to the public repository > - Real personal hostnames do not belong in a public codebase, and this one also contains the contributor's OS username, so `scripts/check-forbidden-tokens.mjs` (which forbids the local username) blocks `npm` publishing from that contributor's machine > - This pull request replaces the fixture with a fictional tailnet-style hostname > - The benefit is no personal identifiers in the test fixtures and a passing forbidden-token check for every contributor ## Linked Issues or Issue Description No existing issue. Description follows the bug template: **What happened?** `ui/src/lib/vite-api-proxy.test.ts` uses a real contributor dev-machine hostname as its `Host` header fixture. `node scripts/check-forbidden-tokens.mjs` fails on that contributor's machine because the hostname contains their OS username, blocking the publish flow. Introduced in #10718. **Expected behavior** Test fixtures use fictional hostnames. The forbidden-token check passes on every contributor machine. **Steps to reproduce** 1. On a machine whose OS username appears in the fixture hostname, run `node scripts/check-forbidden-tokens.mjs`. 2. The check reports the two lines in `ui/src/lib/vite-api-proxy.test.ts` and blocks with exit code 1. ## What Changed - `ui/src/lib/vite-api-proxy.test.ts`: the `Host` fixture is now `dev-box.tail1234.ts.net:3101` (fictional). The test only asserts that whatever host arrives is injected as `x-forwarded-host`, so the value is arbitrary. ## Verification - `pnpm vitest run src/lib/vite-api-proxy.test.ts` in `ui/` — 5 tests pass. - `node scripts/check-forbidden-tokens.mjs` — "No forbidden tokens found" on the previously affected machine. - Note: git history retains the old value; this removes it from the current tree only. ## Risks None. A test fixture string with no behavioral coupling. ## Model Used - Claude (Anthropic), Claude Fable 5 (`claude-fable-5`), via Claude Code CLI with tool use. ## 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 |
||
|
|
d90f4d488e |
fix(ui): survive first load against a cold backend without a blank page (#11246)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - The web UI holds live websocket connections for run events, coordinates cross-tab polling through a leader-election store, and renders app chrome (sidebar, providers) around a routed outlet > - When the backend is still cold-starting (managed hosting wake, server restart, reverse proxy up before the app), the event websockets refuse connections and the first SPA load mounts against a dead backend > - In that state the mount cascade can exceed React's nested update limit (minified error #185); the crash originates in shell hooks outside the routed error boundary, so React unmounts the entire root to a blank page, and the dead page keeps retrying the websocket on a flat 1.5s timer until the user hard-refreshes > - This pull request removes the wasted nested commits from the shared-polling subscription path, adds exponential backoff to the transcript websocket reconnect, and adds a last-resort app-shell error boundary > - The benefit is that a cold or briefly unreachable backend degrades to a recoverable state instead of a blank page that hammers the server ## Linked Issues or Issue Description No existing issue. Description follows the bug template: **What happened?** On the first load against a backend that was still starting, the app showed its loading animation and then a blank page. The console showed repeated `WebSocket connection to 'wss://…/api/companies/<id>/events/ws' failed` lines and `Uncaught Error: Minified React error #185` with a stack through the shared-polling coordinator's `subscribe`. The websocket retries continued indefinitely on the dead page. A manual refresh fixed it. **Expected behavior** A backend that is briefly unreachable degrades gracefully: websocket reconnects back off, the UI keeps rendering from cache, and even a worst-case crash shows a reload prompt instead of a blank page. **Steps to reproduce** 1. Serve the UI while the backend API is still starting (websocket upgrades and API calls refused). 2. Load any company page with several shared-polling consumers mounted (dashboard with sidebar). 3. Observe repeated websocket failures; on affected loads the page goes blank with React error #185. ## What Changed - `ui/src/hooks/useSharedPolling.ts`: coordinator snapshot notifications now keep the previous state object when leadership did not change, so React bails out instead of scheduling a nested re-render. `subscribe` invokes its listener synchronously from inside the mount effect with a fresh object each time; before this change every mount and notify burned nested-update budget even with no value change — the crash frame in the field report was exactly this `subscribe → setState` call. - `ui/src/components/transcript/useLiveRunTranscripts.ts`: the live event websocket reconnect backs off exponentially (1.5s → 15s cap, reset on successful open), mirroring `LiveUpdatesProvider`, instead of a flat 1.5s retry. - `ui/src/components/AppErrorBoundary.tsx` (+ wiring in `ui/src/main.tsx`): a dependency-free boundary above the router and providers. `RouteErrorBoundary` only guards the routed `<Outlet />`; a crash in the shell around it had no boundary, so React unmounted the root to a blank page. The boundary renders a reload prompt with the error message. - Tests: `useSharedPollingSnapshot.test.tsx` (mount costs no extra commit — fails against the previous code; a real leadership change re-renders exactly once and ticks stay quiet), a backoff test in `useLiveRunTranscripts.test.tsx` (delays grow 1.5s → 3s → 6s and reset after a successful open), and `AppErrorBoundary.test.tsx` (render throw, effect throw, healthy pass-through). ## Verification - `pnpm vitest run` in `ui/` over the touched suites (shared polling, cross-tab poll, transcripts, boundary): 34 tests pass. - `pnpm typecheck` in `ui/` — clean. - The snapshot regression test was verified to fail against the pre-change hook (extra commit per mount). - Not reproduced end-to-end: the exact 50-update cascade from the field crash needs a live cold backend; the change removes the identified per-mount/per-notify nested commits at the reported crash frame, bounds the reconnect load, and guarantees the shell can no longer blank the page. ## Risks Low risk. The snapshot change only suppresses re-renders whose state is value-identical; leadership changes propagate exactly as before. The backoff only lengthens retry delays after consecutive failures and resets on success. The new boundary renders children untouched unless an error reaches it; behavior on healthy loads is unchanged. Self-hosted deployments see the same code paths — the cold-backend window simply rarely occurs there. ## Model Used - Claude (Anthropic), Claude Fable 5 (`claude-fable-5`), via Claude Code CLI with extended thinking and tool use (code search, edit, test execution; diagnosis included mapping the production minified stack to source). ## 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 |
||
|
|
9adeb4a9d0 |
fix(ui): fetch the web app manifest with credentials (#11245)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - The web UI is a PWA-capable SPA; `ui/index.html` links `/site.webmanifest` so browsers can read app metadata > - Browsers fetch `<link rel="manifest">` in "omit credentials" mode unless the link opts in with `crossorigin="use-credentials"` > - Self-hosted this is harmless, but when Paperclip runs behind an authenticating reverse proxy (a managed hosting front door), the cookie-less manifest request is rejected with 401 on every page load and logs a console error pair on each navigation > - This pull request adds `crossorigin="use-credentials"` to the manifest link so the request carries the same session cookies as every other same-origin asset request > - The benefit is a clean console and a servable manifest in proxied deployments, with self-hosted behavior unchanged ## Linked Issues or Issue Description No existing issue. Description follows the bug template: **What happened?** On every page load behind an authenticating reverse proxy, the browser logs `Failed to load resource: the server responded with a status of 401` for `/site.webmanifest`, plus `Manifest fetch from … failed, code 401`. The proxy rejects the request because the browser sends the manifest fetch without cookies. **Expected behavior** The manifest request carries the same session credentials as every other same-origin asset request, so the proxy can authenticate and serve it. No console errors. **Steps to reproduce** 1. Serve Paperclip behind a reverse proxy that requires a session cookie for all app routes. 2. Sign in and load any page. 3. Open the browser console: the manifest fetch fails with 401 while all other assets load. ## What Changed - `ui/index.html`: the manifest link now carries `crossorigin="use-credentials"`. - `ui/src/lib/pwa-install-mode.test.ts`: a regression test asserts the attribute stays on the link. ## Verification - `pnpm vitest run src/lib/pwa-install-mode.test.ts` in `ui/` — 2 tests pass. - Manual check of the rendered link tag in `ui/index.html`. ## Risks Low risk. The manifest is same-origin, so `use-credentials` only switches the fetch from "omit" to the include behavior all other same-origin requests already have. Self-hosted deployments see no change. Cross-origin manifest hosting is not used in this project. ## Model Used - Claude (Anthropic), Claude Fable 5 (`claude-fable-5`), via Claude Code CLI with extended thinking and tool use (code search, edit, test execution). ## 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 |
||
|
|
d5bb396518 |
fix: pass sandbox provider credential env vars to plugin workers; hide Local default under managed-sandbox-only (#11244)
<!-- Write all pull request text in Simplified Technical English (ASD-STE100): short sentences, one instruction per sentence, simple approved vocabulary, and the active voice. --> ## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - Environments give each agent run an execution target, and sandbox providers (Daytona, E2B, Novita, exe.dev) run as plugin workers > - A managed deployment provisions one platform-managed sandbox row with no credential in config; the provider is documented to fall back to its process env var (for example `DAYTONA_API_KEY`) > - Plugin workers spawn with a scrubbed environment, so that fallback never sees the host env var — probe and lease acquisition fail with "require an API key in config or DAYTONA_API_KEY" even when the deployment sets the var > - Separately, the managed-sandbox-only mode hides local rows from every list, but the instance Default picker renders a hardcoded synthetic "Local" option that no filter touches > - This pull request forwards each bundled provider's documented credential env var to its own plugin worker, and gates the synthetic Local option on the flag > - The benefit is that the documented host-env credential fallback works for plugin-backed providers, and managed-sandbox-only instances no longer offer Local anywhere ## Linked Issues or Issue Description **Subsystem affected** Plugin worker environment construction (`server/src/services/plugin-loader.ts`) and the environments UI (instance Default picker, agent form inherited-environment label). **Problem or motivation** Two follow-ups to the managed-sandbox-only mode (#11200), both found on a live managed deployment: 1. The deployment sets `DAYTONA_API_KEY` as a server env var and the managed sandbox row omits `config.apiKey` by contract. "Test Connection" fails with `Sandbox environment probe failed for provider "daytona". Daytona sandbox environments require an API key in config or DAYTONA_API_KEY.` A real agent run fails the same way at lease acquisition. The cause: sandbox providers run as plugin workers, and `buildPluginWorkerEnv` passes only model-provider keys and in-cluster Kubernetes vars. The provider's own documented credential env var never reaches the worker, so the in-plugin `process.env` fallback reads nothing. The self-hosted path has the same gap: the Daytona plugin README documents `DAYTONA_API_KEY` as a host-level fallback, and it does not work today. 2. With `enableManagedSandboxOnly` on, the instance Default environment picker still shows "Local". The server filters local *rows* out of the list, and the client filter mirrors that for cached lists, but this option is a hardcoded `<option value="">Local</option>` — not a list row — so no filter removes it. Selecting it writes a null default, which run selection then rejects fail-closed. **Proposed solution** Forward each bundled sandbox provider's documented credential env var into its plugin worker, keyed by the manifest's declared `environmentDrivers[].driverKey` so a worker only receives its own provider's credential (daytona → `DAYTONA_API_KEY`, e2b → `E2B_API_KEY`, exe-dev → `EXE_API_KEY`, novita → `NOVITA_API_KEY`). Keep the existing gate: only plugins that declare `environment.drivers.register` receive any passthrough. In the UI, render the synthetic Local option only when managed-sandbox-only is off; under the flag show a disabled "Select environment" placeholder only while no default is stamped yet, and stop the agent form's inherited label from reading "Local". **Alternatives considered** Adding `DAYTONA_API_KEY` to the existing `ADAPTER_ENV_PASSTHROUGH` list was rejected: that list goes to every environment-driver plugin, so each provider would receive every other provider's credential. A manifest schema field for declared credential env vars was rejected as heavier than needed: the bundled providers are known, and the mapping lives next to the two existing passthrough lists. ## What Changed - `server/src/services/plugin-loader.ts`: new `SANDBOX_PROVIDER_CREDENTIAL_ENV_PASSTHROUGH` map (driverKey → documented credential env vars). `buildPluginWorkerEnv` reads the manifest's `environmentDrivers` and forwards only the matching vars, after the existing `environment.drivers.register` gate. Blank values stay excluded. - `server/src/__tests__/plugin-database.test.ts`: the daytona worker receives `DAYTONA_API_KEY` and not another provider's key; a plugin whose drivers have no mapping (kubernetes) receives no credential var. - `ui/src/pages/CompanyEnvironments.tsx`: the Default picker's synthetic Local option renders only when managed-sandbox-only is off. Under the flag, a disabled "Select environment" placeholder renders only while the default is unset. - `ui/src/pages/CompanyEnvironments.test.tsx`: the Local option is present by default and absent under the flag; saved non-local environments stay selectable. - `ui/src/components/AgentConfigForm.tsx`: the inherited-environment label falls back to "Managed sandbox" instead of "Local" under the flag. ## Verification - `server`: `npx vitest run src/__tests__/plugin-database.test.ts -t buildPluginWorkerEnv` — 5 passed (3 existing, 2 new). - `ui`: `npx vitest run src/pages/CompanyEnvironments.test.tsx` — 22 passed (2 new); `npx vitest run src/components/AgentConfigForm.render.test.tsx` — 10 passed. - `tsc --noEmit` clean in `server` and `ui`. - Live managed deployment: confirmed the tenant service env carries `DAYTONA_API_KEY` while the probe fails with the exact message above, which pins the root cause to the worker env, not delivery. ## Risks - The worker env grows by exactly one var per matching bundled provider, only when the deployment sets it and only for plugins that declare a matching environment driver. Plugins without a mapping see no change. - Self-hosted behavioral shift is the fix itself: a host-level `DAYTONA_API_KEY` (or E2B/EXE/NOVITA equivalent) now reaches the provider as its README documents. Deployments that set the var but expected it to stay inert had no working configuration to preserve — the provider errored on every keyless probe and run. - UI change is inert unless `enableManagedSandboxOnly` is on (default false everywhere). ## Model Used Claude Fable 5 (`claude-fable-5`) via Claude Code — extended thinking, tool use, parallel read-only subagents for the two root-cause traces. ## 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 |
||
|
|
3b74ff4813 |
fix(ui): use issuePrefix instead of name-derived prefix in create dialog badges (#8550)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents at work > - Paperclip stores an `issuePrefix` on each company (e.g. "OPS") used for issue identifiers (`OPS-1`) and company-prefixed routes (`/OPS/dashboard`) > - The create-dialog badges in NewIssueDialog, NewProjectDialog, and NewGoalDialog were derived from the company display name using `company.name.slice(0, 3).toUpperCase()` — so "Acme Labs" showed "ACM" > - This is misleading because the badge visually represents the issue prefix, but actually shows an unrelated 3-letter slice of the display name > - When a company has `issuePrefix = "OPS"` but `name = "Acme Labs"`, the badge showed "ACM" while issues use "OPS-1" > - This pull request replaces `name.slice(0, 3).toUpperCase()` with `company.issuePrefix` in all three dialog badge components > - The benefit is that the badge now matches the actual prefix used for issues and routes, eliminating confusion ## Linked Issues or Issue Description Fixes: #8501 ## What Changed - `ui/src/components/NewIssueDialog.tsx` (line ~1339): Replaced `company.name.slice(0, 3).toUpperCase()` with `company.issuePrefix` in the selected-company header badge - `ui/src/components/NewIssueDialog.tsx`: Replaced `company.name.slice(0, 3).toUpperCase()` with `company.issuePrefix` in the company picker list badge - `ui/src/components/NewProjectDialog.tsx`: Replaced `selectedCompany.name.slice(0, 3).toUpperCase()` with `selectedCompany.issuePrefix` in the selected-company header badge - `ui/src/components/NewGoalDialog.tsx`: Replaced `selectedCompany.name.slice(0, 3).toUpperCase()` with `selectedCompany.issuePrefix` in the selected-company header badge ## Verification 1. Create or configure a company whose `issuePrefix` differs from the first 3 letters of its display name (e.g. name = "Acme Labs", issuePrefix = "OPS") 2. Open the New Task dialog — the selected-company header badge should show "OPS", not "ACM" 3. Open the company picker dropdown inside the New Task dialog — each company list badge should show the actual `issuePrefix` 4. Open the New Project dialog — the selected-company header badge should show "OPS" 5. Open the New Goal dialog — the selected-company header badge should show "OPS" 6. Verify that companies whose prefix matches the first 3 letters (e.g. name="Ops Team", prefix="OPS") still display correctly **Before/After:** - Before: Company "Acme Labs" with `issuePrefix = "OPS"` showed badge "ACM" - After: Same company shows badge "OPS" (Screenshots require running the UI locally against a test instance with the relevant company configuration.) ## Risks Low risk — this is a purely visual change to 3 React component badge labels. No API changes, no schema changes, no behavioral changes to issue creation or routing. The `issuePrefix` field is already loaded on the company objects used by these components. ## Model Used - **Provider:** OpenCode - **Model:** MiMo v2.5 Free - **Reasoning:** N/A (standard code generation) ## 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 searched GitHub for duplicate or related PRs and found none targeting the same badge code - [x] I have linked the existing issue with Fixes: #8501 - [x] I have not referenced internal/instance-local Paperclip issues or links - [x] My branch name describes the change (`fix/issue-8501`) - [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: Claude Sonnet 4.6 <noreply@anthropic.com> |
||
|
|
0a95ada1be |
feat(server): chunked import preview endpoint and resumable upload in the Import page and CLI (#11224)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - The previous pull request added server-side chunked resumable import transfers; without clients, large imports still ride the single fragile upload > - The Import page and the CLI need to slice large packages, upload parts with retry and progress, resume after interruptions, and preview before applying > - Preview is the missing server piece: the browser flow is preview-then-import, so a completed spool must be previewable without re-uploading > - This pull request adds the transfer preview endpoint, switches the Import page to the chunked path for zips over 48 MB, and teaches the CLI the same for oversized local imports > - The benefit is that large imports get progress, per-part retry, and resume in both clients, while small imports keep the exact single-shot path they have today ## Linked Issues or Issue Description **What happened?** With only the server transfer routes in place, users still upload large company packages as one request from the Import page and the CLI: no progress indication, no retry below the whole file, and no resume after a dropped connection or refresh. The preview-then-import flow also cannot run against an uploaded transfer, forcing a second full upload. **Expected behavior** A large package uploads once as verified parts with visible progress; preview and import both run against the uploaded spool; an interrupted upload resumes with only the missing parts re-sent; packages at or below 48 MB behave exactly as before. **Steps to reproduce** 1. Select a 500 MB zip on the Import page over an unreliable connection. 2. Watch the single upload fail near the end and restart from zero, twice — once for preview, once for import. 3. Same story headless via the CLI. ## What Changed - Server: `POST /import/transfers/:id/preview` runs the existing preview logic against the completed spool (shared assembly + whole-file verification helper with apply); preview neither completes the run nor deletes the spool, so the subsequent apply reuses it. Missing parts respond with the missing list. - UI: zips over 48 MB take the chunked path in both preview and import — the file is sliced into 32 MB parts hashed with WebCrypto (single ArrayBuffer, no second copy), the transfer is created or resumed (the create response's missing-parts list drives what uploads), parts upload sequentially with three attempts each and visible progress, then transfer preview/apply replace the multipart calls. The existing preview pane, collision handling, adapter overrides, and async job polling are unchanged; ≤ 48 MB keeps the single-shot path. - CLI: oversized local `.zip` or folder imports zip/slice/hash with node crypto, upload with resume and per-part retry and progress lines, and use transfer preview/apply. Small packages keep the inline path byte-identical. - Failure honesty: adapters/API errors fail open to existing behavior; a part failing all attempts surfaces a durable error panel with resume intact. ## Verification - Server: preview-then-apply on one spool (run stays open, spool intact, then apply completes), preview with missing parts rejected — added to the transfer route suite (embedded Postgres). - UI suite: large file takes the chunked path (manifest shape, part uploads, progress, apply on a resumed transfer, single-shot endpoints never called), small file stays single-shot, part failure after three attempts surfaces the error panel without running preview, resume re-uploads only the missing part. - CLI: manifest slicing/hashing, threshold behavior for zip and folder sources, folder-zip round-trip through the real zip reader, upload resume/retry/exhaustion/already-completed, full-command chunked and small-zip inline flows. - Server, ui, cli typechecks clean. Exact counts in the PR checks. ## Risks - The 48 MB threshold only routes between two verified paths; behavior below it is untouched. - Chunked CLI imports use the board-scoped transfer routes, so oversized CLI imports need board credentials (agent tokens keep the agent-safe small-file path). No privilege change — board actors already had the generic routes — but the two size regimes differ semantically; called out for review. - A CLI dry-run over the threshold uploads parts before previewing; the spool persists (24 h sweep) and a later apply resumes without re-upload — inherent to preview-against-spool. Stacked on #11223 — merge that first; this PR then shows only the preview endpoint and client changes. ## Model Used - Claude Fable 5 (`claude-fable-5`) via Claude Code CLI, with extended thinking and tool use (multi-agent implementation with independent verification). ## 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 |
||
|
|
2494a2a0fe |
perf: add repeatable issue-detail baseline rig (#10409)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - The issue detail page is a core operator surface where perceived latency directly affects task navigation > - Performance work needs repeatable evidence so later optimizations can be compared against the same scenarios > - The page did not expose stable user-timing marks for its header or first useful content > - There was also no isolated seeded browser rig that measured warm navigation, cold deep links, waterfalls, or server time > - This pull request adds the instrumentation and a one-command Playwright baseline harness > - The benefit is that issue-page performance changes can be validated with reproducible median measurements instead of anecdotes ## Linked Issues or Issue Description **Subsystem affected** Cross-cutting: `ui/`, `server/`, and browser performance tooling. **Problem or motivation** The issue detail page performs a large client bootstrap and request fan-out, but the repository lacks stable user-timing boundaries and a repeatable benchmark. That makes performance changes difficult to compare and allows regressions to be judged from anecdotes instead of consistent evidence. **Proposed solution** Add stable header/content paint measures, development/QA-only lifecycle vital reporting, aggregate server timing for the issue endpoint, and a seeded Playwright command that runs warm/cold scenarios under throttled and unthrottled profiles with N≥5 median reporting. **Alternatives considered** Ad hoc DevTools recordings were rejected because they are not repeatable or reviewable. Production telemetry was rejected because this baseline should not change production data collection. A unit-only harness was rejected because it cannot capture browser bootstrap, rendering, and network waterfall costs. **Roadmap alignment** The roadmap calls for agent performance to be measurable over time. This change applies that evidence-first principle to a core operator page and does not duplicate a listed roadmap deliverable. **Additional context** The generated report includes warm and cold medians, TTFB/FCP/LCP where applicable, request and byte totals before first useful content, JavaScript bytes, and issue endpoint server timing. ## What Changed - Added `issue-detail:navigate→header-paint` and `issue-detail:navigate→content-paint` user-timing measures to the issue detail page. - Added development/QA-only TTFB, LCP, and INP console reporting without production telemetry delivery. - Added `Server-Timing` for `GET /api/issues/:id`. - Added `pnpm exec playwright test --config tests/perf/issue-detail/playwright.config.ts`, which seeds an isolated instance and runs N≥5 warm/cold samples under unthrottled and Fast 4G/4x CPU profiles. - Added Markdown, raw JSON, and Chrome-trace outputs with median baseline tables and waterfall data. ## Verification - `pnpm --filter @paperclipai/ui typecheck` - `pnpm --filter @paperclipai/server typecheck` - `pnpm check:token-gates` - `npx playwright test --config tests/perf/issue-detail/playwright.config.ts --list` - `pnpm exec playwright test --config tests/perf/issue-detail/playwright.config.ts` — passed 20 samples in 9.4 minutes (5 runs × 2 scenarios × 2 profiles) for the baseline; post-review integrity reruns also exercised the corrected paths, while this shared runner intermittently killed Chromium processes, so the rig now performs one bounded browser-crash retry per sample. - Baseline medians: warm unthrottled 278/447 ms header/content; cold unthrottled 646/646 ms; warm throttled 1240/2060 ms; cold throttled 3932/3933 ms. ## Risks - Low product risk: the new browser measurements are development/QA tooling and the UI timing work does not change visible layout. - `Server-Timing` exposes only aggregate handler duration, not query contents or private identifiers. - Native INP reporting uses supported browser event timing entries and silently no-ops where unsupported. > 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.4, tool-assisted coding and browser execution with reasoning enabled; context-window size is not exposed in this environment. ## 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: Dev Agent <dev@paperclip.ing> |
||
|
|
5bb2490b86 |
feat(ui): surface all issue documents and agent artifacts in chat-style sidebar (#11226)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - The task detail page uses a chat-style thread with a right sidebar; the sidebar has a Plan tab and an Artifacts tab (#11101 made this UI the default) > - The Plan tab only showed the one issue document named `plan`, and the Artifacts tab only listed formal work products; other agent-authored documents (for example a `synthesis` doc) and agent-attached files were invisible in the sidebar > - Users could see an agent mention a document in the thread but had no way to find that document in the sidebar, which breaks trust in the task view as the record of the work > - This pull request surfaces every non-system issue document in the Plan tab, composes the Artifacts tab from work products, documents, and agent-created attachments, and gives thread images a full-screen lightbox with download > - The benefit is that anything an agent produces on a task is now reachable from the sidebar, while user uploads stay with their comments in the thread ## Linked Issues or Issue Description Refs #11101 (chat-style task UI default — this PR extends its sidebar). **Subsystem affected** Task detail UI (chat-style thread sidebar): Plan tab, Artifacts tab, and thread attachment rendering in `ui/src`. **Current behavior** The Plan tab renders only the issue document literally named `plan`. The Artifacts tab renders only formal work products. Agent-authored documents with any other name, and files agents attach to comments, do not appear anywhere in the sidebar. Thread images open as bare links. **Proposed behavior** The Plan tab lists every non-system issue document, with the `plan` document first and the others rendered inline below it. The Artifacts tab composes three sources — work products, issue documents, and agent-created comment attachments — deduplicated against attachment-backed work products via `metadata.attachmentId`, and shows whenever any source is non-empty. Work-product rows without a resolvable attachment or document fall back to links found in their metadata so they stay clickable. Images in the thread open a shared full-screen lightbox with a download action. Files uploaded by users stay thread-only and are not mixed into the Artifacts tab. **Reason and benefit** Agents routinely produce documents that are not named `plan` and attach files to their comments. Users reading the thread must be able to find every one of those outputs from the sidebar. Redundant surfacing is acceptable; an unfindable document is not. **Breaking changes** None. This is additive rendering; no schema or API changes. ## What Changed - `IssuePropertiesPlansTab.tsx`: renders all non-system issue documents, `plan` primary, others inline below via `MarkdownBody` - `IssuePropertiesArtifactsTab.tsx`: composes work products + documents + agent-created attachments with dedupe; rows without an attachment/document target fall back to `metadata` links - `IssueProperties.tsx`: Artifacts tab visibility now derives from the composed source set - New `ui/src/lib/issue-artifacts.ts`: pure composition/dedupe logic, unit-tested - New `ui/src/components/task-chat/task-chat-attachments.ts`: splits agent vs user comment attachments, unit-tested - `TaskChatBubble.tsx`: thread images open the shared full-screen lightbox with download - `useIssueDocuments.ts`: hook now exposes the full issue-document list ## Verification - `pnpm typecheck` — passes across the workspace - `pnpm check:token-gates` — 3/3 CLEAN - `cd ui && pnpm vitest run src/lib/issue-artifacts.test.ts src/components/task-chat/task-chat-attachments.test.ts src/pages/IssueDetail.test.tsx` — 74 tests pass - Manual: open a task whose agent created a document not named `plan` (for example `synthesis`); confirm it appears in the Plan tab below the plan and in the Artifacts tab; confirm an image the agent attached appears under Artifacts; confirm a user-uploaded image stays only in the thread and opens full screen with a download button Snapshot baselines are intentionally not updated for this visual change, per the `doc/design/DECISION-SHEET.md` entry "Per-change snapshot verification demoted to dormant (Jul 13 2026)". ## Risks - Low risk: rendering-only change scoped to the task sidebar and thread bubbles; composition logic is pure and unit-tested - Dedupe relies on `metadata.attachmentId` linkage; a work product with malformed metadata would render as a duplicate row (cosmetic only) ## Model Used - Claude (Anthropic), model id `claude-fable-5`, extended thinking enabled, agentic tool use via Claude Agent SDK (Claude Code harness) ## 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> |
||
|
|
0044fa8904 |
Let tenants edit env vars on managed sandbox environments; add managed-sandbox-only mode (#11200)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - Environments give each agent run an execution target: the local host, SSH, or a sandbox provider > - A managed deployment can provision one platform-managed sandbox environment through the `PAPERCLIP_MANAGED_CONFIG` `environments` section > - That row is fully locked today. A tenant cannot add environment variables for their agents. There is also no way to hide local execution — run selection falls back to the local row > - A platform that manages the sandbox for its tenants needs both: the tenant adds env vars (and nothing else), and local execution is neither visible nor reachable > - This pull request opens exactly one tenant edit (env vars) on the managed sandbox row, and adds an `enableManagedSandboxOnly` mode that hides local and makes run selection fail closed > - The benefit is a complete managed-sandbox experience with no change for self-hosted instances ## Linked Issues or Issue Description **Subsystem affected** Environments (managed sandbox provisioning, environment routes, run environment selection) and the environments UI. **Problem or motivation** Platform-provisioned sandbox environments (`metadata.managedByPaperclip`) reject every write on cloud-managed instances. Agents often need environment variables inside their sandbox. The tenant has no way to set them on the managed row. Separately, an operator cannot remove local execution: the environment list always shows the local row, and run selection falls back to it when no default is set. **Proposed solution** Allow an envVars-only PATCH on the managed sandbox row, and echo those env vars back for editing. Add a managed-tier feature (`enableManagedSandboxOnly`) that hides the local environment from all read surfaces and redirects local-landing run selection to the managed sandbox environment, failing closed when it is unavailable. **Alternatives considered** UI-only hiding of the local row. This was rejected: it does not stop a run from resolving to local, so it is presentation without enforcement. Full unlock of the managed row was also rejected: name, driver, and config stay platform-owned so boot reconciliation cannot fight tenant edits. ## What Changed - `server/src/routes/environments.ts`: the platform-provisioned write floor admits an envVars-only PATCH on the generalized managed sandbox row (sandbox driver, `managedByPaperclip`, not legacy kubernetes-marker rows). Name, driver, config, status, metadata, and DELETE stay rejected. The read floor stops blanking env vars on that row; credential-shaped config keys stay redacted for every actor. Legacy kubernetes-marker rows keep the full floor. - Same file: under `enableManagedSandboxOnly`, the environments list and the by-id read omit the local row for every actor, including instance admins. - `server/src/services/execution-workspace-policy.ts`: `resolveExecutionWorkspaceEnvironmentId` gains the managed-sandbox-only inputs. A selection that lands on the local environment is redirected to the managed sandbox environment. With no active managed row it throws `ManagedSandboxUnavailableError` — never local. Non-local selections (ssh, user-created sandboxes) are untouched. - `server/src/services/heartbeat.ts`: the run path reads the flag, looks up the managed row (`findManagedSandboxEnvironment`, new read-only finder in `environments.ts`), and passes both to the resolver. Mirrors the forced-kubernetes precedent, which keeps precedence when both regimes are on. - `server/src/services/managed-environments.ts`: after a successful reconcile, the instance default environment moves to the managed sandbox row when the current default is unset, local, or dangling. A tenant-chosen custom environment is never overridden. - `packages/shared`: new `enableManagedSandboxOnly` key (schema default false, catalog tier `managed`, cloudDefault false, selfHostedDefault false) and the matching interface field. - UI: managed rows show a "Managed by Paperclip" lock badge; editing one opens a dedicated env-vars-only editor that sends the one PATCH shape the server admits (the old full form failed with a 403 on save). New `ui/src/lib/managed-sandbox-environment.ts` mirrors the local filter for cached lists (applied in the project picker; the agent picker already excluded local). The experimental settings page gains the toggle at its alphabetical card position. `environmentsApi.update` now declares the `envVars` field it already sent. - Tests: environment route floor coverage (envVars-only accepted, mixed bodies rejected, legacy rows still blanked and locked, local hidden and 404 under the flag, self-hosted unchanged), an embedded-postgres service test pinning that boot reconciliation never touches tenant env vars, resolver redirect/fail-closed cases, managed-environments default-stamping cases, and UI lib/settings tests. ## Verification - `pnpm --filter @paperclipai/server exec vitest run src/__tests__/environment-routes.test.ts src/__tests__/environment-service.test.ts src/__tests__/execution-workspace-policy.test.ts src/services/managed-environments.test.ts` — all pass. - `pnpm --filter @paperclipai/shared exec vitest run` — 425 pass (catalog/schema default parity is pinned by an existing test). - `pnpm --filter @paperclipai/ui exec tsc --noEmit` and the affected UI suites (CompanyEnvironments, InstanceExperimentalSettings incl. card-order test, new lib test) — all pass. - Full workspace `pnpm test`: 3,414 passed. 17 files report failures on this machine; the identical 17 fail on a clean `origin/master` worktree in the same environment (git-worktree/skills/embedded-postgres environment dependencies and plugin-SDK zero-test collections). One additional file (`issue-monitor-scheduler.test.ts`) failed one timing-sensitive test in one of two full-suite runs and passes 7/7 in isolation on this branch — a flake in a domain this diff does not touch. The branch introduces no new failures. - Self-hosted zero-delta: every new behavior is gated on the cloud-managed instance check or the new flag, which defaults to false in schema and catalog; pinned by the "does not floor platform-marked rows on self-hosted instances" and flag-off tests. ## Risks - Behavior is opt-in twice over: the write-floor exception applies only to rows the managed-config provisioner stamps, and the hiding/forcing applies only when `enableManagedSandboxOnly` is on (default false everywhere). Self-hosted instances see no change. - The env-vars echo is scoped to the generalized managed sandbox row; legacy kubernetes-marker rows keep the blanket floor because pre-generalization builds may have written platform values there. - Fail-closed run selection means a managed instance with the flag on and an archived managed row (provider plugin down) refuses runs with a precise error instead of running locally. That is the intended posture. ## Model Used Claude Fable 5 (`claude-fable-5`), extended thinking, agentic tool use via Claude Code CLI. ## 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 |
||
|
|
145d86911b |
Remove decision training UI (#11225)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work. > - The Decisions desk shows work that needs an operator response. > - It also exposed decision-training actions and a separate training library. > - Paperclip does not plan to use these training surfaces now. > - Keeping inactive controls makes the Decisions workflow harder to scan. > - This pull request removes the training UI and keeps the backend snapshot contract unchanged. > - The benefit is a smaller and clearer Decisions workflow without a data migration. ## Linked Issues or Issue Description **What existing behavior does this improve?** The Decisions desk currently exposes training controls, training state, and a separate training library route. **Subsystem affected** `ui/` — React and Vite board UI. **Current behavior** Operators can open a training library from the Decisions toolbar. They can also mark a decision for training from rows and inspect the result in a drawer. **Proposed behavior** Remove the training controls, badges, drawer, library pages, and routes from the Decisions UI. Keep the server APIs and stored training examples unchanged. **Reason and benefit** The product does not plan to use decision training now. Removing the unused surfaces reduces Decisions UI noise and avoids presenting a workflow that operators should not use. **Breaking changes** The `/decisions/training` UI routes are no longer registered. Existing server endpoints and stored decision-training data remain compatible. ## What Changed - Removed decision-training controls and state from Decisions toolbars, rows, queue pages, and shelves. - Removed the training drawer, library, inspector, API client, helpers, query keys, and routes. - Added route and row regressions that assert training UI does not return. - Updated the Decisions Storybook description to match the available controls. ## Verification - `pnpm exec vitest run ui/src/App.test.tsx ui/src/components/AttentionQueueRow.test.tsx` — 32 tests passed. - `pnpm check:token-gates` — all gates clean. - `pnpm -r typecheck` — passed. - `pnpm build` — passed. - `pnpm test:run` — server and UI partitions passed. The CLI partition had one environment-only failure because this agent runtime injects static AWS credentials. The exact CLI file passed all 8 tests when those credential variables were unset. - Searched `ui/src` and `ui/storybook` for the removed training routes, drawer, library, badges, and actions. Only negative regression assertions remain. ## Risks - Low implementation risk. This change deletes UI-only entry points and does not change the database or server APIs. - Saved training-page URLs no longer render a board route. This is the intended behavior. > 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, model `gpt-5.6-sol`, with `xhigh` reasoning. The runtime did not expose the context-window size. The agent used repository tools, shell execution, and automated tests. ## 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> |
||
|
|
45dfb183b6 |
fix(ui): add undo action to inbox archive toast (#11220)
## Thinking Path > - Paperclip helps operators supervise AI-agent work. > - The Mine inbox keeps tasks that need an operator's attention in one place. > - Operators can archive a task from its detail page after they finish triage. > - That action is easy to select accidentally and did not offer immediate recovery. > - This pull request adds Undo to the archive success toast and keeps inbox caches consistent. > - The benefit is fast recovery without searching for or reopening the task. ## Linked Issues or Issue Description **What happened?** Archiving a task from the Mine inbox removed it and showed a success toast with no recovery action. **Expected behavior** The success toast should offer Undo. Selecting Undo should restore the task through the existing unarchive API while preserving a consistent inbox view. **Steps to reproduce** 1. Open a task from the Mine inbox. 2. Select the archive action. 3. Observe that the task leaves the inbox and the success toast has no Undo action. **Paperclip version or commit** Reproduced on `master` before this change. **Deployment mode** Local dev, built from source. Related prior work: #9931 and #10668. ## What Changed - Add an Undo action to the successful inbox archive toast. - Optimistically restore the task in captured inbox query caches before the unarchive request completes. - Cancel in-flight inbox fetches and clear the local archive guard so stale responses cannot hide the restored task. - Reapply the archive guard and remove the cached task if the unarchive request fails. - Add regression tests for successful Undo, the in-flight cache race, and failed Undo rollback behavior. ## Verification - `pnpm exec vitest run ui/src/pages/IssueDetail.test.tsx ui/src/lib/inboxArchiveCache.test.ts` — 51 tests passed on the final head. - `pnpm check:token-gates` — passed. - `pnpm -r typecheck` — passed. - `pnpm build` — passed. - `pnpm test:run` — the general server and UI groups passed. One CLI doctor assertion detected injected host AWS credentials and passed all 8 tests with those unrelated variables unset. A task-watchdog scheduler test also passed all 18 tests in isolation after one full-suite timing failure. ## Risks Low risk. The change uses the existing unarchive endpoint and inbox cache helpers. Undo failure returns the task to its archived state, shows an error toast, and invalidates the inbox queries for server reconciliation. > 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, model ID GPT-5. The service manages the context window. Reasoning, tool use, and code execution were enabled. ## 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> |
||
|
|
7734f4b32d |
fix(ui): keep slash autocomplete scrollable in dialogs (#11222)
<!-- Write all pull request text in Simplified Technical English (ASD-STE100): short sentences, one instruction per sentence, simple approved vocabulary, and the active voice. --> ## Thinking Path > - Paperclip helps operators manage AI-agent companies. > - Operators create tasks and comments through shared rich-text editors. > - These editors show slash-command and mention matches in a floating menu. > - Modal dialogs treat that body-level menu as outside content and cancel its wheel and touch movement. > - This pull request keeps scroll events inside the floating menu and preserves native scrolling. > - The benefit is that operators can reach every match with a mouse wheel, a trackpad, or a touch screen. ## Linked Issues or Issue Description No public GitHub issue exists for this bug. **What happened?** Slash-command and mention menus could contain more matches than their visible height. When an editor was inside a modal dialog, the modal scroll lock canceled wheel and touch movement on the body-level menu portal. Operators could not scroll to later matches. **Expected behavior** The autocomplete menu must scroll with a mouse wheel, a two-finger trackpad gesture, and a vertical touch gesture. Keyboard selection and normal editor behavior must stay unchanged. **Steps to reproduce** 1. Open a task or comment editor inside a modal dialog. 2. Enter a slash command or mention query that has more matches than the menu can show. 3. Try to scroll the menu with a wheel, trackpad, or touch gesture. **Paperclip version or commit** `7ea2068ef8` on `master`. **Deployment mode** Local development UI built from source. ## What Changed - Keep wheel and touch movement inside the shared autocomplete menu portal. - Add vertical overscroll containment while preserving native momentum scrolling. - Add a regression test that mounts the real dialog and verifies that wheel and touch movement stay uncanceled. ## Verification - `pnpm --dir ui exec vitest run src/components/MarkdownEditor.test.tsx` - `pnpm check:token-gates` - `pnpm -r typecheck` - `env -u AWS_ACCESS_KEY_ID -u AWS_SECRET_ACCESS_KEY pnpm test:run` - `pnpm build` The two AWS variables are omitted from the full test command because this agent runtime injects static AWS credentials. One unrelated CLI doctor test correctly warns when those credentials are present. The CI environment does not inject them. ## Risks - Low risk. Event propagation stops only on the open autocomplete menu portal. - Ancestor listeners no longer receive wheel or touch movement from that menu. Native menu scrolling and option-level touch handling still receive the events. > 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 with model ID `gpt-5`. The deployment suffix and context-window size are not exposed to the agent. The model used agentic reasoning, repository tools, GitHub tools, and local code execution. ## 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> |
||
|
|
7ea2068ef8 |
fix(files): only highlight accessible workspace file links (#11090)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work. > - Task comments can contain references to files in project and execution workspaces. > - Paperclip detected path-shaped inline code and showed it as an actionable file chip. > - The UI did not first confirm that the current board session could open the file. > - Missing, denied, ambiguous, remote, and unsupported files therefore looked actionable and failed after a click. > - This pull request adds an issue-scoped availability check and promotes only confirmed files to chips. > - The benefit is that the task thread shows a file action only when that action can succeed. ## Linked Issues or Issue Description **What happened?** Task comments promoted path-shaped inline code to file chips before Paperclip checked the file. A chip could point to a missing, denied, ambiguous, remote, or non-previewable file. The action then failed after the user selected it. **Expected behavior** Paperclip must show a file chip only after the server confirms that the current board session can open the exact file reference. All other path-shaped text must stay ordinary inline code. **Steps to reproduce** 1. Add a task comment that contains inline code with a missing or inaccessible workspace path. 2. Open the task thread as a board user. 3. Observe that the path looks like an actionable file chip. 4. Select the chip and observe that the file cannot open. **Paperclip version or commit** `19be4cf927` and earlier. **Deployment mode** Local dev and self-hosted server. **Access context** Board user. ## What Changed - Added shared request, response, and validation contracts for batched workspace-file availability checks. - Added an issue-scoped server endpoint that resolves file references with company, issue, workspace, and preview-access checks. - Added bounded batch concurrency and tests for missing, denied, ambiguous, remote, unsupported, and available files. - Added an issue-scoped UI availability registry that deduplicates, batches, caches, and invalidates file checks. - Changed task-comment markdown rendering so only confirmed files get chip styling and file-viewer behavior. - Bound each chip to the exact workspace target that passed the availability check. ## Verification - `pnpm exec vitest run packages/shared/src/workspace-file-resource.test.ts server/src/__tests__/file-resources.test.ts ui/src/components/MarkdownBody.test.tsx ui/src/components/WorkspaceFileMarkdownBody.availability.test.tsx ui/src/lib/remark-workspace-file-refs.test.ts ui/src/lib/workspace-file-availability.test.ts` — 93 passed, 35 skipped. - `pnpm check:token-gates` — clean. - `pnpm -r typecheck` — passed. - `pnpm build` — passed. - `pnpm test:run` — all server and UI groups passed. One unchanged CLI test saw the run-injected static AWS credentials and expected only its local `AWS_PROFILE`. The same test passed, 8 of 8, after removing only `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` from its process environment. ## Risks - File chips now appear after an asynchronous availability check, so path-shaped text can briefly render as inline code. - Availability results use the existing 30-second file-resource cache window. File-resource invalidation forces a new check. - The endpoint limits each request to 100 references and the client chunks larger sets. > 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 with GPT-5. The service did not expose a more specific model ID or context-window size. The agent used high-reasoning mode, repository tools, command execution, and test execution. ## 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 - [ ] 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> |
||
|
|
815e49bb7c | feat: make chat-style tasks the default experience (#11101) | ||
|
|
c4abecb2c4 |
fix(skills): refresh project folders in place (#11066)
## Thinking Path > - Paperclip helps operators manage agent skills across a company. > - The installed skills view groups project-backed skills into folders. > - The view showed two folder creation controls and only offered a global project scan. > - Operators need one clear folder action and a refresh action for the selected project. > - This pull request keeps folder creation in the folder rail and adds a scoped project refresh. > - The benefit is a calmer skills view and faster, more precise project skill updates. ## Linked Issues or Issue Description No public GitHub issue exists for this focused UI bug. **What happened?** The installed skills view repeated the folder creation action in the toolbar. A selected project folder also had no way to refresh only its own project skills. **Expected behavior** The folder rail must own folder creation. A selected project-backed folder must offer a refresh action that scans only that project and refreshes the skill and folder queries. **Steps to reproduce** 1. Open the installed skills view for a company with project-backed skill folders. 2. Select a project folder. 3. Observe the duplicate folder action and the absence of a project-scoped refresh action. **Paperclip version or commit** Reproduced before this two-commit fix on `master`. **Deployment mode** Local development with `pnpm dev`. ## What Changed - Removed the duplicate toolbar folder creation button when the folder rail exists. - Preserved the toolbar folder action when no folder rail exists. - Added a refresh action beside the breadcrumb for a selected project-backed folder. - Passed the selected project ID to the project scan API. - Refreshed both the installed skill list and skill folder data after scans. - Added component tests for compact folder creation, the empty-folder fallback, and scoped project refresh. ## Verification - `pnpm exec vitest run ui/src/pages/CompanySkills.test.tsx` — 20 tests passed. - `pnpm check:token-gates` — passed with all three gates clean. - `pnpm -r typecheck` — passed. - `pnpm build` — passed. - `pnpm test:run` — the server and UI stages passed 7,475 tests. The CLI stage then found one environment-sensitive AWS doctor assertion because this agent runtime injects static AWS credentials. - `env -u AWS_ACCESS_KEY_ID -u AWS_SECRET_ACCESS_KEY pnpm exec vitest run cli/src/__tests__/secrets.test.ts --project paperclipai` — all 8 tests passed. - GitHub CI — all latest-head checks passed. ## Risks - Low risk. The scoped refresh depends on the existing `project:<id>` folder system key. - The global scan path is unchanged. - There are no schema, migration, API contract, dependency, workflow, or documentation 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, GPT-5 family. The runtime did not expose a more specific model ID or context-window size. The agent used high-reasoning mode, repository tools, GitHub tools, and code execution. ## 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> |