Bumps [i18next](https://github.com/i18next/i18next) from 26.3.1 to
26.3.6.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/i18next/i18next/releases">i18next's
releases</a>.</em></p>
<blockquote>
<h2>v26.3.6</h2>
<ul>
<li>fix: allow TypeScript 7 in the optional <code>typescript</code> peer
dependency range (<code>^5 || ^6 || ^7</code>). With
<code>typescript@7.0.2</code> in a project, <code>npm install</code>
failed with an <code>ERESOLVE</code> peer conflict. The published types
are TS7-compatible as-is: every <code>test/typescript</code> suite
produces identical results under 6.0 and 7.0.2. Reported in <a
href="https://redirect.github.com/i18next/react-i18next/issues/1927">react-i18next#1927</a>,
thanks <a
href="https://github.com/andikapradanaarif"><code>@andikapradanaarif</code></a>.</li>
</ul>
<h2>v26.3.5</h2>
<ul>
<li>fix: <code>$t()</code> nesting options blocks that span multiple
lines are now parsed. <code>nest()</code> decided where the nested key
ends by testing <code>match[1]</code> with <code>/{.*}/</code>, whose
dot does not cross line breaks — so a <code>$t(key, { ... })</code>
options object containing a newline was treated as having no options,
mis-split as formatters, and the nested lookup ran without its options
(placeholders stayed unresolved). The nesting regexp itself already
matches newlines inside <code>$t(...)</code>; adding the <code>s</code>
(dotAll) flag makes multiline options behave like the single-line form.
Thanks <a href="https://github.com/spokodev"><code>@spokodev</code></a>
(<a
href="https://redirect.github.com/i18next/i18next/pull/2440">#2440</a>).</li>
<li>fix: <code>getUsedParamsDetails</code> (the <code>returnDetails:
true</code> path) no longer mutates the passed <code>replace</code>
object. It wrote <code>count</code> straight onto
<code>options.replace</code> so the returned <code>usedParams</code>
would include it — a caller reusing one <code>replace</code> object
across <code>t()</code> calls then carried a stale <code>count</code>
into later interpolations (e.g. a previous call's <code>count: 5</code>
rendered instead of the current call's value). The details are now built
from a copy; <code>usedParams</code> still includes <code>count</code>.
Thanks <a href="https://github.com/spokodev"><code>@spokodev</code></a>
(<a
href="https://redirect.github.com/i18next/i18next/pull/2441">#2441</a>).</li>
<li>fix: with the default <code>skipOnVariables: true</code> +
<code>escapeValue: true</code>, a <code>{{placeholder}}</code> carried
inside an interpolated value now stays literal even when the value
contains escapable characters. The skip logic advanced the regex
<code>lastIndex</code> by the raw value length, but the escaped text
written into the string is longer, so <code>lastIndex</code> landed
inside the inserted value and a trailing <code>{{placeholder}}</code> in
it got interpolated — leaking another in-scope variable that should have
stayed literal (values without escapable characters were already skipped
correctly). The advance now uses the escaped length that is actually
written, and the regex-safe <code>$</code>-doubling is applied only at
the <code>String.replace</code> call so it can't distort the length
arithmetic. Thanks <a
href="https://github.com/spokodev"><code>@spokodev</code></a> (<a
href="https://redirect.github.com/i18next/i18next/pull/2442">#2442</a>).</li>
</ul>
<h2>v26.3.4</h2>
<ul>
<li>fix(security): <code>deepExtend</code> (used by
<code>addResourceBundle(..., deep, overwrite)</code>) no longer recurses
into inherited properties. It checked key existence with the
<code>in</code> operator, which walks the prototype chain, so a source
key matching an inherited built-in (e.g. <code>hasOwnProperty</code>,
<code>toString</code>) caused recursion into the shared
<code>Object.prototype</code> function and, with <code>overwrite:
true</code>, could overwrite e.g.
<code>Object.prototype.hasOwnProperty.call</code> with a non-callable
value — corrupting a shared built-in process-wide (DoS). Existence is
now checked with <code>Object.prototype.hasOwnProperty.call</code>, so
such keys are copied as plain own data instead. This complements the
existing <code>__proto__</code>/<code>constructor</code> guard and is
also strictly more correct for an own-property merge. Only affects
applications that pass attacker-controlled data with <code>deep:
true</code> and <code>overwrite: true</code>; no standard
backend/integration does this. Distinct from CVE-2026-48713 /
CVE-2026-48714 (different packages, <code>setPath</code> mechanism).
Thanks to zx (Jace) for the responsible disclosure.</li>
</ul>
<h2>v26.3.3</h2>
<ul>
<li>fix(types): selector <code>t($ => $.arr, { returnObjects: true,
context })</code> on a JSON array of <strong>heterogeneous</strong>
objects now preserves each element's full shape (e.g. <code>{ transKey1:
string; transKey2: string }[]</code>) instead of collapsing to a union
of partial element types. Two type-level causes: (1)
<code>FilterKeys</code> evaluated the whole array element type at once,
so <code>keyof (A | B)</code> only saw the keys common to every element
— it now distributes over the object union and filters each element
independently; (2) when TypeScript merges mismatched array element types
it injects phantom optional <code>undefined</code> keys (e.g.
<code>transKey1_withContext?: undefined</code> on elements that don't
define it), which the context-detection helpers mistook for real context
variants — they now skip keys typed as <code>undefined</code>. Also adds
a dedicated <code>context</code> + <code>returnObjects: true</code>
selector overload using <code>const Fn</code> +
<code>ReturnType<Fn></code>, so <code>Target</code> is no longer
collapsed to <code>unknown</code> via <code>ApplyTarget</code>. Resolves
Problem 1 of <a
href="https://redirect.github.com/i18next/i18next/issues/2398">#2398</a>
(Problem 2 was already fixed on master). Thanks <a
href="https://github.com/sauravgupta-dotcom"><code>@sauravgupta-dotcom</code></a>
(<a
href="https://redirect.github.com/i18next/i18next/pull/2438">#2438</a>).
Fixes <a
href="https://redirect.github.com/i18next/i18next/issues/2398">#2398</a>.</li>
</ul>
<h2>v26.3.2</h2>
<ul>
<li>fix: chained formatters with a parenthesised option that contains
the format separator (e.g. <code>join(separator: ', ')</code>) now work
at <strong>any</strong> position in the chain, not just first.
Previously the comma-in-parens reassembly only repaired
<code>formats[0]</code>, so <code>{{v, uppercase, join(separator: ',
')}}</code> split the <code>join(...)</code> option on the inner comma
and never rejoined it, producing corrupt output. Replaced the
first-position-only repair with a position-independent pass that
re-joins fragments until each open paren closes. Thanks <a
href="https://github.com/spokodev"><code>@spokodev</code></a> (<a
href="https://redirect.github.com/i18next/i18next/pull/2437">#2437</a>).</li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/i18next/i18next/blob/master/CHANGELOG.md">i18next's
changelog</a>.</em></p>
<blockquote>
<h2>26.3.6</h2>
<ul>
<li>fix: allow TypeScript 7 in the optional <code>typescript</code> peer
dependency range (<code>^5 || ^6 || ^7</code>). With
<code>typescript@7.0.2</code> in a project, <code>npm install</code>
failed with an <code>ERESOLVE</code> peer conflict. The published types
are TS7-compatible as-is: every <code>test/typescript</code> suite
produces identical results under 6.0 and 7.0.2. Reported in <a
href="https://redirect.github.com/i18next/react-i18next/issues/1927">react-i18next#1927</a>,
thanks <a
href="https://github.com/andikapradanaarif"><code>@andikapradanaarif</code></a>.</li>
</ul>
<h2>26.3.5</h2>
<ul>
<li>fix: <code>$t()</code> nesting options blocks that span multiple
lines are now parsed. <code>nest()</code> decided where the nested key
ends by testing <code>match[1]</code> with <code>/{.*}/</code>, whose
dot does not cross line breaks — so a <code>$t(key, { ... })</code>
options object containing a newline was treated as having no options,
mis-split as formatters, and the nested lookup ran without its options
(placeholders stayed unresolved). The nesting regexp itself already
matches newlines inside <code>$t(...)</code>; adding the <code>s</code>
(dotAll) flag makes multiline options behave like the single-line form.
Thanks <a href="https://github.com/spokodev"><code>@spokodev</code></a>
(<a
href="https://redirect.github.com/i18next/i18next/pull/2440">#2440</a>).</li>
<li>fix: <code>getUsedParamsDetails</code> (the <code>returnDetails:
true</code> path) no longer mutates the passed <code>replace</code>
object. It wrote <code>count</code> straight onto
<code>options.replace</code> so the returned <code>usedParams</code>
would include it — a caller reusing one <code>replace</code> object
across <code>t()</code> calls then carried a stale <code>count</code>
into later interpolations (e.g. a previous call's <code>count: 5</code>
rendered instead of the current call's value). The details are now built
from a copy; <code>usedParams</code> still includes <code>count</code>.
Thanks <a href="https://github.com/spokodev"><code>@spokodev</code></a>
(<a
href="https://redirect.github.com/i18next/i18next/pull/2441">#2441</a>).</li>
<li>fix: with the default <code>skipOnVariables: true</code> +
<code>escapeValue: true</code>, a <code>{{placeholder}}</code> carried
inside an interpolated value now stays literal even when the value
contains escapable characters. The skip logic advanced the regex
<code>lastIndex</code> by the raw value length, but the escaped text
written into the string is longer, so <code>lastIndex</code> landed
inside the inserted value and a trailing <code>{{placeholder}}</code> in
it got interpolated — leaking another in-scope variable that should have
stayed literal (values without escapable characters were already skipped
correctly). The advance now uses the escaped length that is actually
written, and the regex-safe <code>$</code>-doubling is applied only at
the <code>String.replace</code> call so it can't distort the length
arithmetic. Thanks <a
href="https://github.com/spokodev"><code>@spokodev</code></a> (<a
href="https://redirect.github.com/i18next/i18next/pull/2442">#2442</a>).</li>
</ul>
<h2>26.3.4</h2>
<ul>
<li>fix(security): <code>deepExtend</code> (used by
<code>addResourceBundle(..., deep, overwrite)</code>) no longer recurses
into inherited properties. It checked key existence with the
<code>in</code> operator, which walks the prototype chain, so a source
key matching an inherited built-in (e.g. <code>hasOwnProperty</code>,
<code>toString</code>) caused recursion into the shared
<code>Object.prototype</code> function and, with <code>overwrite:
true</code>, could overwrite e.g.
<code>Object.prototype.hasOwnProperty.call</code> with a non-callable
value — corrupting a shared built-in process-wide (DoS). Existence is
now checked with <code>Object.prototype.hasOwnProperty.call</code>, so
such keys are copied as plain own data instead. This complements the
existing <code>__proto__</code>/<code>constructor</code> guard and is
also strictly more correct for an own-property merge. Only affects
applications that pass attacker-controlled data with <code>deep:
true</code> and <code>overwrite: true</code>; no standard
backend/integration does this. Distinct from CVE-2026-48713 /
CVE-2026-48714 (different packages, <code>setPath</code> mechanism). See
advisory <a
href="https://github.com/i18next/i18next/security/advisories/GHSA-6jcc-5g8w-32mx">GHSA-6jcc-5g8w-32mx</a>,
CVSS 5.9 (<code>CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:N/I:L/A:H</code>).
Thanks to zx (Jace) <a
href="https://github.com/manus-use"><code>@manus-use</code></a> for the
responsible disclosure.</li>
</ul>
<h2>26.3.3</h2>
<ul>
<li>fix(types): selector <code>t($ => $.arr, { returnObjects: true,
context })</code> on a JSON array of <strong>heterogeneous</strong>
objects now preserves each element's full shape (e.g. <code>{ transKey1:
string; transKey2: string }[]</code>) instead of collapsing to a union
of partial element types. Two type-level causes: (1)
<code>FilterKeys</code> evaluated the whole array element type at once,
so <code>keyof (A | B)</code> only saw the keys common to every element
— it now distributes over the object union and filters each element
independently; (2) when TypeScript merges mismatched array element types
it injects phantom optional <code>undefined</code> keys (e.g.
<code>transKey1_withContext?: undefined</code> on elements that don't
define it), which the context-detection helpers mistook for real context
variants — they now skip keys typed as <code>undefined</code>. Also adds
a dedicated <code>context</code> + <code>returnObjects: true</code>
selector overload using <code>const Fn</code> +
<code>ReturnType<Fn></code>, so <code>Target</code> is no longer
collapsed to <code>unknown</code> via <code>ApplyTarget</code>. Resolves
Problem 1 of <a
href="https://redirect.github.com/i18next/i18next/issues/2398">#2398</a>
(Problem 2 was already fixed on master). Thanks <a
href="https://github.com/sauravgupta-dotcom"><code>@sauravgupta-dotcom</code></a>
(<a
href="https://redirect.github.com/i18next/i18next/pull/2438">#2438</a>).
Fixes <a
href="https://redirect.github.com/i18next/i18next/issues/2398">#2398</a>.</li>
</ul>
<h2>26.3.2</h2>
<ul>
<li>fix: chained formatters with a parenthesised option that contains
the format separator (e.g. <code>join(separator: ', ')</code>) now work
at <strong>any</strong> position in the chain, not just first.
Previously the comma-in-parens reassembly only repaired
<code>formats[0]</code>, so <code>{{v, uppercase, join(separator: ',
')}}</code> split the <code>join(...)</code> option on the inner comma
and never rejoined it, producing corrupt output. Replaced the
first-position-only repair with a position-independent pass that
re-joins fragments until each open paren closes. Thanks <a
href="https://github.com/spokodev"><code>@spokodev</code></a> (<a
href="https://redirect.github.com/i18next/i18next/pull/2437">#2437</a>).</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="e1c60d4dd2"><code>e1c60d4</code></a>
26.3.6</li>
<li><a
href="04da43e08c"><code>04da43e</code></a>
fix: allow typescript 7 in optional peerDependencies range
(react-i18next#1927)</li>
<li><a
href="8eed4accc6"><code>8eed4ac</code></a>
build</li>
<li><a
href="573ae73568"><code>573ae73</code></a>
26.3.5</li>
<li><a
href="cc54b05b5c"><code>cc54b05</code></a>
docs(changelog): 26.3.5 — multiline $t() options, replace mutation,
escaped-l...</li>
<li><a
href="3180d67291"><code>3180d67</code></a>
fix: skip interpolation of placeholders inside escaped values (<a
href="https://redirect.github.com/i18next/i18next/issues/2442">#2442</a>)</li>
<li><a
href="d16f5a2da7"><code>d16f5a2</code></a>
fix: stop mutating the passed replace object when returning details (<a
href="https://redirect.github.com/i18next/i18next/issues/2441">#2441</a>)</li>
<li><a
href="bed56c1159"><code>bed56c1</code></a>
fix: parse $t() nesting options block that spans multiple lines (<a
href="https://redirect.github.com/i18next/i18next/issues/2440">#2440</a>)</li>
<li><a
href="c19e45864f"><code>c19e458</code></a>
docs(changelog): link GHSA advisory for deepExtend fix</li>
<li><a
href="7bb87d09f9"><code>7bb87d0</code></a>
docs(changelog): reference security advisory for deepExtend fix</li>
<li>Additional commits viewable in <a
href="https://github.com/i18next/i18next/compare/v26.3.1...v26.3.6">compare
view</a></li>
</ul>
</details>
<br />
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
<details>
<summary>Dependabot commands and options</summary>
<br />
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
</details>
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps
[@tanstack/react-query](https://github.com/TanStack/query/tree/HEAD/packages/react-query)
from 5.101.2 to 5.101.4.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/TanStack/query/releases">@tanstack/react-query's
releases</a>.</em></p>
<blockquote>
<h2><code>@tanstack/react-query-devtools</code><a
href="https://github.com/5"><code>@5</code></a>.101.4</h2>
<h3>Patch Changes</h3>
<ul>
<li>Updated dependencies []:
<ul>
<li><code>@tanstack/query-devtools</code><a
href="https://github.com/5"><code>@5</code></a>.101.4</li>
<li><code>@tanstack/react-query</code><a
href="https://github.com/5"><code>@5</code></a>.101.4</li>
</ul>
</li>
</ul>
<h2><code>@tanstack/react-query-next-experimental</code><a
href="https://github.com/5"><code>@5</code></a>.101.4</h2>
<h3>Patch Changes</h3>
<ul>
<li>Updated dependencies []:
<ul>
<li><code>@tanstack/react-query</code><a
href="https://github.com/5"><code>@5</code></a>.101.4</li>
</ul>
</li>
</ul>
<h2><code>@tanstack/react-query-persist-client</code><a
href="https://github.com/5"><code>@5</code></a>.101.4</h2>
<h3>Patch Changes</h3>
<ul>
<li>Updated dependencies []:
<ul>
<li><code>@tanstack/query-persist-client-core</code><a
href="https://github.com/5"><code>@5</code></a>.101.4</li>
<li><code>@tanstack/react-query</code><a
href="https://github.com/5"><code>@5</code></a>.101.4</li>
</ul>
</li>
</ul>
<h2><code>@tanstack/react-query</code><a
href="https://github.com/5"><code>@5</code></a>.101.4</h2>
<h3>Patch Changes</h3>
<ul>
<li>Updated dependencies []:
<ul>
<li><code>@tanstack/query-core</code><a
href="https://github.com/5"><code>@5</code></a>.101.4</li>
</ul>
</li>
</ul>
<h2><code>@tanstack/react-query-devtools</code><a
href="https://github.com/5"><code>@5</code></a>.101.3</h2>
<h3>Patch Changes</h3>
<ul>
<li>Updated dependencies []:
<ul>
<li><code>@tanstack/query-devtools</code><a
href="https://github.com/5"><code>@5</code></a>.101.3</li>
<li><code>@tanstack/react-query</code><a
href="https://github.com/5"><code>@5</code></a>.101.3</li>
</ul>
</li>
</ul>
<h2><code>@tanstack/react-query-next-experimental</code><a
href="https://github.com/5"><code>@5</code></a>.101.3</h2>
<h3>Patch Changes</h3>
<ul>
<li>Updated dependencies []:
<ul>
<li><code>@tanstack/react-query</code><a
href="https://github.com/5"><code>@5</code></a>.101.3</li>
</ul>
</li>
</ul>
<h2><code>@tanstack/react-query-persist-client</code><a
href="https://github.com/5"><code>@5</code></a>.101.3</h2>
<h3>Patch Changes</h3>
<ul>
<li>Updated dependencies []:
<ul>
<li><code>@tanstack/query-persist-client-core</code><a
href="https://github.com/5"><code>@5</code></a>.101.3</li>
<li><code>@tanstack/react-query</code><a
href="https://github.com/5"><code>@5</code></a>.101.3</li>
</ul>
</li>
</ul>
<h2><code>@tanstack/react-query</code><a
href="https://github.com/5"><code>@5</code></a>.101.3</h2>
<h3>Patch Changes</h3>
<ul>
<li>Updated dependencies [<a
href="7e3c822a10"><code>7e3c822</code></a>]:</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/TanStack/query/blob/main/packages/react-query/CHANGELOG.md">@tanstack/react-query's
changelog</a>.</em></p>
<blockquote>
<h2>5.101.4</h2>
<h3>Patch Changes</h3>
<ul>
<li>Updated dependencies []:
<ul>
<li><code>@tanstack/query-core</code><a
href="https://github.com/5"><code>@5</code></a>.101.4</li>
</ul>
</li>
</ul>
<h2>5.101.3</h2>
<h3>Patch Changes</h3>
<ul>
<li>Updated dependencies [<a
href="7e3c822a10"><code>7e3c822</code></a>]:
<ul>
<li><code>@tanstack/query-core</code><a
href="https://github.com/5"><code>@5</code></a>.101.3</li>
</ul>
</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="86bb8a6fb2"><code>86bb8a6</code></a>
ci: Version Packages (<a
href="https://github.com/TanStack/query/tree/HEAD/packages/react-query/issues/11094">#11094</a>)</li>
<li><a
href="181ea826cb"><code>181ea82</code></a>
ci: Version Packages (<a
href="https://github.com/TanStack/query/tree/HEAD/packages/react-query/issues/11089">#11089</a>)</li>
<li><a
href="6d55b0759b"><code>6d55b07</code></a>
test({react,preact}-query): use the '.then()' convention consistently
(<a
href="https://github.com/TanStack/query/tree/HEAD/packages/react-query/issues/11085">#11085</a>)</li>
<li><a
href="44f38df5de"><code>44f38df</code></a>
test({react,preact,solid}-query/useInfiniteQuery): inline the shared
'fetchIt...</li>
<li><a
href="d1558c1491"><code>d1558c1</code></a>
test({react,preact}-query/usePrefetchQuery): inline the
'generateQueryFn' fac...</li>
<li><a
href="99690d18b7"><code>99690d1</code></a>
test({react,preact}-query/usePrefetchInfiniteQuery): inline single-use
helper...</li>
<li><a
href="10770f0720"><code>10770f0</code></a>
test({react,preact}-query/usePrefetchInfiniteQuery): inline the shared
'Suspe...</li>
<li><a
href="dbd5a86e95"><code>dbd5a86</code></a>
test({react,preact}-query/usePrefetchQuery): inline the shared
'Suspended' co...</li>
<li><a
href="b955f60d79"><code>b955f60</code></a>
test({react,preact}-query/useSuspenseQuery): assert the 'loading'
fallback is...</li>
<li><a
href="b9c657e20b"><code>b9c657e</code></a>
test({react,preact}-query/usePrefetchQuery): assert the 'Loading...'
fallback...</li>
<li>Additional commits viewable in <a
href="https://github.com/TanStack/query/commits/@tanstack/react-query@5.101.4/packages/react-query">compare
view</a></li>
</ul>
</details>
<br />
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
<details>
<summary>Dependabot commands and options</summary>
<br />
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
</details>
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps
[react-resizable-panels](https://github.com/bvaughn/react-resizable-panels)
from 4.12.1 to 4.12.2.
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/bvaughn/react-resizable-panels/blob/main/CHANGELOG.md">react-resizable-panels's
changelog</a>.</em></p>
<blockquote>
<h2>4.12.2</h2>
<ul>
<li><a
href="https://redirect.github.com/bvaughn/react-resizable-panels/issues/726">726</a>:
Updated inline documentation to clarify size units.</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="a1eeb7aefd"><code>a1eeb7a</code></a>
4.12.1 -> 4.12.2</li>
<li>See full diff in <a
href="https://github.com/bvaughn/react-resizable-panels/compare/4.12.1...4.12.2">compare
view</a></li>
</ul>
</details>
<br />
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
<details>
<summary>Dependabot commands and options</summary>
<br />
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
</details>
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - Cloud-managed instances authenticate tenant users through a
trusted-header path (`resolveCloudTenantActor`) that deliberately never
grants `instance_admin`, so every tenant user is company-scoped
> - On a dedicated (single-owner) managed instance that leaves the
paying owner unable to administer their own instance: instance settings,
the environments admin surface, and the custom sandbox image flow are
all instance-admin gated (the environments UI can't even show the
provider/image of the platform sandbox because the restricted read view
blanks `config` entirely)
> - Re-granting the old blanket `instance_user_roles` row would repeat
the mistake the shared-pool hardening fixed: DB rows go stale, resurrect
via restores, and elevate through every auth path
> - This pull request elevates only the stack `owner`, computed per
request at the trusted-header boundary behind a new managed-tier feature
flag, and ships that elevation together with code floors on the
platform-owned surfaces an instance admin must not control on a managed
instance
> - The benefit is that dedicated-stack owners can administer their own
instance while platform credentials, execution policy, backups, and
runtime code-install stay platform-owned, and self-hosted behavior is
unchanged
## Linked Issues or Issue Description
No public GitHub issue exists for this change; the underlying issue is
described here following the feature-request template.
### Problem or motivation
- On cloud-managed instances, tenant users resolved from trusted headers
are always company-scoped. For dedicated instances with a single paying
owner, the owner cannot reach any instance-admin surface of their own
instance (instance settings, environments administration, custom image
setup), and the restricted environment read view hides even structural
fields like the sandbox provider and image.
- The previous hardening intentionally removed blanket elevation (and
purges stale `instance_user_roles` rows on every trusted-header
authentication). That protection must not regress for shared
multi-tenant pools.
### Proposed solution
- Owner-only, computed, flag-gated elevation plus code floors on
platform-owned surfaces, in one PR so the elevation can never ship
without the floors.
### Alternatives considered
- Re-inserting an `instance_user_roles` row for owners (the
pre-hardening model): rejected — DB rows go stale, survive restores, and
elevate through every auth path; #7525 removed exactly this.
- Widening only the environments read view without any elevation:
rejected — it fixes one screen but still leaves a dedicated-instance
owner unable to administer instance settings or custom images.
- Elevating additional stack roles (`member`/`admin`/`support`):
rejected — only the owner has an ownership claim over the whole
instance; other roles stay company-scoped.
### Roadmap alignment
- Extends the shipped "Cloud deployments" roadmap work (multi-tenant
isolation, company-scoped cloud tenants, managed-instance bootstrap)
without overlapping planned core items, and leaves self-hosted behavior
unchanged.
## What Changed
- **New feature key** `enableOwnerInstanceAdmin` (`packages/shared`):
boolean flag in `instanceExperimentalSettingsSchema`, catalog tier
`managed`, `cloudDefault: true`, `selfHostedDefault: false`. Inert on
self-hosted instances — the elevation path only exists behind the cloud
tenant trust token.
- **Computed elevation** (`server/src/middleware/auth.ts`):
`resolveCloudTenantActor` now returns `isInstanceAdmin: true` only when
the trusted-header stack role is `owner` **and** the flag is enabled.
The flag is resolved through the instance-settings service so the
managed-config overlay applies (the control plane can disable elevation
fleet-wide without touching tenant databases; a DB row edit or restore
cannot resurrect it). Resolution fails closed on settings read errors.
The `instance_user_roles` never-insert and the per-request stale-row
purge are byte-identical. `member`/`admin`/`support` stack roles stay
company-scoped.
- **Authorization guard split**
(`server/src/services/authorization.ts`): the blanket-allow now trusts
the actor's *computed* `isInstanceAdmin` flag (only the attested
resolver can set it for `cloud_tenant` actors) while keeping the
`instance_user_roles` DB lookup excluded for `cloud_tenant` — a stale or
hand-inserted role row still elevates nothing.
- **Floor F1 — platform environment credentials**
(`server/src/routes/environments.ts`): on cloud-managed instances,
platform-provisioned environment rows (`managedByPaperclip` marker, plus
the legacy managed-Kubernetes marker) use a single floored view for
every reader on all environment routes (list, get, create, update,
delete responses): `envVars` are never echoed and credential-shaped
`config` keys (reusing the managed-config
`SECRET_LIKE_CONFIG_KEY_PATTERN`) are dropped — for **all** actors
including instance admins — while structural config (provider, image,
template, region, …) and the managed markers stay visible. This also
fixes the environments UI for managed sandboxes, which previously lost
the provider/image entirely in the restricted view. The floor also
covers writes: `PATCH /environments/:id` and `DELETE /environments/:id`
on a platform-provisioned row are rejected (403,
`environment_platform_managed`) for every actor including instance
admins, and the guard binds to the persisted row's markers so a patch
cannot strip the managed marker to lift the floor. The one recovery path
is a metadata-only PATCH that solely clears the marker keys
(null/false), for rows stamped through the old unrestricted API before
the markers became reserved — and it never applies to a row whose slot
markers are live platform state: the single local row
(`environments_local_driver_idx`), which `ensureLocalEnvironment` adopts
and stamps on cloud-managed instances from every caller (company
creation, the heartbeat, run orchestration), and the single marked
sandbox row (`environments_managed_sandbox_idx`) while a managed-sandbox
bootstrap path is configured (managed-config `environments` section or
`PAPERCLIP_EXECUTION_MODE=kubernetes`) and the provisioner therefore
adopts and refreshes it on every boot. Clearing a live slot row's
markers would let the next write reclassify it as tenant-managed and
bypass the floor; conversely, when no sandbox provisioning path is
configured the platform holds no claim on any sandbox row, so a platform
marker there is stale by definition and the recovery patch applies.
Every marker outside a live slot is clearable, so no legacy row is ever
locked permanently. Custom-image setup and probes on the platform
sandbox stay available to instance admins — those are the owner-facing
flows this elevation exists for. The marker keys themselves are
reserved: client create/update payloads that set `managedByPaperclip` or
`managedKubernetesSandbox` are rejected (422,
`environment_platform_marker_reserved`) on cloud-managed instances, so a
tenant row can never be stamped platform-provisioned through the API and
self-locked behind the write floor (the provisioner writes markers at
the service layer, not through these routes). Tenant-created
environments are otherwise unaffected.
- **Floor F2 — executionMode**
(`server/src/routes/instance-settings.ts`): on cloud-managed instances,
`PATCH /instance/settings/general` rejects writes that would change
`executionMode` (403, `execution_mode_platform_managed`). Same-value
echoes pass so settings forms that submit the full general-settings
object keep working. The boot-time execution-policy bootstrap path is
untouched (it calls the service directly).
- **Floor F3 — manual database backups**
(`server/src/routes/instance-database-backups.ts`): floored off on
cloud-managed instances (403, `database_backups_platform_managed`);
backups are platform-owned there, and the result would also echo a
server-side filesystem path.
- **Floor F4 — adapter code install** (`server/src/routes/adapters.ts`):
`POST /adapters/install` and `POST /adapters/:type/reinstall` are
floored off on cloud-managed instances (403,
`adapter_install_platform_managed`). Adapter packages execute in the
server process, so a runtime install would let an instance admin read
the platform trust anchors out of the process environment. This mirrors
the existing bundled-only plugin install floor; adapter code on managed
instances comes bundled with the platform image.
## Instance-admin surface audit
Before widening who can hold `isInstanceAdmin`, every
instance-admin-gated surface in `server/src` was enumerated and reviewed
for whether its response or side effects could echo process environment
values or platform credentials (tenant trust token, JWT signing keys,
database connection strings, provider API keys): 29 distinct gate
definitions covering ~90+ call sites, in four groups — sole
instance-admin gates (12), instance-admin-or-company-permission gates
(10), response-shaping/scope-widening sites (6), and the central
`allow_instance_admin` short-circuit in the authorization service (58
`decide()` call sites).
Findings and dispositions:
- **Environment read/write responses** exposed platform sandbox
`envVars`/credential-shaped config to instance admins → closed by floor
F1.
- **Manual backup trigger** echoed a server filesystem path and triggers
a platform-owned operation → closed by floor F3.
- **Adapter install/reinstall** loads externally fetched code into the
server process (indirect, complete env exposure) → closed by floor F4.
The sibling plugin-install path already had a bundled-only floor on
managed instances and needed no change.
- **Token-minting surfaces** (gateway tokens, custom-image
terminal/connection tokens) mint credentials scoped to the instance's
own resources, not platform trust anchors → acceptable for an
owner-admin of a dedicated instance; unchanged.
- All remaining gated surfaces return ordinary instance-scoped business
data; none echo `process.env` or platform secrets directly. OAuth client
secrets are referenced by env-var *name* only; SSH private keys are
stored as secret refs before persistence and are not echoed.
Operational note for managed platforms: this model assumes the process
environment of a managed instance holds only that instance's own
credentials. Platform operators should keep provider credentials
per-instance (never fleet-shared) since an instance admin ultimately
controls in-process code on their own instance.
## Verification
- `pnpm vitest run server/src/middleware/cloud-tenant-actor.test.ts` —
resolver matrix: owner × flag on/off, flag via managed overlay
(on-over-DB-off and off-over-DB-on), member/admin/support × flag on,
no-token self-hosted, fail-closed settings read, purge still runs and no
role row is ever inserted (14 tests).
- `pnpm vitest run server/src/__tests__/authorization-service.test.ts` —
computed flag elevates a `cloud_tenant` actor; a stale
`instance_user_roles` row still never does; `session` actors unchanged
(full suite, embedded Postgres).
- `pnpm vitest run server/src/__tests__/environment-routes.test.ts` —
F1: no secret echo to admins on get/list, structural config visible to
restricted readers, platform-row PATCH/DELETE rejected for admins
(including a marker-stripping patch), marker-clear recovery allowed for
stale legacy rows and for a marked sandbox row when no provisioning path
is configured, but refused on the managed local row and on the sandbox
slot row under a managed-config `environments` entry or the forced
kubernetes execution mode, client marker-stamping creates/patches
rejected, tenant rows still readable and writable, self-hosted
read+write regression (60 tests).
- `pnpm vitest run server/src/__tests__/environment-service.test.ts` —
`ensureLocalEnvironment` adopts a pre-existing local row on
cloud-managed instances (marker stamped, other metadata preserved,
idempotent — no rewrite on re-ensure) and leaves self-hosted rows
untouched (22 tests, embedded Postgres).
- `pnpm vitest run server/src/__tests__/instance-settings-routes.test.ts
server/src/__tests__/instance-database-backups-routes.test.ts` — F2
change-vs-echo matrix incl. self-hosted regression; F3 floor for both
admin shapes (32 tests).
- `pnpm vitest run server/src/__tests__/adapter-routes-authz.test.ts` —
F4 floor; self-hosted install/reinstall behavior unchanged (existing
cases).
- `pnpm vitest run server/src/__tests__/first-admin-claim.test.ts
server/src/__tests__/bootstrap-claim-routes.test.ts
server/src/__tests__/managed-config.test.ts
server/src/__tests__/health.test.ts
server/src/__tests__/instance-settings-managed-overlay.test.ts
server/src/services/managed-environments.test.ts
server/src/services/execution-policy-bootstrap.test.ts` — first-admin
bootstrap gate and managed-config behavior unchanged (91 tests).
- `pnpm vitest run packages/shared/src/feature-catalog.test.ts` —
catalog/schema sync tests cover the new key (selfHostedDefault must
equal the schema default).
- `pnpm run typecheck` — all 31 workspace projects clean.
## Risks
- Self-hosted behavior is unchanged: every floor binds to
`isCloudManagedInstance()` (tenant trust token present), the new flag
defaults off with no elevation path, and regression tests pin the
self-hosted branches.
- The elevation is fail-closed and stateless: turning the flag off
(managed overlay or DB) de-elevates on the next request; there is no
role row to clean up and restores cannot resurrect elevation.
- On a cloud-managed instance a pre-existing unmarked local row is
adopted (stamped `managedByPaperclip`) by the next ensure and becomes
platform-owned — the intended managed-product semantic: the platform
owns the single local slot. Self-hosted instances are untouched.
- F1 widens restricted readers' view of platform-provisioned rows from
fully blanked `config`/`metadata` to structural-only `config` plus
markers. Platform-delivered config is guaranteed secret-free by the
managed-config contract (secret-shaped keys fail startup), and the floor
re-drops secret-shaped keys defensively.
- One extra instance-settings read per trusted-header request for
owner-role actors (the resolver already performs several queries per
request).
## Model Used
Claude Fable 5 (Anthropic) — model id `claude-fable-5`, extended
thinking enabled, agentic tool use via Claude Code; read-only explore
subagents on the same model were used for the surface audit sweep.
## 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>
## Thinking Path
> - Paperclip is the open source control plane people use to organize
and operate AI-agent companies.
> - Agent behavior depends partly on the bundled Paperclip core skill
synchronized into each runtime.
> - The existing database and runtime plumbing already supports
immutable skill-version snapshots and per-agent version selections, but
no product workflow exposed that capability.
> - Replacing the live bundled skill globally would make champion
adoption risky and difficult to compare across agents.
> - This pull request adds an experimental, instance-level beta-skills
gate plus a repository release registry, immutable seeded releases,
enforcement, and a per-agent release picker.
> - The benefit is controlled per-agent evaluation of frozen core-skill
releases while the default-off path remains behaviorally unchanged.
## Linked Issues or Issue Description
### Subsystem affected
Cross-cutting: `server/`, `ui/`, `packages/db`, and `packages/shared`.
### Problem or motivation
Paperclip needs a safe way to evaluate improved versions of its core
operating skill without globally replacing the live default. Today the
version-snapshot and per-agent pin plumbing exists, but operators cannot
use it. A global replacement would make regressions difficult to contain
and would prevent controlled comparisons across agents.
### Proposed solution
Add a default-off instance experiment that exposes immutable, named
core-skill releases. When enabled, operators can pin each agent to a
seeded release; when disabled, every agent resolves the live default
while saved pins remain intact. Validate pinned writes at the API
boundary, gate reads at runtime, and expose the selection in the agent
Skills tab.
### Alternatives considered
- **Replace the bundled core skill globally:** rejected because it
changes every agent at once and provides no rollback/isolation boundary.
- **Ship releases as separate skills:** rejected because releases are
versions of one core capability, not independently enabled skills.
- **Store release snapshots only outside the repository:** rejected
because repository provenance and hashes make builds reproducible and
reviewable.
### Roadmap alignment
This extends the Skills Manager / Skill Studio direction in `ROADMAP.md`
by making core-skill versions operable per agent. It does not duplicate
another open implementation PR; GitHub searches found no related
`enableBetaSkills` change.
### Additional context
The feature remains experimental and default off. The V7 champion was
selected through a multi-model evaluation process, and the frozen
release contents are verified by SHA-256 below.
## What Changed
- Added the default-off instance-level `enableBetaSkills` experimental
flag.
- Added `skills-releases/paperclip/` with the ordered release registry
and frozen `v0` plus `v7-roster` snapshots.
- Added release metadata to `company_skill_versions` and idempotent
release seeding. The migration was planned as `0191`, then renumbered to
`0192` because current `master` claimed `0191` before final rebase.
- Added read-time gating and write-time validation so disabled instances
always resolve the live default and reject pinned-version writes.
- Added the per-agent Release picker in the agent Skills tab, including
responsive layout and beta-pin state.
- Kept `EDITS.md` out of the release registry and PR diff.
### V7 Adoption Evidence
- Paid roster: 6 models, 94-case suite.
- Result: 553/564 pass-within-2, mean 92.17/94, versus the P2 baseline
of 544/564.
- Reference model improved 84→91; maximin improved 84→90.
- Final report:
https://pages.paperclip.ing/skills/optimization/paperclip/pap-14624-p3-final-20260721/
### Provenance
- `v7-roster` is the Phase 1 champion plus additions-only edits
E107–E112. Per-edit rationale remains in the evals repository at
`source/v7-roster/EDITS.md` and is deliberately excluded from this PR.
- `v0` is the `skills/paperclip` tree from commit `ea66ea81`.
- Champion selection was accepted on July 21, 2026 via board card
`9c304fc2` (PAP-14624 G3).
- This delivery mechanism was accepted on July 24, 2026 via plan
revision `2367abd2` (PAP-14858).
### QA Evidence
- P4 QA matrix comment `b7f40522-4e9b-4a3a-9821-28e86fe1a987`: all 6
acceptance criteria passed.
- Automated QA matrix: 166 tests passed with 0 failures, including real
filesystem materialization and full SHA-256 assertions.
- UI QA exercised the real agent Skills tab at desktop and mobile widths
with the experimental flag both on and off.
## Verification
- `pnpm check:token-gates`
- Focused beta-release matrix: 169 tests passed across shared
validators, server services/routes/heartbeat behavior, instance settings
UI, and release picker UI.
- `pnpm -r typecheck`
- `pnpm build`
- `pnpm test:run`: server and UI partitions passed; one CLI doctor test
inherited temporary AWS credentials from the agent heartbeat and
expected no static credentials. The isolated rerun with
`AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, and `AWS_SESSION_TOKEN`
unset passed 8/8.
- V7 SHA-256:
- `SKILL.md`:
`53ab290489684cbf116fdd1406a95f6b6f53c9c36358b1bf8bfeae481e253575`
- `references/cases.md`:
`3b821f59064a7761091020a14819a8d787131f24029748563d6c0e1be7e6eaec`
- `references/workflows.md`:
`69747bd6e05f7e3673d1e67b07ff295df1869c05e1fd029804d5fa9177db92cd`
- Confirmed 49 changed files, no `pnpm-lock.yaml`, no workflow changes,
and no `EDITS.md`.
## Risks
- **Migration:** low-to-moderate risk. Three nullable columns and one
partial unique index are added idempotently; existing rows remain valid.
- **Behavior:** low risk while the flag is off because read-time
resolution forces the live default and saved pins are preserved but
inactive.
- **Frozen content:** release snapshots intentionally diverge from
future live skill edits; provenance and hashes make that divergence
explicit and reproducible.
- **UI:** low risk. The picker only renders for the bundled core skill
when the experimental flag is enabled and seeded releases exist.
> This extends the existing Skills Manager / Skill Studio direction
described in `ROADMAP.md`; it does not duplicate another open
implementation PR. The GitHub PR search found no related
`enableBetaSkills` change.
## Model Used
- OpenAI Codex using `gpt-5.5` with reasoning and
terminal/code-execution tools; context-window size is not exposed by
this runtime. Earlier implementation commits also record Claude Opus 4.8
assistance where applicable.
## 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
- [ ] I have not referenced internal/instance-local Paperclip issues or
links (required governance identifiers are included above; no internal
URL is included)
- [ ] My branch name describes the change and contains no internal
Paperclip ticket id (the approved delivery plan mandated this shared
branch name)
- [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>
## Thinking Path
> - Paperclip is the open source control plane people use to manage
AI-agent companies and their work.
> - Artifacts and documents are first-class outputs, but users need a
personal way to keep important documents easy to find.
> - Existing resource memberships already model per-user starred
projects and agents with company scoping and activity logging.
> - Documents lacked the equivalent membership model, route, and
artifact filtering behavior.
> - The shared membership contract also needs to remain safe for
existing UI project/agent mutation helpers when documents become a
recognized resource type.
> - This pull request extends the existing resource-membership system
with per-user document stars and a starred artifacts view.
> - The benefit is a company-scoped, idempotent server foundation for a
dedicated starred-documents experience without weakening authorization
or artifact filtering semantics.
## Linked Issues or Issue Description
### Problem / Motivation
Board users cannot star individual documents, and the company artifacts
API cannot return only the current user's starred documents.
### Proposed Solution
Add company/user-scoped document memberships, a board-only document star
route, document membership data in the shared contract, and a
`starred=true` artifacts filter.
### Alternatives Considered
A document column was rejected because stars are per-user; a separate
star API was rejected because projects and agents already use resource
memberships.
### Roadmap Alignment
This extends the existing Artifacts & Work Products roadmap area and
does not duplicate another open pull request found in the repository
search.
## What Changed
- Added the `document_memberships` schema and migration with
company/user/document uniqueness and starred ordering.
- Extended shared resource-membership and artifact-query contracts for
documents and `starred=true`.
- Added company-scoped document star/unstar service and board-only route
behavior with activity logging.
- Added starred document artifact filtering, including user-authored
documents, document kinds, cursor ordering, and incompatible-kind
handling.
- Preserved idempotency under concurrent star requests and synchronized
UI membership defaults/helpers with the expanded contract.
- Added focused shared, route, service, and UI regression coverage.
## Verification
- `pnpm exec vitest run packages/shared/src/resource-memberships.test.ts
server/src/__tests__/company-artifacts-service.test.ts
server/src/__tests__/resource-memberships-routes.test.ts`
- `pnpm exec vitest run ui/src/components/SidebarAgents.test.tsx
ui/src/components/SidebarProjects.test.tsx
ui/src/components/SidebarStarredProjects.test.tsx`
- `pnpm --filter @paperclipai/db check:migrations`
- `pnpm -r typecheck`
- `pnpm test:run`
- `pnpm build`
## Risks
- The migration adds a new membership table and non-concurrent indexes;
migration safety gates pass with the repository's established policy.
- The starred artifacts query intentionally returns only documents and
relaxes the normal agent-authored/system-kind predicates for documents
the current user explicitly starred.
- Document membership mutations remain board-user-only; agent callers
receive no document-star capability.
> 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 CLI; runtime model ID and context-window size were not
exposed to this session. Reasoning, repository tool use, code execution,
and test 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>
## Thinking Path
> - Paperclip is the control plane people use to create and govern
AI-agent companies
> - Agent creation persists runtime configuration that controls which
model profiles future runs may select
> - Adapters can expose a `cheap` profile, and existing creation paths
implicitly left that profile available when operators made no choice
> - That made a newly created agent eligible for a lower-cost model
without an explicit operator opt-in
> - The UI also dropped an explicit opt-in when the operator selected
the adapter's default cheap model rather than a custom model ID
> - Codex additionally hardcoded `gpt-5.3-codex-spark` into its cheap
profile and static fallback model list, making Paperclip choose an
auth-dependent model rather than requiring an operator choice
> - This pull request makes new-agent creation disable an available
cheap profile by default while preserving explicit opt-in from the UI or
API
> - The Codex cheap profile now remains available for explicit
configuration but supplies no model default, so an unconfigured cheap
request stays on the primary model
> - The benefit is predictable model quality for new agents and an
intentional, auditable choice before lower-cost routing is enabled
## Linked Issues or Issue Description
**Problem**
New agents created with an adapter that exposes a `cheap` model profile
can inherit that profile without the operator explicitly enabling it. In
the UI, enabling the adapter-default cheap model is also omitted because
runtime configuration is only written when a custom model ID is present.
**Expected behavior**
- New agents default an available `cheap` model profile to `{ enabled:
false }` when the caller does not specify it.
- Explicit API configuration remains authoritative.
- UI opt-in persists even when the adapter default model is used.
- Codex does not advertise or automatically select
`gpt-5.3-codex-spark`; operators must explicitly configure any
lower-cost Codex model.
**Related public work**
- Refs #4881, which introduced cheap model profiles for local adapters.
- Supersedes the default-selection portions of #8032 and #10004 by
removing the Codex model default instead of replacing it with another
hardcoded model.
## What Changed
- Detect whether the selected adapter exposes a `cheap` model profile
during agent creation and hiring.
- Persist `runtimeConfig.modelProfiles.cheap.enabled = false` only when
the caller did not explicitly configure the profile.
- Preserve UI cheap-profile opt-in when using the adapter's default
model by writing an empty adapter config.
- Remove `gpt-5.3-codex-spark` from the Codex static model list.
- Keep the Codex `cheap` profile explicitly configurable while giving it
an empty adapter config, so Paperclip never chooses a cheap Codex model
automatically.
- Verify that a Codex cheap request without an explicit model leaves the
primary model unchanged.
- Extend server route and UI runtime-config tests for default-disable
and explicit-opt-in behavior.
## Verification
- `env -u PAPERCLIP_IN_WORKTREE -u PAPERCLIP_WORKTREE_NAME -u
PAPERCLIP_CONFIG -u PAPERCLIP_HOME -u PAPERCLIP_INSTANCE_ID -u
PAPERCLIP_CONTEXT pnpm exec vitest run
packages/adapters/codex-local/src/index.test.ts
packages/adapters/codex-local/src/server/codex-args.test.ts
server/src/__tests__/adapter-models.test.ts
server/src/__tests__/adapter-registry.test.ts
server/src/__tests__/heartbeat-model-profile.test.ts
server/src/__tests__/agent-permissions-routes.test.ts
ui/src/lib/new-agent-runtime-config.test.ts`
- Result: 7 test files passed, 105 tests passed.
- GitHub `Typecheck + Release Registry` check passed on the final head.
- `git diff --check public-gh/master...HEAD`
## Risks
- Low behavioral risk: only newly created or hired agents are
normalized; existing agents are unchanged.
- Explicit `cheap` profile settings remain untouched, including explicit
opt-in.
- Codex users who explicitly opt into the cheap lane must choose a
model; requests without a configured override intentionally continue on
the primary model.
- Adapter profile discovery is now awaited during creation, adding a
small amount of adapter metadata lookup work.
- The source branch name is automation-provided and retained as required
by the task, so it does not satisfy the preferred public branch naming
convention.
> 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 `gpt-5.4` via Codex CLI, with reasoning, repository editing,
terminal execution, and GitHub/Paperclip tool access. 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)
- [ ] 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>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - Operators review decisions and turn useful outcomes into training
examples
> - The training library belongs to the Decisions surface, but its
routes and entry point appeared as a separate top-level destination
> - That mismatch made the training icon feel disconnected and left
internal links pointing at legacy `/training` URLs
> - This pull request nests the training library and inspector under
`/decisions/training`, keeps legacy URLs working through redirects, and
uses one route helper everywhere
> - The benefit is a clearer Decisions workflow without breaking
existing bookmarks or links
## Linked Issues or Issue Description
Refs: #9718
Refs: #9779
**Problem:** The Decisions page exposed training as a labeled header
action while the training library lived at the unrelated top-level
`/training` route. Links across the UI duplicated that legacy path.
**Expected behavior:** Training should be an icon action alongside the
other Decisions controls, and training library/record URLs should live
under `/decisions/training` while old URLs continue to redirect.
**Steps to reproduce:**
1. Open the Decisions page.
2. Observe the separate labeled Training button in the page heading.
3. Open a training record and note the top-level `/training/...` URL.
**Version/commit:** Current `master` before this PR.
**Deployment mode:** All UI deployment modes.
## What Changed
- Move the training library and inspector routes under
`/decisions/training`.
- Redirect legacy `/training` and `/training/:id` URLs to the new
locations.
- Add `decisionTrainingHref()` and use it for training links and
navigation.
- Place the Training icon action with the Decisions filter and sort
controls.
- Add focused tests for library and record URL generation.
## Verification
- `pnpm exec vitest run ui/src/lib/decisionTraining.test.ts`
- `pnpm --filter @paperclipai/ui typecheck`
- `pnpm --filter @paperclipai/ui build`
- `pnpm check:token-gates`
## Risks
- Low risk: the change is limited to client-side routes and navigation.
- Existing `/training` bookmarks remain supported through replacement
redirects.
- No API, database, migration, dependency, or workflow 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 using GPT-5.4 with reasoning, repository tool use, shell
execution, and code/test 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
- [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>
## Thinking Path
> - Paperclip is the open source control plane people use to manage
AI-agent companies.
> - Its secrets subsystem can resolve external references such as AWS
Secrets Manager values without copying those values into Paperclip
custody.
> - Operators also need to rotate a referenced secret's value while
preserving the same provider reference for consumers inside and outside
Paperclip.
> - Previously, external-reference rotation could only retarget
metadata, and secret detail sheets were driven by local component state
rather than shareable navigation state.
> - This pull request adds an optional provider write capability,
implements AWS Secrets Manager write-through rotation, and exposes
capability-aware rotate modes in the UI.
> - It also makes secret and each-user definition detail sheets
URL-driven and adds a copy-link action.
> - The benefit is that operators can update the canonical external
value safely while keeping AWS rotation tracking intact, and they can
share or navigate directly to secret details.
## Linked Issues or Issue Description
### Subsystem affected
Cross-cutting (`server/`, `ui/`, and `packages/shared`).
### Problem or motivation
External-reference secrets can follow a provider-managed value, but
Paperclip could not write a replacement value back to providers that
support it. Operators had to leave Paperclip, update the value
separately, and then return without an auditable Paperclip rotation
record. Secret detail sheets also could not be shared or restored
through browser history because their selection lived only in component
state.
### Proposed solution
Add an optional `updateExternalSecretValue` provider capability and
surface it as `supportsExternalValueWrites`. Implement AWS writes with
`PutSecretValue` while leaving the resolution `versionId` unset so
future reads continue following `AWSCURRENT`. Add write-value and
retarget modes to the rotate dialog for capable providers. Drive secret
detail selection from `?secret=` / `?definition=` query parameters and
provide a copy-link action.
### Alternatives considered
Converting an external reference into a Paperclip-managed secret would
break consumers that depend on the existing provider reference. Pinning
reads to the newly written AWS version would prevent later out-of-band
rotations from flowing through. Keeping sheet selection only in React
state would not support browser Back or shareable links.
### Roadmap alignment
This extends the completed “Secrets Manager with per-agent access”
roadmap capability; it does not duplicate a separate planned roadmap
item. Public GitHub searches found no duplicate or closely related issue
or PR.
### Additional context
The PR includes focused provider, service, and UI render coverage.
Cutter also generated previews for the deep-linked detail sheet and
capability-aware rotate modes.
## What Changed
- Added optional external-value write support to the secret provider
contract and provider descriptors.
- Implemented AWS Secrets Manager write-through with `PutSecretValue`,
audit material, and compensation when persistence fails after the
provider write.
- Allowed `secretService.rotate()` value updates for external references
while rejecting ambiguous value-plus-retarget combinations and
unsupported providers.
- Added capability-aware “Write new value” and “Change reference” rotate
modes with updated custody and action copy.
- Made secret and each-user definition detail sheets source their
selection from URL query parameters, compose with folder paths, close
through browser history, and expose a copy-link action.
- Added provider, service, and UI render coverage for write-through,
rollback, capability messaging, dialog modes, and deep links.
## Verification
- `pnpm vitest run
server/src/__tests__/aws-secrets-manager-provider.test.ts` — 18 passed.
- `pnpm vitest run server/src/__tests__/secrets-service.test.ts` — 75
passed.
- `pnpm vitest run ui/src/pages/Secrets.render.test.tsx` — 31 passed.
- `pnpm --filter @paperclipai/shared typecheck` — passed.
- `pnpm --filter @paperclipai/server typecheck` — passed.
- `pnpm --filter @paperclipai/ui typecheck` — passed.
- `pnpm check:token-gates` — passed with all gates clean.
## Risks
- External value writes affect the canonical provider secret and
therefore all consumers of that AWS secret; the UI explicitly labels
this custody behavior.
- A provider write can succeed before Paperclip persistence fails. The
service records the written version and AWS support includes
compensation coverage to restore the prior value where possible;
unrecoverable failures return explicit audit-safe error details.
- URL-driven sheet state changes navigation behavior; render tests cover
deep links, Back/close behavior, and composition with folder query
state.
- No database migration or breaking API requirement is introduced;
providers without the optional capability retain reference-only
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 using exact model ID `gpt-5.6-sol`, high reasoning mode,
Codex CLI `0.142.5`, with repository, shell, Git, GitHub CLI, and
code-execution tools. The runtime did not expose a context-window size.
- Earlier implementation commits were assisted by Anthropic `Claude
Fable 5` as recorded in their commit trailers; the exact backend model
ID and context-window size were not preserved in the workspace metadata.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change and contains no internal
Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes, or
confirmed no documentation change is required
- [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 Fable 5 <noreply@anthropic.com>
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - Scheduled routines provide recurring control-plane work without
manual intervention.
> - The activity gate (`activity_gate_policy` / `activity_gate_scope`)
lets a scheduled routine skip a tick when nothing has happened since its
last run, so watcher-style routines stay asleep while the system is
settled instead of burning tokens every tick.
> - The scheduler, database columns, and the create/update API for those
fields already landed (see #9438), but there was no way for an operator
to actually set the policy from the routine editor, and the runs list
rendered gated skips as bare "skipped" rows with no "why".
> - This pull request adds the editor control and the run-row labels:
the Delivery section gets an "Advanced run policy" picker plus a scope
selector, and skipped runs explain why they were skipped.
> - The benefit is that the activity gate becomes discoverable and
usable end-to-end from the UI, closing the loop on the feature the API
already supports.
## Linked Issues or Issue Description
- Refs #8534 — activity gate for scheduled routines.
- Builds on #9438 (merged) which exposed the activity-gate create/update
API and the `Routine` response fields this UI reads and writes. This PR
is the editor/UI companion to that API work.
## What Changed
- **Routine editor — Advanced run policy control**
(`ui/src/components/routine-sections/editable-sections.tsx`): the
Delivery section gains a `RadioCardGroup` to choose between *Run on
every scheduled tick* (default) and *Skip when there's been no activity
since the last run*. When gating is enabled, a second scope picker
(*Company-wide* / *This project*) appears. The control is **disabled
with an explanatory hint** — rather than hidden — when the routine has
no schedule trigger, since the gate only affects scheduled ticks
(webhook/manual/API fires are themselves activity and always run). This
keeps the capability discoverable.
- **Edit-draft plumbing**
(`ui/src/components/routine-sections/context.tsx`,
`ui/src/pages/RoutineDetail.tsx`): `activityGatePolicy` /
`activityGateScope` flow through the edit draft, the delivery section's
dirty-field detection, the save payload, and revision restore, matching
how `concurrencyPolicy` / `catchUpPolicy` are handled.
- **Run-history skip reasons** (`ui/src/lib/routine-run-display.ts`):
skipped run rows now render a human-readable "why" —
`no_external_activity` → "Skipped — no activity since last run", plus
labels for `paused` and `worktree_execution_cutoff` — instead of a bare
status.
- Storybook fixture updates for the new routine fields, plus focused
run-display coverage for the skipped-run labels.
## Verification
- `pnpm exec vitest run ui/src/lib/routine-run-display.test.ts` — 11
tests passing.
- `pnpm --filter @paperclipai/ui typecheck` — clean.
- `pnpm check:token-gates` — all token gates clean.
- `pnpm --filter @paperclipai/ui build` — production build succeeds.
- Current-head GitHub CI for `9f9af8ecccfadcdc4a3afab313aafad81cbd1112`
— all checks pass (Storybook visual check skipped by workflow policy).
## Risks
- **Low risk.** UI-only change; no schema/migration and no server
changes (the API and columns already shipped in #9438). Fields are
optional and default to the pre-feature behavior (`always` / `company`),
so existing routines are unaffected. The scope picker only renders when
gating is turned on, and the whole control is inert without a schedule
trigger.
## Model Used
- Claude Opus 4.8 (`claude-opus-4-8`, 1M context), extended thinking,
with tool use (repo edit + shell 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
- [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>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - Agents and boards coordinate through issue-thread interactions
(request_confirmation, ask_user_questions, suggest_tasks, …) that wait
as `pending` cards until someone resolves them
> - Two lifecycle gaps existed: an interaction's creator could not take
back a card it no longer stands behind, and interactions left `pending`
on issues that reached a terminal status lingered forever as
live-looking approval requests
> - Stale pending cards mislead humans (they look actionable), distort
attention/liveness signals, and in the worst case invite acting on a
proposal whose issue is already closed or cancelled
> - This pull request adds an explicit withdraw route for pending
interactions and automatically expires pending interactions when their
issue reaches a terminal status (including a catch-up sweep for issues
closed before this change)
> - The benefit is that interaction cards now faithfully reflect
reality: only genuinely actionable requests stay pending, and creators
can retract requests that events have overtaken
## Linked Issues or Issue Description
Fixes#5787
Refs #7403
Related prior PRs found while searching for duplicates (all overlap
partially; none combine both lifecycle paths or the route-level
authorization used here): #6709 and #7312 (creator-withdraw attempts),
#8169 (terminal expiry), #8081 and #5137 (generalized cancel/expire
endpoints), #6094 (stale confirmation auto-resolve). Related merged
context: #9568 (agent cancel for ask_user_questions), #10119 (tolerating
legacy `withdrawn_by_creator` result outcomes — the reader side of the
outcome this PR writes).
## What Changed
- New route `POST /issues/:id/interactions/:interactionId/withdraw` that
resolves a `pending` interaction to status `withdrawn` with a structured
result (`outcome: "withdrawn"`, optional trimmed `reason`), stamps
`resolvedBy*`/`resolvedAt`, touches the issue, logs activity, and emits
resolved-interaction telemetry
- Withdrawal authorization: board users, the interaction's creator
agent, or the issue's current assignee agent (assignees additionally
pass the standard issue-mutation gate); task-watchdog runs are
explicitly rejected, and authorization-boundary plus low-trust
control-plane checks apply
- Withdrawing an already-resolved interaction returns `409`;
unknown/cross-issue/cross-company interaction ids return `404`
- New service method `expirePendingInteractionsForTerminalIssue`: when
an issue transitions to a terminal status, all of its `pending`
interactions are resolved to `expired` with `outcome: "issue_closed"`,
guarded by a `status = 'pending'` predicate so concurrent resolutions
are not overwritten
- The same expiry runs as a catch-up when interactions are listed on an
already-terminal issue, so cards stranded by issues closed before this
change also get cleaned up; expired request_confirmations are logged
with a distinguishing source
- Shared package: new `withdrawIssueThreadInteractionSchema` validator,
`WithdrawIssueThreadInteraction` type, and `withdrawn` / `issue_closed`
result-outcome support for all interaction kinds (kind-aware result
shapes for `ask_user_questions` and `request_item_verdicts`)
- UI helper `ui/src/lib/issue-thread-interactions.ts` recognizes the new
outcomes for card rendering
- Docs: bundled skill API reference updated with the withdraw endpoint
- Review follow-up: terminal expiry moved from the HTTP route hooks into
`issueService.update`'s status-transition block, so direct service
callers (tree control, recovery, pipelines, status cards) expire pending
cards too; the list-endpoint catch-up remains for issues closed before
this change
- Review follow-up: withdrawing or issue-close-expiring a
`request_confirmation` also settles its linked `tool_action_requests`
row (withdraw -> `cancelled`, issue closed -> `expired`), so a parked
tool call cannot stay approvable after its card is gone
- Review follow-up: interaction cards render dedicated copy for the new
outcomes ("Withdrawn" with the reason, "Expired when issue closed")
instead of falling through to superseded-by-comment / stale-target
variants; withdrawn plan reviews badge as "Withdrawn" rather than
"Changes requested"
## Screenshots
Card states rendered from a local ux-lab harness with mocked data ([full
gallery](https://pages.paperclip.ing/pr-10251-interaction-withdrawal-cards/)):
| Light | Dark |
| --- | --- |
| 
| 
|
## Verification
- `pnpm --filter @paperclipai/shared build` — clean tsc
- `cd server && npx vitest run
src/__tests__/issue-thread-interaction-routes.test.ts` — 22 tests pass,
including new coverage for: creator-agent withdraw success,
non-creator/non-assignee agent 403, watchdog-run 403, double-withdraw
409, and board-user withdraw
- `cd server && npx vitest run
src/services/issue-thread-interactions.test.ts` — 4 tests pass,
including terminal-issue expiry writing `issue_closed` results and
leaving already-resolved interactions untouched
- `cd ui && pnpm typecheck` — clean
- `cd server && npx vitest run src/__tests__/issues-service.test.ts` —
includes a new embedded-Postgres test proving a direct
`issueService.update` terminal transition expires pending interactions
and writes the activity-log entry
- `cd ui && npx vitest run
src/components/IssueThreadInteractionCard.test.tsx` — 32 tests,
including new coverage for withdrawn / issue-closed confirmation and
question cards
- `cd server && npx tsc --noEmit` — matches the pre-existing repo error
baseline exactly (no new errors)
- Manual: `POST /issues/:id/interactions/:interactionId/withdraw` with
`{"reason":"superseded"}` as the creator agent resolves the card to
`withdrawn`; closing an issue with a pending confirmation flips it to
`expired` with `outcome: "issue_closed"`
## Risks
- Interactions on terminal issues now auto-expire (including
retroactively via the list-time catch-up), so consumers that expected to
resolve a pending interaction on a closed issue will get `409`; this is
the intended semantics and matches how the attention feed already wants
to treat dead cards
- New result outcomes (`withdrawn`, `issue_closed`) are written to
stored results; readers were already made tolerant of these outcome
strings in #10119, so mixed-version reads are safe
- No schema/migration changes; per-row conditional updates (`status =
'pending'`) avoid clobbering concurrent resolutions
- Withdrawal is a new mutation surface, but it is strictly narrower than
existing resolve paths (board, creator, or assignee only; watchdog runs
blocked)
## Model Used
Claude Fable 5 (`claude-fable-5`, Anthropic Mythos-class tier) with
extended thinking and agentic tool use (Claude Code harness); commit
authored in a Paperclip-managed engineering session.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [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>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - The inbox helps operators scan and act on tasks that need attention
> - Inbox task rows currently include external-object summary markers
alongside the core task information
> - Those markers add a column of visual noise that is not needed for
inbox triage
> - External-object data must remain available to the inbox filters even
when the row marker is removed
> - This pull request stops passing external-object summaries into inbox
rows and adds regression coverage
> - The benefit is a cleaner inbox while preserving external-object
filtering behavior
## Linked Issues or Issue Description
- Refs #4556
- **Problem:** Inbox task rows display external-object summary markers
that operators do not need for triage.
- **Expected behavior:** Inbox rows omit the external-object marker,
while filters that depend on external-object summaries continue to work.
## What Changed
- Removed the external-object summary prop from inbox task rows.
- Added a regression test that provides external-object summary data and
confirms the inbox row does not render its marker.
- Kept external-object summary loading intact for inbox filtering.
## Verification
- `pnpm exec vitest run ui/src/pages/Inbox.test.tsx` — 18 tests passed.
- `pnpm check:token-gates` — reproduces five pre-existing `#9627`
color-literal violations; this PR adds no token values or new gate
violations.
## Risks
- Low risk: the change removes one optional presentation prop from the
inbox row call site and leaves filtering data flow unchanged.
- Regression coverage verifies summary data no longer produces the
removed inbox marker.
> 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-enabled coding agent with shell and code
execution; reasoning enabled; context-window size not exposed by the
runtime.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - Scheduled routines provide recurring control-plane work without
manual intervention
> - The new activity gate can suppress scheduled runs when no external
work occurred
> - The core scheduler and database support landed without a public
create/update contract
> - Agents, operators, and managed plugins need validated fields plus
discoverable semantics to opt in safely
> - This pull request exposes the activity gate through routine APIs,
revisions, plugin contracts, tests, and skill documentation
> - The benefit is backward-compatible control over idle scheduled work
without losing activity-triggered follow-up
## Linked Issues or Issue Description
- Refs #8534
## What Changed
- Added shared activity-gate policy and scope enums with create/PATCH
validation.
- Persisted activity-gate fields through routine creation, updates,
revision snapshots, pipeline snapshots, and revision restores.
- Defaulted legacy revision snapshots during restore and added
regression coverage for pre-field snapshots.
- Extended managed-plugin routine declarations, production
reconciliation, and the SDK test harness to preserve non-default gate
settings.
- Added end-to-end API coverage for create/PATCH/list/detail
round-trips, defaults, and invalid enum rejection.
- Documented schedule-only semantics, activity windows,
own-run/read-action exclusions, scopes, and an hourly quiet-night
watcher example.
## Verification
- `pnpm exec vitest run packages/shared/src/validators/routine.test.ts
server/src/__tests__/routines-service.test.ts
server/src/__tests__/routines-e2e.test.ts`
- `pnpm exec vitest run packages/shared/src/validators/plugin.test.ts
packages/plugins/sdk/tests/testing-actions.test.ts
server/src/__tests__/plugin-managed-routines.test.ts
server/src/__tests__/routines-service.test.ts -t 'activity
gate|preserves declared activity gate settings|resolves routine agent
and project refs'`
- `pnpm exec vitest run ui/src/lib/workspace-routines.test.ts
ui/src/pages/Routines.test.tsx`
- `pnpm --filter @paperclipai/shared typecheck`
- `pnpm --filter @paperclipai/plugin-sdk typecheck`
- `pnpm --filter @paperclipai/server typecheck`
- `pnpm --filter @paperclipai/ui typecheck`
- `pnpm check:token-gates`
- GitHub CI: all final-head checks green; Storybook visual regression
skipped by path rules.
- Greptile: 5/5 with no unresolved review threads.
## Risks
- Low risk: defaults remain `always` and `company`, preserving existing
routine behavior and old revision snapshots.
- Managed plugin manifests can now declare the same validated gate
settings as the public routine API; omitted values retain core defaults.
- Revision snapshots now include the new fields so policy changes are
not lost or treated as no-ops during restore.
> For core feature work, checked `ROADMAP.md`: this extends the existing
Scheduled Routines roadmap item and does not duplicate a separate
planned capability.
## Model Used
- OpenAI GPT-5.5 via Codex CLI, with repository tool use and code
execution; context-window size was not exposed by the runtime.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - Status cards summarize changing company work and watch issues so
later changes can produce useful deltas
> - A summary can explicitly reference issues that are important to the
update even when those issues do not match the card's configured queries
> - Previously, those referenced issues were not retained in the watched
set, so their later status, assignee, or comment changes could be missed
> - The watched snapshot must avoid artificial additions or removals
caused only by a summary changing which issues it references
> - This pull request resolves issue references when a summary is
written, persists them, and joins them to the watched snapshot with
stable delta semantics
> - The benefit is that status cards continue tracking the exact issues
their latest update called out while keeping follow-up updates relevant
and non-duplicative
## Linked Issues or Issue Description
### Pre-submission checklist
- [x] I have searched existing open and closed issues and this is not a
duplicate.
- [x] I am on the latest released version of Paperclip (or can reproduce
on `master`).
- [x] I have confirmed the error originates in Paperclip itself — not in
my agent adapter, API provider, or local configuration.
### What happened?
When a status-card summary explicitly referenced an issue by identifier
or `/issues/<uuid>` URL, that issue was not automatically retained in
the card's watched set unless it independently matched a configured
query. Later status, assignee, or comment changes to an issue
highlighted by the latest update could therefore be omitted.
### Expected behavior
References in the latest summary should resolve only within the card's
company, appear in dry runs and the watched-issues UI, count and
fingerprint like query matches, and enter or leave the watched set
without artificial added/removed deltas already represented by the
summary change.
### Steps to reproduce
1. Create a status card whose query does not match a second issue in the
same company.
2. Write a summary that references the second issue by identifier or
issue URL.
3. Inspect the card's watched count or Watched issues tab.
4. Change the referenced issue's status, assignee, or comments and run
the next update.
5. Before this change, the referenced issue is absent from the watched
snapshot and its later change does not produce the expected delta.
### Paperclip version or commit
- Reproduced on `master` before this PR (base commit `762ce5b4ef`).
### Deployment mode
- Local dev (`pnpm dev`), built from source.
### Agent adapter(s) involved
- Not adapter-specific (core bug).
### Database mode
- Embedded Postgres test environment; the schema change uses standard
PostgreSQL JSONB.
### Access context
- Board (human operator).
## What Changed
- Added migration `0191` and schema support for persisted
`status_cards.mentioned_issue_ids`.
- Resolved summary references by issue identifier or `/issues/<uuid>`
URL within the status card's company when summaries are written.
- Joined mentioned issues into watched counts and fingerprints so later
status, assignee, and comment changes generate normal update deltas.
- Suppressed artificial added/removed deltas when the latest summary
starts or stops mentioning an issue.
- Added `mentionedIssues` to dry-run responses and a “Mentioned in the
latest update” group in the Watched issues tab.
- Updated the summarizer prompt to explain that referenced issues
automatically join the watched set.
- Added focused server and UI coverage for reference resolution,
snapshot behavior, deltas, API responses, and rendering.
## Verification
- `pnpm --filter @paperclipai/server exec vitest run
src/__tests__/status-cards.test.ts
src/__tests__/status-card-update-engine.test.ts` — 31 tests passed.
- `pnpm --filter @paperclipai/ui exec vitest run
src/pages/StatusCards/StatusCardTile.test.tsx` — 11 tests passed.
- Earlier implementation verification also passed database/shared/server
typechecks, UI `tsc -b`, the broader StatusCards UI test set, and
embedded-Postgres migration application.
### Visual Verification
- Greptile T-Rex ran Playwright browser checks successfully and captured
the Status Card drawer Watched tab showing the new “Mentioned in the
latest update” grouping:
https://app.greptile.com/trex/runs/15796101/artifacts
## Risks
- The migration adds a nullable JSONB column and is backward-compatible;
existing cards have no mentioned issues until their next summary write.
- Reference extraction is company-scoped to prevent cross-company issue
association.
- Watched counts and future fingerprints change for cards whose latest
summaries reference issues; tests cover additions, removals, and
suppression of spurious deltas.
- This targeted status-card fix does not introduce a new roadmap
subsystem or external integration.
## Model Used
- Anthropic Claude Fable 5 (Paperclip model alias; exact underlying
provider model ID and context window were not recorded in the
implementation task metadata), with extended reasoning, tool use, and
code execution.
- OpenAI Codex coding agent (runtime model identifier and context window
not exposed to this task) prepared the PR, rebased the branch, and ran
focused verification with terminal 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)
- [ ] 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>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - The Routines page is part of the operator UI for scheduled routine
management
> - The grouped-by-folder view was not presenting folder sections inline
in the main pane
> - That made the folder grouping mode harder to scan and hid the
separation between custom folders, Unfiled routines, and built-in
routines
> - This pull request updates the Routines page rendering so folder
groups appear as inline sections and built-in routines still split into
their own section afterward
> - The benefit is that grouped routines stay readable and the page
matches the intended folder organization
## Linked Issues or Issue Description
No corresponding public GitHub issue exists, so the problem is described
directly below following the bug template.
### What happened
On the Routines page, selecting Group → Folder flattened the grouped
list into a single "All routines" section with only the separate
built-in routines section below it.
### Expected behavior
Group → Folder should render one inline section per folder, keep
routines with no folder in an Unfiled section, and preserve the separate
built-in routines section after the custom folder groups.
### Steps to reproduce
1. Open the Routines page.
2. Change grouping to Folder.
3. Observe the main pane.
4. The routine list is flattened instead of grouped into folder-labeled
inline sections.
### Paperclip version / commit
Current PR head: `a8e384c838e362de3437c7a88bc7aa38b10fd9c0` on
`fix/routine-folder-grouping`.
### Deployment mode
Local development workspace for the Paperclip app UI.
## What Changed
- Updated the Routines page rendering so grouped folders render as
inline sections instead of flattening into a single list.
- Kept routines without a folder grouped under Unfiled.
- Preserved the built-in routines section after custom folder groups.
- Added and updated tests for the folder-grouped rendering behavior.
## Verification
- `pnpm --filter @paperclipai/ui exec vitest run
src/pages/Routines.test.tsx`
- `pnpm --filter @paperclipai/ui typecheck`
- `pnpm check:token-gates`
## Risks
- Low risk: the change is localized to the Routines page rendering and
its test coverage.
- The main behavioral risk is accidental grouping regressions if future
routine-grouping logic changes without updating the tests.
## Model Used
OpenAI Codex, GPT-5, tool-use enabled, 256k-context class model.
## 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
- [ ] All Paperclip CI gates are green
- [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [ ] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Harold Kim <harold@paperclip.ing>
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip is the open source control plane people use to manage
AI-agent companies and their ongoing work.
> - Status cards turn a standing question into recurring,
agent-generated summaries on the board.
> - The existing setup split intent across a watch prompt and separate
update instructions, which made creation and later behavior harder to
understand.
> - A status card should have one durable source of truth for both
deciding what to watch and telling the summarizer what each update must
contain.
> - This pull request makes the card prompt that source of truth,
simplifies creation to one step, and lets operators choose the running
agent immediately.
> - The benefit is a smaller mental model, fewer configuration modes,
and consistent update instructions throughout the card lifecycle.
## Linked Issues or Issue Description
Status cards currently require operators to express the same intent in
two places: the watch prompt and optional update instructions with
append/replace/none modes. This feature simplifies the experimental
status-card workflow so a single prompt defines both the watch query and
every generated update. The create flow must also support selecting the
responsible agent without a second setup step.
Related prior status-card work: #10101.
## What Changed
- Use the status card's single prompt to compile the watch query and
directly instruct every summary update.
- Add migration `0190_status_card_single_prompt` to remove
`status_cards.instructions_mode` and `status_cards.instructions`.
- Add `agentId` to `createStatusCardSchema`, validate company
membership, and default new cards to the built-in Summarizer.
- Replace the two-step create flow with one prompt-and-agent dialog and
extract a shared `SummarizerAgentSelect` for create/settings surfaces.
- Remove the extra-instructions settings section, reset incremental
history when the prompt changes, and rename the board page to "Status".
- Update the bundled `status-card-query` skill and board-operator
documentation, then regenerate the skills catalog manifest.
## Verification
- Server status-card suites: 29/29 passing.
- UI `StatusCards` suites: 22/22 passing.
- Skills catalog suite: 20/20 passing.
- `tsc -b` passes for server, UI, shared, and database packages.
- `pnpm check:migrations` passes.
- Light and dark mode screenshots cover the new create dialog and
settings tab.
## Risks
- Migration `0190` intentionally drops existing separate instruction
text. Existing card prompts remain and become the update instructions
under the new model; status cards are experimental and feature-flagged.
- Prompt edits now reset the incremental summary chain and trigger a
full rebuild, which is intentional because the prompt is also the update
contract.
- Agent selection is company-scoped; invalid agent ids return a
validation error rather than creating a misrouted card.
> 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
- Implementation: Anthropic Claude via the `claude_local` adapter, agent
label "Claude Fable 5"; extended reasoning, tool use, and code
execution. The exact provider model id and context-window value were not
retained in the task metadata.
- PR preparation: OpenAI GPT-5.4 through Codex CLI, with reasoning,
repository inspection, GitHub CLI, and Paperclip API 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)
- [ ] 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: Claude Fable 5 <noreply@anthropic.com>
## Thinking Path
> - Paperclip is the open source control plane people use to manage
AI-agent companies.
> - Operators need a board-level way to monitor a changing slice of
company work without repeatedly rebuilding filters or reading raw task
threads.
> - Existing summaries are useful snapshots, but they do not provide a
dedicated query-backed card with refresh policy, change tracking, update
history, and per-update cost visibility.
> - The capability needs to be safe to evaluate before it becomes part
of the default product surface.
> - This pull request adds end-to-end experimental Status Cards, from
schema and query compilation through update orchestration and operator
UI.
> - The entire feature is gated behind the `enableStatusCards`
experimental toggle, including its route and sidebar entry.
> - The benefit is a governed, inspectable way to keep focused
operational rollups current while preserving explicit controls over
refresh frequency and spend.
## Linked Issues or Issue Description
### Subsystem affected
Cross-cutting (`packages/db`, `packages/shared`, `server/`, `ui/`, and
bundled skills/docs).
### Problem or motivation
Operators cannot currently define a reusable natural-language view of
company work, compile it into an inspectable query, and keep its summary
current as matching issues change. Rebuilding filters and rereading task
threads makes board-level monitoring repetitive and hides the
relationship between source changes, refresh cost, and the resulting
summary.
### Proposed solution
Add experimental Status Cards that compile operator intent into a query,
summarize matched work, record each update, expose
manual/interval/reactive refresh policies and costs, and preserve the
last good result across stale, updating, paused, and error states. The
capability is off by default and fully gated behind `enableStatusCards`,
including its route and navigation entry.
### Alternatives considered
- Extend existing one-off summaries: rejected because status cards
require persistent query provenance, refresh policy, update history, and
card-specific cost controls.
- Add a dashboard-only filter widget: rejected because it would not
provide governed background refresh, an update ledger, or an inspectable
compile pipeline.
- Ship the surface by default: rejected in favor of an experimental
toggle while behavior and operator value are evaluated.
### Roadmap alignment
This advances Paperclip’s board-level execution visibility and
output-first product goals. `ROADMAP.md` was checked and no duplicate
status-card initiative was found.
### Additional context
No related open PR was found in the public GitHub search for status
cards. The PR-only design wireframes were removed from the repository
after review; the published prototype remains external to the production
source tree.
## What Changed
- Added company-scoped status-card schema, CRUD APIs, compile
provenance, update ledger, shared contracts, validators, and OpenAPI
coverage.
- Added the text-to-query compile pipeline, bundled `status-card-query`
agent skill, query versioning, and authorized write-back flow.
- Added the experimental board, create flow, lifecycle tiles,
detail/settings/debug drawers, archived view, routing, navigation, and
instance setting.
- Added a change-gated update engine with manual, interval, and reactive
refresh policies, trigger selection, active hours, and daily token caps.
- Added per-update token/cost recording, today and lifetime rollups, and
policy-derived cost previews.
- Added operator documentation and agent-authoring hardening for compile
and update behavior.
- Added PR-prep integration coverage for settings/startup wiring and
replaced raw UI values with design-system tokens.
- Removed the PR-only `design/pap-15023-status-cards` wireframe
artifacts so the repository contains only production feature assets.
## Verification
- `pnpm -r typecheck` — passes on the PR head; includes `ui` `tsc -b`
passing. The UI compile gate was also independently recorded as passing
at `6d7f3cf96b` on July 23, 2026.
- `pnpm build` — passes.
- `pnpm check:token-gates` — passes with all three gates clean.
- `pnpm test:run` — 2,880 tests passed and 1 skipped; the sole failure
was an unrelated 10-second `afterAll` database-cleanup timeout in
`execution-workspaces-service.test.ts`.
- `pnpm --filter @paperclipai/server exec vitest run
src/__tests__/execution-workspaces-service.test.ts` — passes on
immediate focused rerun (25/25).
- `pnpm --filter @paperclipai/server exec vitest run
src/__tests__/instance-settings-service.test.ts
src/__tests__/server-startup-feedback-export.test.ts` — passes (31/31).
- `pnpm --filter @paperclipai/ui exec vitest run
src/pages/StatusCards/StatusCardSettingsForm.test.tsx
src/pages/StatusCards/StatusCardTile.test.tsx
src/pages/StatusCards/format.test.ts src/lib/status-card-state.test.ts`
— passes (26/26).
- Recorded pre-PR QA: compile-pipeline e2e PASS; full lifecycle and cost
QA PASS; security re-review PASS after write-back hardening; UX
approved.
- `pnpm exec vitest run packages/db/src/status-card-migrations.test.ts`
— passes; reapplies migrations `0185`–`0189` against an already-migrated
embedded Postgres database.
- `pnpm --filter /db check:migrations` — passes migration numbering and
safety checks.
- `pnpm --filter /db typecheck` — passes.
- Merged current `origin/master` on July 24, 2026 with no conflicts;
migrations `0185`–`0189` remain unclaimed on master.
## Risks
- The feature introduces five database migrations and a new background
update path; all new DDL is repeat-safe after partial application,
migration numbering/safety checks pass, and update execution is
company-scoped and change-gated.
- Natural-language compilation can produce invalid or overly broad
queries; compile provenance, query validation, debug visibility, and
version history make failures inspectable and recoverable.
- Reactive or interval refresh could increase spend; active hours, max
refresh frequency, daily token caps, per-update cost records, and
budget-paused states bound and expose that risk.
- The branch name contains an internal execution identifier because it
is a fixed handoff branch; it was intentionally not renamed or rebased
per the release handoff instructions.
- Overall rollout risk is limited because the route, navigation,
services, and UI are disabled by default behind `enableStatusCards`.
> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.
## Model Used
- OpenAI Codex using GPT-5.5 with reasoning, repository tool use, shell
execution, GitHub CLI, and test/build execution. 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; the fixed execution-workspace
identifier is documented as an authorized handoff exception
- [x] I have run tests locally and they pass, with the one cleanup
timeout passing on focused rerun
- [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 Fable 5 <noreply@anthropic.com>
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - The issue-detail UI shows recovery cards and blocked/parked notices
when a task loses its next step — a run finished with no disposition, a
task is stranded, work is blocked behind other tasks, or an assigned
item sits in the backlog
> - That copy was written in the scheduler's internal vocabulary —
"Corrective wake queued", "Graph Liveness", "lost a live action path",
"the responsible" — which describes Paperclip's internals rather than
the user's situation
> - Users seeing these cards report having no idea what the card means
or what they are supposed to do
> - This pull request rewrites the user-facing copy in plain language
and adds explicit calls to action that match the options in the card's
Resolve menu
> - The benefit is that a non-expert operator can read a recovery or
blocked notice and immediately understand what happened and which action
to take next
## Linked Issues or Issue Description
No public GitHub issue exists for this; describing the problem here
(bug-report format):
- **What happened:** Recovery action cards and blocked notices render
internal jargon, e.g. the headline "Paperclip detected this task lost a
live action path. A recovery owner needs to act.", the chip "Corrective
wake queued", the kind label "Graph Liveness", and phrases like
"Comments still wake the responsible". Status values also appear as raw
code literals (`in_progress`, `todo`).
- **Expected behavior:** These notices should tell a normal user, in
plain language, what happened and what to do next (retry the task, mark
it done, send it for review, or record a blocker).
- **Impact:** Operators stall on tasks that only need a simple
disposition because the UI doesn't tell them that's what is being asked.
Related prior work: #9417 (merged) made the reopen-suppressed blocked
message explicit; this PR extends the same plain-language treatment to
the rest of the recovery and blocked-notice copy.
## What Changed
- Recovery card headlines for `missing_disposition`,
`stranded_assigned_issue`, and `issue_graph_liveness` now say what
Paperclip found and name the concrete next steps ("try the task again,
mark it done, or send it for review") matching the card's Resolve menu.
- The `issue_graph_liveness` kind label "Graph Liveness" is now "Task
Needs Next Step", and the "Wake" metadata row is now "Follow-up".
- Wake-policy chips describe actual behavior: "An agent will be asked to
choose the next step" (was "Corrective wake queued"), "Board will
decide", "Manual follow-up needed", "Repair needed before retry", "Check
scheduled".
- Blocked/waiting/parked notices say "the assignee" instead of "the
responsible" / "responsible agent", and "notify" instead of "wake".
- The still-needs-a-next-step notice drops raw `in_progress` code
literals and keeps a plain-language option list (mark done or cancelled,
send for review, record what is blocking it, delegate follow-up).
- Parked-backlog notice renders "To do / In progress" as plain labels
instead of code literals.
- Component tests updated to pin the new copy and the successful-run
example options.
## Verification
- `cd ui && npx vitest run
src/components/IssueRecoveryActionCard.test.tsx
src/components/IssueBlockedNotice.test.tsx
src/components/IssueAssignedBacklogNotice.test.tsx
src/components/IssueChatThread.test.tsx` — 4 files, 126 tests, all
passing.
- Copy-only review: the diff touches display strings, one label map
entry, and test assertions; no control flow, props, or identifiers
change.
## Risks
- Low risk — user-facing strings and test updates only. No behavior,
API, or schema changes. The only functional surface is that anything
keying off the displayed text (e.g. screenshots, external docs) will
show the new wording.
## Model Used
- Claude (Anthropic) — Claude Fable 5, model ID `claude-fable-5`,
extended thinking enabled, running in Claude Code with 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 (no
docs reference this copy)
- [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
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - The experimental settings page renders one interactive toggle per
feature from the settings API
> - On managed instances some values are enforced by the hosting control
plane, and the API now reports those keys as managed
> - Rendering enforced values as live toggles misleads users: the click
appears to work, and the value silently snaps back
> - This pull request renders managed keys as locked toggles with a
"Managed by Paperclip Cloud" badge and guards the handlers so no PATCH
can be emitted
> - The benefit is UI honesty on managed instances, with self-hosted
responses rendering exactly as before
## Linked Issues or Issue Description
Builds on #10058 — renders the per-key `managedKeys` metadata #10058
adds to settings responses (typing shared from #10058 at rebase).
No public issue exists; `feature_request` template fields:
- **Problem or motivation:** on managed instances users see fully
interactive toggles for settings the control plane enforces; changes
appear to apply and never do, with no explanation.
- **Proposed solution:** disabled toggle + badge + guarded handler
driven by the settings response's managed-key metadata; the ~17 uniform
setting cards are extracted into one shared component with copy,
aria-labels, and patch payloads preserved verbatim.
- **Alternatives considered:** hiding managed settings entirely (users
lose sight of the effective value and why it is fixed); tooltip-only
hints on still-active toggles (doomed PATCHes are still emitted and
stripped server-side).
- **Roadmap alignment:** supports the in-progress "Cloud deployments"
milestone in `ROADMAP.md`.
## What Changed
- When the settings API reports a feature key as managed (`managedKeys`
from the managed-config overlay), the experimental settings page renders
that toggle disabled with a badge and a guarded handler, so a click can
never emit a PATCH. Previously, managed-instance users saw fully
interactive toggles they could never actually change. Self-hosted
responses (no `managedKeys`) render exactly as before.
- The ~17 copy-pasted uniform setting cards are extracted into one
`ExperimentalToggleCard` component with titles, descriptions, footnotes,
aria-labels, and patch payloads preserved verbatim; the two bespoke
cards get inline managed handling (the managed auto-recovery toggle also
cannot open its preview dialog).
- `ui/src/api/instanceSettings.ts` response typing now uses the shared
`InstanceExperimentalSettingsWithManaged` / `ManagedSettingMetadata`
types from #10058; `ui/src/pages/InstanceExperimentalSettings.tsx`
locked rendering + card extraction; tests.
## Verification
- 24 page tests (20 existing unmodified + 4 new: locked badge with no
PATCH while unmanaged keys stay editable; managed auto-recovery opens no
dialog; an open recovery preview closes with no PATCH when a refresh
marks auto-recovery managed; self-hosted unaffected): `pnpm --filter
@paperclipai/ui exec vitest run
src/pages/InstanceExperimentalSettings.test.tsx`
- `pnpm --filter @paperclipai/ui typecheck` clean
## Risks
- Low risk. UI-only change; no server or API behavior changes.
Self-hosted responses carry no `managedKeys`, so the page renders
exactly as before there. The card extraction preserves copy,
aria-labels, and patch payloads verbatim, covered by the 20 pre-existing
page tests passing unmodified.
## Model Used
Claude Fable 5 (`claude-fable-5`), extended thinking, agentic tool use;
independently peer-reviewed by a second AI agent before push
## 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: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - Operators run those agents against paid model providers, so the web
UI has a Costs surface that reports spend and quota utilisation
> - Those figures are drawn as horizontal bars by the shared `QuotaBar`
component (`ui/src/components/QuotaBar.tsx`), consumed by
`BillerSpendCard` and `ProviderQuotaCard`
> - `QuotaBar` renders its fill as a plain `<div>` whose CSS width is
the only encoding of the percentage — no `role`, no value attributes, no
accessible name
> - A screen reader therefore announces nothing at all for these bars,
so the spend and quota numbers they convey are unavailable to
assistive-technology users (WCAG 2.1 SC 4.1.2, Name/Role/Value)
> - The same gap is being closed for the other bars in this area by
#1805 (BudgetPolicyCard) and #1869 (ProviderQuotaCard's inline bars);
`QuotaBar` is the remaining shared component with no ARIA semantics
> - This pull request adds the standard ARIA progressbar attributes to
`QuotaBar`'s fill element, reusing the `label` prop the component
already takes
> - The benefit is that every progress bar on the Costs screen exposes
its name and current value to assistive technology, with no visual or
behavioural change for sighted users
## Linked Issues or Issue Description
No existing GitHub issue — the problem is described in-PR below,
following [`bug_report.yml`](.github/ISSUE_TEMPLATE/bug_report.yml).
Related PRs from the same accessibility sweep (each covers a *different*
component, so these are companions rather than duplicates — all three
are currently open):
- Refs #1805 — ARIA attributes for the BudgetPolicyCard progress bar
- Refs #1869 — ARIA attributes for the ProviderQuotaCard inline bars
**What happened?**
On the Costs screen, the spend/quota bars rendered by `QuotaBar` (via
`BillerSpendCard` and `ProviderQuotaCard`) are non-semantic `<div>`
elements. Screen readers skip them entirely: no role, no value, no label
is announced, so the percentage information is available only visually.
**Expected behavior**
Each bar should be exposed as a progress bar with an accessible name and
its current value — e.g. announced as "Weekly spend: 45%, progress bar".
**Steps to reproduce**
1. Run the app and open the Costs page.
2. Expand any provider or biller card so a quota/spend bar is visible.
3. Navigate to the bar with a screen reader (VoiceOver, NVDA, or Chrome
DevTools → Accessibility pane).
4. Observe that the fill element has no role, no value, and no
accessible name.
**Paperclip version or commit**
Reproduces on `master`; `ui/src/components/QuotaBar.tsx` has carried no
ARIA attributes since the component was introduced.
**Deployment mode**
Not deployment-specific — the missing markup is in the shipped
component. Verified in local dev (`pnpm dev`).
**Agent adapter(s) involved**
Not adapter-specific (core UI).
## What Changed
- `ui/src/components/QuotaBar.tsx`: added `role="progressbar"` to the
fill `<div>`.
- Added `aria-valuenow={Math.round(clampedPct)}` with
`aria-valuemin={0}` / `aria-valuemax={100}`, using the already-clamped
percentage so the reported value can never fall outside 0–100.
- Added an `aria-label` of the form `<label>: <pct>%`, reusing the
existing `label` prop for the accessible name.
- No changes to props, styling, layout, or rendering logic: 1 file, 5
added lines, 0 deleted.
## Verification
- Manual: open Costs → expand a provider/biller card, inspect the bar in
Chrome DevTools → Accessibility pane. The fill node now reports role
`progressbar`, value `45`, min `0`, max `100`, and name "Weekly spend:
45%".
- Manual: with VoiceOver/NVDA, the bar announces "Weekly spend: 45%,
progress bar" instead of being skipped.
- Visual regression check: the bar is unchanged for sighted users — only
ARIA attributes were added, no class or style changes.
- CI (lint, typecheck, build, tests) is green on this branch.
- No unit test is added: the change is a set of static ARIA attributes
on one element, and `QuotaBar` currently has no test file. Happy to add
one if maintainers would like coverage here.
## Risks
Low risk. Presentation-only accessibility metadata on a single element;
no props, state, or styling change, and no other component is touched.
The one debatable point is that the percentage appears in both
`aria-label` and `aria-valuenow`, so some screen readers may announce it
twice; both forms are valid, and the label is kept because it carries
the bar's name alongside the value. Happy to drop the percentage from
the label if reviewers prefer the terser announcement.
## Model Used
<!-- @bluzername: please replace this line with the provider + exact
model ID (and context window / reasoning mode if relevant), or "None —
human-authored". Required by CONTRIBUTING.md. -->
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [ ] 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
- [ ] I have run tests locally and they pass
- [ ] I have added or updated tests where applicable
- [ ] I have updated relevant documentation to reflect my changes (no
docs cover this component's markup)
- [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
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - People increasingly drive Paperclip from a phone, so the UI ships a
mobile layout alongside the desktop one
> - `PageTabBar` is the shared component behind the tab strip on nearly
every detail page — AgentDetail, ProjectDetail, RoutineDetail,
IssueDetail, Inbox, Costs
> - On desktop it renders a Radix `TabsList`, whose `TabsTrigger`s carry
their own accessible names; on mobile it swaps to a native `<select>`
> - That `<select>` had no accessible name at all, so screen readers
announced it only as "popup button" — a user could not tell what the
control switches between
> - Because the component is shared, the gap reproduced on every mobile
page that uses tabs rather than on one screen
> - This pull request adds `aria-label="Page section"` to the mobile
`<select>`
> - The benefit is that mobile screen-reader users get the same
orientation desktop users already get from the tab triggers, from a
one-line change with no visual or behavioral impact
## Linked Issues or Issue Description
No existing public issue covers this, so the problem is described in-PR
following `.github/ISSUE_TEMPLATE/bug_report.yml`:
- **What happened** — On a mobile viewport, the `PageTabBar` `<select>`
had no `aria-label`, no `<label>` association, and no visible text of
its own. VoiceOver/TalkBack announce it as an unlabeled "popup button".
- **Expected behavior** — The control announces what it switches
between, matching the accessible naming the desktop `TabsTrigger`s
already provide.
- **Steps to reproduce** — 1. Open any detail page with tabs (agent,
project, routine, issue). 2. Narrow the viewport to mobile width so the
tab strip collapses to a `<select>`. 3. Focus the `<select>` with a
screen reader. 4. Observe that no purpose is announced.
- **Version / commit** — head `ef92d1c`, branch
`fix/page-tab-bar-mobile-a11y`.
- **Deployment mode** — Any. The change is UI-only and client-side.
**Related prior PR:** #1532 (closed unmerged on 2026-03-23) made this
same one-line change to `ui/src/components/PageTabBar.tsx` as part of a
~100-file batch. This PR is the focused standalone version of that fix.
## What Changed
- Added `aria-label="Page section"` to the mobile `<select>` in
`ui/src/components/PageTabBar.tsx`. One line added; no other files
touched.
## Verification
- **Automated:** `pnpm -C ui test` and the repo CI gates (lint,
typecheck, build) — CI is currently green on `ef92d1c`.
- **Manual:** Open any tabbed detail page, narrow the viewport until the
tab strip becomes a `<select>`, and focus it with VoiceOver (macOS/iOS)
or TalkBack (Android). It now announces "Page section, popup button"
instead of an unlabeled "popup button".
- **Inspector check:** In devtools, the `<select>` node's computed
accessible name is "Page section" (previously empty).
## Risks
Low risk. `aria-label` on a `<select>` is a presentation-free attribute:
it changes nothing about layout, styling, DOM structure, event handling,
or the desktop code path, which is untouched. No migration, no API
change, no new dependency. The only debatable point is wording — "Page
section" is a generic name shared by every call-site (see the note
below).
## Model Used
Not specified by the original author, and not recoverable from the
commit metadata (no `Co-Authored-By` or model trailer on `ef92d1c`).
@bluzername — please replace this line with the provider, model
ID/version, and any relevant capability details, or "None —
human-authored".
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [ ] I have specified the model used (with version and capability
details) — see above; needs the author
- [ ] 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
(`fix/page-tab-bar-mobile-a11y`) and contains no internal Paperclip
ticket id or instance-derived details
- [ ] I have run tests locally and they pass
- [ ] I have added or updated tests where applicable — no test added;
the change is a static attribute with no branching behavior
- [ ] I have updated relevant documentation to reflect my changes — not
applicable
- [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 —
currently 4/5, see below
- [ ] I will address all Greptile and reviewer comments before
requesting merge
---
### Maintainer note on the open Greptile comment
This description was restructured to the repository PR template by a
maintainer; the code and the author's intent are unchanged. Unchecked
boxes above are ones only @bluzername can attest to.
Greptile's one remaining comment asks for a `selectAriaLabel` prop so
call-sites could override the label. We think the hardcoded label is
correct here and match existing practice: shared components whose
meaning is fixed own their label internally (`ThemeToggle.tsx`), while
components whose label depends on the data they render take it as a prop
(`CopyText.tsx`'s `ariaLabel`). This `<select>` always means "which page
section", at every call-site, so a prop no caller would set would be
unused API surface.
## Thinking Path
> - Paperclip is the open source control plane people use to manage
AI-agent companies.
> - Agents already receive selected company secrets through `env.*`
bindings at run launch, but environment injection is ambient,
long-lived, and not suitable for every secret consumer.
> - The existing binding and secret-access-event models already provide
company-scoped authorization and per-resolution audit seams.
> - Agents need an explicit way to discover only the secrets granted to
them and fetch a value on demand without exposing the wider company
catalog.
> - That capability must remain run-bound, preserve low-trust token
carve-outs, and make every value read visible in both security and
operator audit trails.
> - This pull request adds an `access.*` delivery namespace, two
run-bound agent routes, dual audit logging, documentation, and an
operator grants editor.
> - The benefit is least-privilege, revocable, auditable secret access
while preserving existing env injection behavior.
## Linked Issues or Issue Description
No pre-existing public issue. Related work:
- Refs #9797 — existing in-sheet agent access UI that this PR extends to
distinguish env and API delivery.
- Refs #9918 — complementary searchable-agent picker improvement for the
same secrets sheet.
- Refs #9530 — related company-wide metadata catalog proposal; this PR
intentionally exposes only the authenticated run's granted aliases and
values.
**Problem / motivation:** Agents can currently consume secrets only
through process environment injection. This keeps values resident for
the run, does not support on-demand consumers, and cannot provide a
discrete operator-visible activity event for each agent-initiated read.
**Proposed solution:** Treat `company_secret_bindings` as the source of
truth for agent secret grants. Keep `env.KEY` as env delivery and add
`access.ALIAS` for API-only delivery; an env binding also implies read
access because the value is already present in the agent process. Add
run-bound list/fetch endpoints that derive scope from the authenticated
heartbeat run and never accept caller-selected overlays.
**Alternatives considered:** A company-wide agent-readable catalog was
rejected for this value path because it increases reconnaissance and
does not prove a per-secret grant. Reusing the ephemeral
environment-probe resolver was rejected because it lacks binding
enforcement. Approval-gated reads and user-scoped secrets remain
deferred beyond v1.
**Roadmap alignment:** This extends the completed **Secrets Manager with
per-agent access** roadmap capability from launch-time env injection to
explicit run-bound API delivery without duplicating a separate planned
initiative.
## What Changed
- Added `access.*` agent binding validation and a dedicated run-bound
resolver that combines `secrets:read` authorization with binding-context
enforcement.
- Added `GET /api/agents/me/secrets` for minimal granted metadata and
`POST /api/agents/me/secrets/:key/value` for on-demand value fetches
with `Cache-Control: no-store`.
- Preserved the existing denials for low-trust review agents,
task-bridge credentials, and skill-test tokens; standard long-lived
agent API keys cannot call the run-bound routes.
- Added dual audit behavior: value attempts write `secret_access_events`
and `activity_log` (`secret.value.read`), while metadata listing writes
the lighter `secret.access.listed` activity event.
- Kept env compatibility: `env.*` remains injected at launch and also
implies API read for the same bound agent; `access.*` never becomes an
environment variable.
- Added the agent-settings **Secret access** editor plus
delivery-mode/alias surfacing on the Secrets page, with focused UI tests
and tokenized layout styles.
- Updated OpenAPI, shared types, agent-facing skill documentation, and
API reference documentation.
### UI Screenshots
P3 produced and reviewed three screenshots using mock data; images are
intentionally not committed to the repository:
- `secret-access-editor.png` — agent settings grant editor.
- `secret-access-light.png` — Secrets-page delivery surfacing in light
mode.
- `secret-access-dark.png` — Secrets-page delivery surfacing in dark
mode.
The source attachments are retained with the implementation task and
linked in the internal handoff; the public page publisher was
unavailable in the PR-prep runtime.
## Verification
- `pnpm exec vitest run
server/src/__tests__/agent-secrets-routes.test.ts
server/src/__tests__/secrets-service.test.ts
server/src/__tests__/secrets-routes.test.ts
ui/src/lib/secret-delivery.test.ts
ui/src/components/AgentSecretAccessEditor.test.tsx` — 5 files, 122 tests
passed.
- Security follow-up: `pnpm exec vitest run
server/src/__tests__/agent-secrets-routes.test.ts
server/src/__tests__/secrets-service.test.ts` — 2 files, 73 tests passed
after active-run and version-consistency fixes.
- Final-head CI: all feature, typecheck, build, e2e, security, and
review gates pass; `General tests (server (1/3))` remains red after one
rerun because unrelated `heartbeat-retry-scheduling.test.ts` cleanup
deletes `heartbeat_runs` before referenced `activity_log` rows.
- `pnpm --filter @paperclipai/server typecheck` — passed.
- `pnpm --filter @paperclipai/shared typecheck` — passed.
- `pnpm --filter @paperclipai/ui typecheck` — passed.
- `pnpm check:token-gates` — feature-local arbitrary-value violations
fixed; command still reports five unchanged `#9627` literals outside
this PR.
- End-to-end QA passed all eight acceptance criteria: grant/list, fetch,
dual audit, env-implies-read, denial matrix, revocation, UI rendering,
and env-injection regression. Evidence:
https://github.com/paperclipai/paperclip/pull/9921#issuecomment-5027455492
- Security review returned PASS-with-required-changes; the
implementation uses the required dedicated binding-enforcing resolver,
run-bound JWT restriction, run-derived overlays, minimal metadata, and a
resolver redaction-registration hook. Evidence:
https://github.com/paperclipai/paperclip/pull/9921#issuecomment-5027455382
## Risks
- A compromised agent can exfiltrate any secret explicitly granted to
it; explicit company-scoped/run-scoped grants, revocation, and audit
reduce but cannot remove that inherent capability risk.
- The resolver invokes a redaction-registration hook before returning
values, but the current route has no persistent cross-request per-run
redaction registry. Paperclip-owned later comments/events therefore
cannot yet guarantee automatic scrubbing of a deliberately copied
fetched value; QA classified this as non-blocking residual hardening.
- Audit-event insertion currently fails open if the security-event
insert itself fails; the operator activity event provides partial
redundancy, but a future hardening change should define fail-closed
behavior for value delivery.
- This PR overlaps `ui/src/pages/Secrets.tsx` with #9918 and may require
a straightforward rebase after that PR moves.
> 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.3-codex`, with reasoning, repository tool use,
terminal execution, Paperclip API access, and GitHub CLI capabilities.
Context-window size is not exposed by the runtime.
- Anthropic Claude Opus 4.8 with 1M context and tool use assisted with
the UI implementation commit.
## 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: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - Connections is the subsystem that defines which external apps and
MCP-style integrations operators can browse, configure, and run
> - The v3 schema core in #9958 added stable connection identities, auth
metadata, and grant-aware contracts, but the app catalog still used the
older gallery shape
> - The product needs a richer, typed AppDefinition catalog so browsing
and setup can render provider-specific auth and configuration
requirements consistently
> - This pull request moves the Wave 1 app catalog onto generated
AppDefinition data and carries that shape through shared types, server
lookup paths, and app connection UI
> - The benefit is that follow-up runtime and wizard work can build
against one catalog contract instead of local-only mock/gallery data
## Linked Issues or Issue Description
Refs #9958.
No public GitHub issue exists for this branch. This is the catalog layer
for the Connections v3 stack after the schema-core foundation in #9958.
## What Changed
- Adds generated AppDefinition data for the Wave 1 catalog and ingestion
reporting.
- Replaces the legacy tool app gallery exports with
AppDefinition-centered shared contracts, validators, and tests.
- Updates server tool-access lookup behavior to use the AppDefinition
catalog.
- Updates app connection UI surfaces and tests to consume
AppDefinition-backed catalog data.
- Documents the catalog ingestion workflow in the connector playbook.
## Verification
- `pnpm run preflight:workspace-links`
- `pnpm exec vitest run packages/shared/src/app-definitions.test.ts
packages/shared/src/app-definitions-url.test.ts
ui/src/pages/apps/AppsConnect.test.tsx
server/src/__tests__/tool-access-service.test.ts`
## Risks
- Medium: this changes the catalog contract used by shared, server, and
UI app connection surfaces.
- Catalog data quality matters because generated definitions now drive
browse/setup display.
- Follow-up runtime and wizard PRs must rebase on this branch or on
master after this lands.
> 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 GPT-5 Codex coding agent with repository 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
- [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>
## Thinking Path
> - Paperclip is the open source control plane people use to manage
AI-agent companies and their governed access to external systems.
> - Connected Apps build on the existing Apps and MCP gateway substrate
so companies can configure reusable, auditable integrations.
> - The current connection record does not yet have a stable public
address, explicit ownership/auth method fields, or subject-specific
credential grants.
> - Without that schema core, later OAuth, per-user authorization, token
brokering, triggers, and connector-service phases cannot enforce tenant
and subject boundaries consistently.
> - This pull request adds the forward-compatible Connections v3 schema
core while preserving the existing connection lifecycle and directly
migrating the remote MCP transport name.
> - The benefit is a company-scoped, least-privilege foundation for
one-click integrations without bypassing Paperclip secrets, profiles,
rules, or audit controls.
## Linked Issues or Issue Description
No matching public issue was found.
**Problem**
Paperclip's current app connections need a durable identity and
authorization substrate before Connected Apps can safely support
multiple setup methods, per-user credentials, provider tenants, and
managed connector services. The existing schema only models a single
connection-level credential set and uses legacy transport terminology.
**Proposed solution**
Add a stable company-scoped connection UID, explicit
ownership/auth/transport fields, a subject-aware `connection_grants`
table, and multi-key credential annotations. Backfill existing
connections and workspace grants in a reversible migration, then update
shared/server/UI contracts to the new `mcp_remote` transport name.
**Related work**
- Related foundation: #9534
- Roadmap: Connected Apps (one-click integrations)
## What Changed
- Added company-scoped connection `uid`, `ownership`, `authKind`, and
canonical transport fields across database, shared contracts,
validators, services, and UI fixtures.
- Added `connection_grants` with workspace/user subject rules, provider
tenant metadata, credential secret refs, revocation state, company
scoping, and uniqueness constraints.
- Added migration `0182_connections_v3_schema_core` to backfill stable
UIDs, rename `remote_http` to `mcp_remote`, infer auth kinds, create
default workspace grants, and support rollback coverage.
- Added multi-key credential annotations and updated gateway/access
services without changing the existing lifecycle behavior.
- Updated the connection glossary, connector playbook, and security
threat model for the new identity, grant, and relay boundaries.
- Added explicit test UIDs to direct database fixtures so the new
non-null invariant is exercised across affected server suites.
## Verification
- `pnpm --filter @paperclipai/shared typecheck`
- `pnpm --filter @paperclipai/db typecheck`
- `pnpm --filter @paperclipai/server typecheck`
- `pnpm --filter @paperclipai/ui typecheck`
- `pnpm exec vitest run server/src/__tests__/tool-access-service.test.ts
server/src/__tests__/tool-gateway-service.test.ts
server/src/__tests__/tool-gateway.test.ts
server/src/__tests__/heartbeat-runtime-skills.test.ts
server/src/__tests__/tool-oauth-legacy-backfill.test.ts
server/src/__tests__/tool-access-policy-service.test.ts
server/src/__tests__/heartbeat-runtime-mcp-servers.test.ts
packages/db/src/connections-v3-schema-core-migration.test.ts
packages/shared/src/validators/tool-access.test.ts --config
vitest.config.ts` — 9 files, 218 tests passed.
- Latest-head GitHub Actions: build, typecheck, general/serialized
suites, backup/worktree restore coverage, both e2e shards, canary,
policy, and security scans pass.
- Greptile: 5/5 with zero unresolved threads.
- `pnpm check:token-gates` remains red only on five pre-existing `#9627`
color literals outside this change.
## Risks
- **Migration risk:** UID backfill and default-grant creation touch
every existing connection. The migration uses company-scoped uniqueness,
deterministic legacy UIDs with ID suffixes, and seeded up/rollback
coverage.
- **Authorization risk:** Grant rows carry credential references.
Constraints enforce workspace-vs-user subject shape, company/connection
lookup indexes, one default grant per connection, and one user grant per
connection/subject. Security review is requested specifically for this
design.
- **Compatibility risk:** `remote_http` is renamed directly to
`mcp_remote`; all repository call sites and fixtures are updated in the
same change.
- **Future-phase risk:** Subject-bound token issuance, triggers, and
connector-service relay verification remain fail-closed requirements
documented for later phases; this PR does not expose those capabilities.
> 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 CLI coding agent. The runtime did not expose an exact
underlying model ID or context-window size; capabilities used include
repository inspection, code editing, shell execution, test execution,
Git/GitHub CLI operations, and structured 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>
Bumps
[radix-ui](https://github.com/radix-ui/primitives/tree/HEAD/packages/react/radix-ui)
from 1.6.0 to 1.6.4.
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/radix-ui/primitives/blob/main/packages/react/radix-ui/CHANGELOG.md">radix-ui's
changelog</a>.</em></p>
<blockquote>
<h2>1.6.4</h2>
<ul>
<li>Fixed a regression where importing primitives from the root
<code>radix-ui</code> entry point erased every primitive's types to
<code>any</code>.</li>
</ul>
<h2>1.6.3</h2>
<h3>Dialog</h3>
<ul>
<li>Fixed broken ARIA references in Dialogs where title or description
elements are not rendered.</li>
</ul>
<h3>Slider</h3>
<ul>
<li>Fixed a bug where <code>onValueCommit</code> was not called when a
slider thumb was dragged across another thumb.</li>
</ul>
<h3>Toast</h3>
<ul>
<li>Fixed <code>Toast</code> removing non-focused toasts when pressing
<code>Escape</code>.</li>
</ul>
<h3>Tooltip</h3>
<ul>
<li>Fixed a bug where <code>Tooltip.Content</code> children were mounted
to the DOM twice.</li>
</ul>
<h3>Other updates</h3>
<ul>
<li>Fixed overriding inline animation style in
<code>Popper.Content</code>.</li>
<li>Improved tree-shaking so bundlers can drop unused components.
Component parts are now marked <code>/* @__PURE__ */</code> and use
named render functions instead of <code>Component.displayName =
...</code> assignments, which previously prevented dead-code elimination
with some bundlers.</li>
<li>Widened <code>virtualRef</code> prop type to allow
<code>RefObject<Measurable | null></code> in popover
components.</li>
<li>Fixed dev-only checks with conditional exports to drop dev-warnings
from production builds.</li>
<li>Added per-primitive subpath entry points so each primitive can be
imported directly, eg. <code>import { Accordion } from
'radix-ui/accordion'</code> or <code>import * as Accordion from
'radix-ui/accordion'</code>. This mirrors the namespaced exports
available from the root <code>radix-ui</code> entry point.</li>
<li>Fixed a bug where updating a <code>Checkbox</code>,
<code>Switch</code>, or <code>RadioGroup</code> value programmatically
(eg. a "select all" control) while inside a
<code><form></code> would dispatch a <code>click</code> event from
the hidden bubble input that propagated to ancestor <code>onClick</code>
handlers.</li>
</ul>
<h2>1.6.2</h2>
<h3>Other updates</h3>
<ul>
<li>Added CSS custom properties for Navigation Menu item indicators'
translate values.</li>
<li>Fixed a bug in Dismissable Layer causing background nested popovers
to close all layers on outside click</li>
<li>Fixed runtime errors for <code>Form.Message</code>,
<code>Form.Control</code>, <code>Form.Label</code> and
<code>Form.ValidityState</code> that are correctly rendered outside of
<code>Form.Field</code> components</li>
<li>Fixed a bug in form control components to ensure their values are
updated when their associated form's is reset. This affects
<code>RadioGroup</code>, <code>Slider</code>, <code>Select</code>, and
<code>Switch</code>.</li>
<li>Fixed menu items, tab triggers, toolbar links, and select items
intercepting <code>Space</code>/<code>Enter</code> keys that originate
from focusable descendants.</li>
<li>Fixed a bug where calling an event handler without an argument would
throw, preventing successive event handlers from being called. This
affected all components that accept event handlers with internal
implementations.</li>
<li>Fixed a bug in Context Menu to ensure that the menu properly
re-anchors to the latest pointer position when re-triggered in its open
state.</li>
<li>Fixed stale <code>onEscapeKeyDown</code>/<code>onDismiss</code>
handlers on React 19.2.</li>
<li>Fixed items in a Roving Focus Group not being auto-focused on mount
within a Focus Scope component.</li>
<li>Fixed a regression in Dismissable Layer originating from a <a
href="https://redirect.github.com/react/react/pull/34831">bug in React's
<code>useEffectEvent</code></a>.</li>
<li>Fixed <code>--radix-scroll-area-corner-width</code> and
<code>--radix-scroll-area-corner-height</code> not resetting to
<code>0</code> when a corner is removed. Previously these values would
stick around and leave a permanent gap on the remaining scrollbar.</li>
<li>Fixed a bug in Slider where stepping with the keyboard would skip a
valid value when the current value is off the step grid. Stepping now
snaps to the next step-aligned value in the direction of travel,
matching native <code><input type="range"></code>
behavior.</li>
</ul>
<h2>1.6.1</h2>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li>See full diff in <a
href="https://github.com/radix-ui/primitives/commits/HEAD/packages/react/radix-ui">compare
view</a></li>
</ul>
</details>
<details>
<summary>Attestation changes</summary>
<p>This version has no provenance attestation, while the previous
version (1.6.0) was attested. Review the <a
href="https://www.npmjs.com/package/radix-ui?activeTab=versions">package
versions</a> before updating.</p>
</details>
<br />
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
## Thinking Path
> - Paperclip is the open-source app people use to manage AI agents for
work
> - The `ui` package contains React components that drive the agent
scheduling and workspace configuration UX
> - Dependabot PR #9895 bumped `@radix-ui/react-*` from 1.6.0 → 1.6.4,
which changed the internal effect-scheduling order inside Dialog and
Select primitives
> - Two `workspaces-a` CI tests started failing:
`RoutineRunVariablesDialog` and `editable-sections / TriggersSection`
> - Root cause for both: radix 1.6.4's changed scheduling pushes a
cascaded state update one render-tick later, and each test asserted
before that tick landed
> - This PR fixes the two affected surfaces at the source (one
production fix, one test fix) so the radix bump can land cleanly
> - The benefit is unblocking PR #9895 without compromising test
fidelity or production correctness
## Linked Issues or Issue Description
Refs #9895 — this PR fixes the two `workspaces-a` test failures that
blocked the radix-ui 1.6.0 → 1.6.4 Dependabot bump.
**Root cause:** radix-ui 1.6.4 changed the internal effect-scheduling
order inside its Dialog and Select primitives, pushing certain cascaded
state updates one render-tick later than before. Two tests each asserted
against the intermediate state, before the deferred tick landed.
**Affected tests (both now pass at radix 1.6.0 AND 1.6.4):**
1. `editable-sections / TriggersSection` — `ScheduleEditor`
disabled-button assertion
2. `RoutineRunVariablesDialog` — workspace branch propagation assertion
Production behavior is unchanged and correct in both cases (verified via
tracing).
## What Changed
- **`ui/src/components/ScheduleEditor.tsx`** — `onValidityChange` is now
called synchronously inside the custom-cron `onChange` handler, not only
via the passive `useEffect` below. Previously there was a one-render
window where an invalid draft still read as valid to the parent (button
enabled); this closes that window. The effect call is preserved as a
safety net for other entry paths; only the `onChange` path is new.
- **`ui/src/components/RoutineRunVariablesDialog.test.tsx`** — the
post-mount settle loop now waits for the branch value to actually appear
in an `<input>` (`value === "pap-1634-routine-branch"`) rather than
exiting as soon as the workspace card mounts. The card reports its
branch name through an effect callback that triggers a follow-up render;
the old loop exited one tick too early. Iteration cap raised from 10 →
20 to give the extra tick room.
This PR intentionally does **not** bump radix-ui — that stays in #9895.
## Verification
```sh
# TypeScript — clean at radix 1.6.0 (master):
tsc -p ui/tsconfig.json
# Targeted vitest:
npx vitest run ui/src/components/RoutineRunVariablesDialog.test.tsx
npx vitest run ui/src/components/editable-sections
npx vitest run ui/src/components/ScheduleEditor
# Full ui suite — green with radix 1.6.4 installed locally (371 files / 3035 tests):
npx vitest run --project ui
# check-forbidden-tokens — clean
```
All of the above pass at **both** radix 1.6.0 (current master) and
1.6.4.
## Risks
Low risk. The production change (`ScheduleEditor.tsx`) adds a
synchronous call to an already-injected `onValidityChange` prop — same
value, earlier in the same event cycle. No new state, no new effects, no
API changes. The test change tightens an assertion (waits longer, checks
a more specific condition) rather than relaxing one.
## Model Used
Claude Sonnet (Anthropic) — Paperclip agent workflow; model family
claude-sonnet-4-x with tool use and extended reasoning 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: Harold Kim <harold@paperclip.ing>
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip orchestrates AI agents for zero-human companies, and
humans onboard into companies via invite links.
> - Some invite types (`company_join` with
`requires_company_admin_approval`) require an admin to approve the join
request after the invitee submits it.
> - While the requester waits, the invitee sees the
`AwaitingJoinApprovalPanel` in `InviteLanding`, which describes where
the admin needs to go to approve the request.
> - That panel rendered the destination — "Company Settings → Access" —
as two clickable `<a href="/company/settings/access">` links, even
though the surrounding copy is plainly addressed to the admin ("Ask
**them** to visit ..."), not the requester.
> - First-time invitees naturally click the only underlined link on the
screen, are sent to `/company/settings/access`, hit the "No company
access" panel (they have no membership yet), and conclude the invite
flow is broken.
> - This PR removes the navigation by rendering both "Company Settings →
Access" references as plain styled text (`<p>` / `<span>`), so the
guidance stays visible but cannot be followed by the requester.
> - The benefit is that the post-submit invite experience matches the
copy's intent — guidance for the admin, not navigation for the
requester.
Fixes#6784.
## What Changed
- `ui/src/pages/InviteLanding.tsx` — In `AwaitingJoinApprovalPanel`,
replace the two `<a href={approvalUrl}>Company Settings → Access</a>`
elements with `<p>` and `<span>` containing the same text. Remove the
now-unused `approvalUrl` constant.
- `ui/src/pages/InviteLanding.test.tsx` — Update the existing "pending
approval page" test: it previously asserted two anchor tags pointing at
`/company/settings/access`; it now asserts **zero** anchors while the
text "Company Settings → Access" still appears twice (in the "Approval
page" box and inline in the "Ask them to visit ..." sentence). Renamed
the test description from "...linked access instructions" to
"...non-clickable access instructions" to reflect the contract.
## Verification
```
pnpm vitest run ui/src/pages/InviteLanding.test.tsx
```
Result: 8 / 8 pass, including the updated "shows the pending approval
page with the company icon and non-clickable access instructions" case.
Manual reproduction (master @ `242a2c2f`,
`deploymentMode=authenticated`, `bind=lan`, embedded Postgres):
1. As instance admin, generate a `company_join` invite that requires
admin approval.
2. In a fresh browser profile, open the invite link.
3. Fill **Create your account** and submit.
4. The "Request to join \<company\>" panel appears.
5. Hover the "Company Settings → Access" mentions — no underline, no
link cursor; clicking does nothing. The text is still readable and the
surrounding copy ("Ask them to visit ...") still conveys the instruction
to the requester.
Before / after screenshots: see issue #6784 — the "before" state lands
users on `/company/settings/access` which renders "No company access".
After this PR the guidance is informational only.
## Risks
Low. UI-only change confined to one function in `InviteLanding.tsx` plus
its matching test. No API contracts, routes, or data shapes are
modified. The removed `approvalUrl` constant was only referenced by the
two anchor elements.
## Model Used
- Anthropic Claude Opus 4.7 (`claude-opus-4-7`, 1M context, extended
thinking enabled).
- Tools: file editing, Bash, Playwright reproduction against a
self-hosted Paperclip instance running master @ `242a2c2f`, and the
Paperclip monorepo's own Vitest suite for verifying the test update.
## 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 run tests locally and they pass
- [x] I have added or updated tests where applicable
- [ ] If this change affects the UI, I have included before/after
screenshots (will attach in PR thread)
- [x] I have updated relevant documentation to reflect my changes (no
docs files needed updating)
- [x] I have considered and documented any risks above
- [x] I will address all Greptile and reviewer comments before
requesting merge
Bumps [react-i18next](https://github.com/i18next/react-i18next) from
17.0.9 to 17.0.10.
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/i18next/react-i18next/blob/master/CHANGELOG.md">react-i18next's
changelog</a>.</em></p>
<blockquote>
<h2>17.0.10</h2>
<ul>
<li>fix(warnings): the <code>useTranslation</code> and
<code>Trans</code> "You will need to pass in an i18next
instance" warnings now match the <code>useSSR</code> wording,
mentioning the props/context alternatives and the most common
unexplained cause at scale: duplicate react-i18next copies in monorepo
setups. The <code>Trans</code> variant also referenced the internal
<code>i18nextReactModule</code> name; it now points to the public
<code>initReactI18next</code> API.</li>
<li>feat(warnings): development-only warning
(<code>SUSPENDED_WHILE_LOADING</code>, logged once) right before
<code>useTranslation</code> suspends while translations are loading.
With the default <code>useSuspense: true</code> and no
<code><Suspense></code> boundary this previously surfaced as a
blank screen or a cryptic React error; the warning now names both fixes
(add a <code><Suspense></code> boundary or set
<code>react.useSuspense: false</code>). No-op in production builds; the
<code>process.env.NODE_ENV</code> check is wrapped so runtimes without a
<code>process</code> global (raw ESM in the browser, some edge runtimes)
stay silent instead of throwing.</li>
<li>ci: weekly workflow typechecking the test suite against
<code>@types/react@next</code> / <code>@types/react-dom@next</code>, so
the next React major's type changes (like the React 18
<code>TFunctionResult</code>/children wave) surface before user
reports.</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="3b71c2766c"><code>3b71c27</code></a>
17.0.10</li>
<li><a
href="57c3500e6a"><code>57c3500</code></a>
build</li>
<li><a
href="c62476f4d1"><code>c62476f</code></a>
chore: sync package-lock with i18next ^26.2.0 devDependency bump</li>
<li><a
href="0126bd1cad"><code>0126bd1</code></a>
improve instance warnings (monorepo hint) + dev-only suspense warning +
weekl...</li>
<li>See full diff in <a
href="https://github.com/i18next/react-i18next/compare/v17.0.9...v17.0.10">compare
view</a></li>
</ul>
</details>
<br />
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
<details>
<summary>Dependabot commands and options</summary>
<br />
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
</details>
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
## Thinking Path
> - Paperclip is the open source control plane people use to manage AI
agents and their work
> - Operators rely on task detail properties and header actions to scan
linked resources and make quick decisions
> - External GitHub objects used a long pull-request label and repeated
mention counts that added visual noise without adding state
> - The task-detail star action also rendered as a labelled outline
button, unlike the compact star controls used elsewhere
> - These inconsistencies made dense task-detail surfaces slower to scan
and broke Paperclip's content-first visual language
> - This pull request shortens the GitHub pull-request label, removes
duplicate mention-count decoration, and aligns the detail star action
with the icon-only control pattern
> - The benefit is a calmer, more consistent task-detail experience with
accessible labels preserved for assistive technology
## Linked Issues or Issue Description
No matching public GitHub issue or open pull request was found.
**What happened?**
On the task detail surface, linked GitHub pull requests were labelled
`Github Pull Request` and could show a repeated `×N` mention count. The
detail-header star control used a labelled outline button rather than
the compact icon-only star pattern.
**Expected behavior**
Linked pull requests should use the concise `Github PR` label without
duplicate mention-count decoration, and the detail star action should
render as an accessible icon-only ghost button consistent with
neighboring controls.
**Steps to reproduce**
1. Open a task with a linked GitHub pull request mentioned more than
once.
2. Inspect the external-object property label and value row.
3. Inspect the star action in the task detail header.
**Paperclip version or commit:** `230126d80b` (`master` at preparation
time)
**Deployment / installation:** Local development, built from source.
**Scope:** Core UI; not adapter-specific, database-related, or
configuration-related.
## What Changed
- Render GitHub pull-request property labels as `Github PR`.
- Remove repeated external-object mention-count decoration from property
values.
- Render detail-header star controls as icon-only ghost buttons while
preserving `aria-label`, pressed, busy, error, and tooltip states.
- Expand component coverage for concise labels, duplicate-count
suppression, visual variants, and accessible star actions.
## Verification
- `pnpm --filter @paperclipai/ui exec vitest run
src/components/IssueProperties.test.tsx
src/components/StarToggle.test.tsx` — 54 tests passed.
- `pnpm --filter @paperclipai/ui typecheck` — passed.
- Visual review (before/after plus normal, starred, pending, and error
states):
45cb492185/star-toggle-review.html
- `pnpm check:token-gates` — reports five existing `#9627` violations in
files unchanged by this PR; the same values are present on `master`.
## Risks
- Low risk: changes are limited to task-detail presentation and tests.
- The star action remains fully accessible through its existing ARIA
label and tooltip, but it no longer displays visible text.
- External-object mention counts remain available in data; only the
redundant property-row decoration is removed.
> 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.3 Codex, tool-enabled coding agent with repository
and shell access.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have 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>
## Thinking Path
> - Paperclip is the control plane operators use to coordinate AI-agent
companies and review work needing attention.
> - The Inbox is the operator-facing surface that aggregates tasks
requiring attention across server state and shared client polling.
> - Archiving a task optimistically removed it, but ordinary background
activity and stale polling responses could make it reappear seconds
later.
> - The server therefore needs to distinguish genuine user-attention
events from routine agent/system activity.
> - The client also needs a bounded local archive guard across every
Inbox query path while the server mutation and in-flight polls settle.
> - This pull request fixes both resurrection paths and adds
race-focused regression coverage.
> - The benefit is stable archive behavior without hiding a genuine
archive failure after reconciliation or reload.
## Linked Issues or Issue Description
### Pre-submission checklist
- [x] Searched existing open and closed issues and pull requests; no
duplicate implementation was found.
- [x] Reproduced on `master` before this branch.
- [x] Confirmed this is a Paperclip core bug, not adapter or provider
behavior.
### What happened?
Archiving an Inbox task hid it optimistically, then background refresh
activity could insert it back into the list seconds later.
### Expected behavior
A successfully archived task remains hidden during normal polling. A
genuine failed archive may become visible again after reconciliation or
reload.
### Steps to reproduce
1. Open Inbox with a visible task.
2. Archive the task.
3. Wait for shared polling or routine agent activity to refresh task
data.
4. Observe the archived task reappear without a hard page refresh.
### Paperclip version or commit
`master` before this branch.
### Deployment mode
Built from source using the local development application.
### Installation method
Built from source (`pnpm`).
### Agent adapter(s) involved
Not adapter-specific; this is a core Inbox bug.
### Database mode
Not database-mode-specific.
### Access context
Board (human operator).
### Additional context
The failure had independent server and client causes: routine activity
could resurface archived rows server-side, while stale shared-poll
responses could bypass optimistic client removal.
## What Changed
- Restrict server-side Inbox resurfacing to explicit user-attention
events rather than any issue activity write.
- Add a bounded client-side archive guard with confirmation, failure
restoration, and cache reconciliation behavior.
- Apply the guard to Inbox rendering, badge counts, optimistic cache
updates, and shared-poll result application.
- Classify the generic compact Inbox query so stale shared-poll data
cannot bypass the guard.
- Add server visibility-matrix tests and UI race-condition regression
tests.
## Verification
- `pnpm --filter @paperclipai/ui exec vitest run
src/hooks/useSharedPolling.test.ts src/lib/inboxArchiveCache.test.ts
src/pages/Inbox.test.tsx` — 25 passed.
- `pnpm --filter @paperclipai/server exec vitest run
src/__tests__/issues-service.test.ts` — 107 passed.
- Branch rebased cleanly onto current `origin/master` before push.
## Risks
- Low-to-moderate behavioral risk: resurfacing is intentionally
narrower, so the server tests cover human comments, mentions,
interactions, and status transitions that must still regain attention.
- The client guard is bounded and cleared on mutation failure, limiting
the risk of hiding a task whose archive did not persist.
- No schema, migration, public API, workflow, dependency-lock, or
visual-token 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
- Anthropic Claude via Claude Code (`claude_local`; prior
implementation/review run, exact underlying model ID and context window
were not retained in the handoff metadata), with repository tool use and
test execution.
- OpenAI `gpt-5.5` via Codex CLI for final review repair and PR
preparation, with reasoning, repository editing, GitHub tooling, 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/described the result 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 task identifier
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation where needed; no
documentation change is required for this bug fix
- [x] I have considered and documented risks above
- [x] All Paperclip-authored commits include the required co-author
trailer
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - The Secrets UI lets operators navigate slash-delimited secret names
as folders
> - PR #9913 shipped create-in-folder behavior for company and per-user
secrets
> - The production behavior is already on master, but several
create-in-folder interaction paths lack retained regression coverage
there
> - Without that coverage, prefix composition, derived keys, prefix
removal, and staged empty folders could regress unnoticed
> - This pull request adds focused render tests without changing
production behavior
> - The benefit is safer maintenance of the folder-based secrets
workflow with a small, reviewable patch
## Linked Issues or Issue Description
- Refs #9913
## What Changed
- Added render coverage for creating a company secret from a folder and
deriving its key from the full slash-delimited name.
- Added coverage for per-user secret prefixes and exposing the full name
when the prefix chip is removed.
- Added coverage for inline folder-name validation and URL-backed
staging of an empty folder.
## Verification
- `env -u PAPERCLIP_IN_WORKTREE -u PAPERCLIP_WORKTREE_NAME -u
PAPERCLIP_CONFIG -u PAPERCLIP_HOME -u PAPERCLIP_INSTANCE_ID -u
PAPERCLIP_CONTEXT pnpm exec vitest run
ui/src/pages/Secrets.render.test.tsx` — 29 tests passed.
- `pnpm check:token-gates` — reports five existing `#9627` literals in
unrelated files; this PR changes no token-gated component code and
introduces no new violation.
## Risks
- Low risk: test-only change with no production, API, schema, migration,
dependency, or runtime behavior changes.
- The tests exercise existing DOM interactions and may need updates if
the Secrets creation UI copy or controls intentionally 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 CLI managed coding agent with repository inspection, code
execution, Git/GitHub, and Paperclip API tools. The managed harness does
not expose the exact underlying model ID or context-window size.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [ ] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
— the assigned execution branch name is fixed by the task
- [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 is needed for test-only coverage
- [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>
## Thinking Path
> - Paperclip is the control plane people use to manage AI-agent
companies.
> - Operators need to grant secrets to specific agents safely and
efficiently.
> - The secrets access sheet previously used a native select while
similar agent-assignment surfaces use a searchable picker.
> - That inconsistency makes finding an agent slow and error-prone as
companies grow.
> - This pull request reuses the shared agent picker behavior for
single-agent secret access grants.
> - The benefit is a consistent, searchable selection experience without
changing secret-access semantics.
## Linked Issues or Issue Description
Refs #9797
**Problem / motivation:** The in-sheet agent access form introduced in
#9797 renders every grantable agent in a native select. In companies
with many agents, operators cannot filter by name or title and the
experience differs from other agent-selection surfaces.
**Proposed solution:** Add a single-select variant beside the existing
`AgentMultiSelect`, then use it in the secret access grant form.
**Alternatives considered:** Keeping a native select would preserve less
code but would not scale or align with existing searchable agent
selection.
**Roadmap alignment:** This is a focused usability fix for an existing
core control-plane surface and does not overlap an unstarted roadmap
initiative.
## What Changed
- Added `AgentSelect`, a searchable single-agent popover that filters by
agent name and title.
- Replaced the secret access form's native select with the shared
searchable picker.
- Added focused coverage for filtering, selecting, callback behavior,
and popover closure.
## Verification
- `pnpm exec vitest run ui/src/components/AgentMultiSelect.test.tsx` —
passes (3 tests).
- `pnpm --filter @paperclipai/ui typecheck` — passes.
- `pnpm check:token-gates` — branch adds no violations; the command
currently fails on five unchanged `#9627` literals already present on
`master`.
- Manual: open Secrets, choose a secret, add agent access, filter by
agent name or title, select the result, and grant access.
## Risks
- Low risk: the change is limited to agent selection UI and preserves
the existing grant request payload.
- The new picker depends on the existing popover/input primitives and
resets its filter when closed.
> 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-series coding agent (exact runtime model ID and
context-window size are not exposed), with reasoning, repository tool
use, terminal execution, and GitHub CLI capabilities.
## 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>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - Operators store company and user secrets under human-readable names
> - Slash-delimited names already express useful hierarchy, but the
Secrets page previously rendered them as one flat list
> - Large secret collections therefore became harder to scan, navigate,
and create within consistently named groups
> - A client-derived folder model preserves the existing server contract
while making those names navigable
> - This pull request adds folder browsing, URL-addressable paths,
global search, and create-in-folder behavior without adding folder
records
> - The benefit is a more scalable secrets workflow with no migration or
API compatibility risk
## Linked Issues or Issue Description
Slash-delimited secret names such as `dev/github/oauth/clientid`
currently appear as raw flat rows. This change treats name prefixes as
client-side navigation folders on the main Secrets tab while leaving
stored names, API contracts, validation, and the database unchanged.
- Wireframes and interaction specification:
https://pages.paperclip.ing/pap-14698-secrets-folders/
- Folder paths are derived only from secret names; there is no
server-side folder entity or data-model change.
## What Changed
- Added pure secret-path utilities for normalized segments, breadcrumbs,
nested folder listings, counts, and leaf/path rendering.
- Added a Folders/Flat view to the main Secrets tab with folder-first
sorting, breadcrumbs, empty-folder states, filters, and global search
results.
- Added URL navigation through `?path=<normalized/path>` so deep links,
reload, browser Back, and new-tab folder navigation work.
- Persisted the preferred view in `localStorage` under
`paperclip.secrets.viewMode`; an explicit `?path=` deep link takes
precedence for that visit.
- Added create-in-folder behavior with a removable prefix chip, staged
New folder paths, inline segment validation, and full-name key
derivation.
- Kept My secrets flat while rendering slash-delimited names with muted
paths and emphasized leaves.
- Added unit/render coverage for path helpers, folder navigation, and
create-in-folder behavior.
## Verification
- `pnpm exec vitest run ui/src/pages/secrets/secret-path.test.ts
ui/src/pages/Secrets.render.test.tsx` — 35 tests passed.
- `pnpm -r typecheck` — passed.
- `pnpm test:run` — server suite passed 2,707 tests and UI suite passed
3,022 tests; one unrelated CLI doctor test was environment-sensitive
because this agent inherited AWS variables.
- `env -u AWS_PROFILE -u AWS_ACCESS_KEY_ID -u AWS_SECRET_ACCESS_KEY -u
AWS_SESSION_TOKEN -u AWS_REGION -u AWS_DEFAULT_REGION pnpm exec vitest
run cli/src/__tests__/secrets.test.ts` — 8 tests passed.
- `pnpm build` — passed.
- `pnpm check:token-gates` — feature files are clean; the command
reports five existing `#9627` color literals outside this diff.
## Risks
- Low data risk: folders are derived client-side from existing names,
with no schema, migration, API, or stored-name changes.
- URL behavior changes only the main Secrets tab and uses the additive
`?path=` contract.
- View preference is browser-local and scoped to the
`paperclip.secrets.viewMode` key.
- Renaming or deleting the last secret under an open prefix
intentionally leaves the user on an empty-folder state instead of
redirecting.
- “Move to folder…” bulk prefix rename remains deferred because it is a
multi-secret mutation with separate conflict and partial-failure
semantics.
> 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 `gpt-5.5` via Codex CLI for PR preparation, verification,
GitHub/Paperclip tool use, and codebase analysis; the managed harness
does not expose the active context-window size.
- Anthropic Claude Opus 4.8 (1M context) and Claude Fable 5 assisted
earlier implementation/design commits, as recorded in their commit
trailers; both used repository and code-editing 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)
- [ ] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
— the assigned shared execution branch name is fixed for this work
- [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 Fable 5 <noreply@anthropic.com>
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip is the open source control plane people use to supervise
AI-agent companies.
> - Operators need task execution state to be visible where they read,
reply to, and manage an issue.
> - Scheduled monitor state was easy to miss because it lived in a
description-area card and used inconsistent, mostly static time copy.
> - The task page, composer, retry card, and properties panel therefore
needed one shared monitor-state and countdown language.
> - This pull request adds shared live-ticking time utilities, moves
monitor status into a top-of-page banner and composer strip, and
redesigns the properties row with complete read-only details.
> - The benefit is that operators can immediately understand when an
agent resumes, why it is waiting, and how to act without hunting across
the page.
## Linked Issues or Issue Description
Issue monitors can schedule a future agent check, retry, or wake, but
the UI did not present that state consistently or prominently. The
existing description-area activity card competed with issue content, the
composer did not explain that replying wakes the agent early, compact
properties copy truncated important details, and relative times did not
share a live two-unit formatter.
This change makes scheduled monitor state visible and consistent across
the issue header, reply composer, properties panel, and scheduled-retry
card. Related prior server-side recovery visibility work: #9629
(distinct scope).
## What Changed
- Added shared two-unit monitor ETA/offset formatters and a live-ticking
countdown hook, including compact absolute-time rules for Today,
weekday, and cross-year dates.
- Replaced the description-area monitor activity card with a top-of-page
status banner and added an inline composer strip that explains replies
wake the agent before the scheduled check.
- Redesigned the properties Monitor row as readable two-line copy with
attempt state, due/overdue/cleared wording, click-to-edit behavior, and
a hover/tap details tooltip.
- Adopted the shared two-unit formatting in the scheduled-retry card and
added focused coverage for monitor formatting, state transitions,
visibility, and controls.
- Added the approved wireframe package and published reference:
https://pages.paperclip.ing/pap-14557-monitor-visibility/
## Verification
- `pnpm vitest run ui/src/lib/issue-monitor.test.tsx
ui/src/components/IssueMonitorBanner.test.tsx
ui/src/components/IssueProperties.test.tsx` — 3 files, 74 tests passed.
- `pnpm -r typecheck` — passed.
- `pnpm build` — passed with existing Vite optimization/chunk-size
warnings.
- UX review approved the rendered real components across desktop/mobile,
light/dark, and the scheduled/retrying/due/overdue/cleared/none state
matrix.
- QA passed 6/6 criteria in Chromium, including a live countdown
transition without refresh. Review evidence included the P2 banner
state-matrix screenshot and the P3 properties-row and details-tooltip
screenshots, plus a dark-mode capture.
- `pnpm check:token-gates` currently reports five pre-existing `#9627`
comment literals on `origin/master`; this branch introduces none of
those literals or any new token violation.
## Risks
- Low-to-moderate UI behavior risk: monitor copy and placement change
across several issue surfaces, but all derive from one shared state
builder and focused tests cover the state matrix.
- Countdown rendering wakes once per minute while a visible monitor is
scheduled; the hook is limited to active monitor surfaces and stops when
hidden.
- No database, API contract, migration, telemetry, or authorization
behavior 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.6-sol`; context-window metadata was unavailable
in this runtime; reasoning, repository tool use, command execution, and
test execution enabled. Earlier implementation commits were assisted by
Claude Fable 5 and Claude Opus 4.8 (1M context), as credited in their
commit trailers.
## 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: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip is the open source control plane people use to manage AI
agents and the credentials those agents need for work.
> - The secrets UI is responsible for making credential creation
understandable and for showing where credentials are used.
> - The create flow previously exposed an editable generated key too
early, used an unnatural field order, and rendered uneven provider tab
rows.
> - The detail sheet also lacked an in-context way to see and manage
which agents reference a selected secret.
> - This pull request makes the create dialog follow a predictable
name-to-value flow and adds agent access management directly to the
secret details sheet.
> - The benefit is a clearer secrets workflow with fewer accidental key
edits and less navigation when granting or revoking agent access.
## Linked Issues or Issue Description
**Subsystem affected:** `ui/` — React + Vite board UI
**Problem or motivation:** Creating a secret currently makes its
generated key look immediately editable, places fields in an awkward
keyboard order, and applies provider-tab overrides that produce uneven
rows. After creation, operators cannot inspect or change agent access
from the selected secret's detail sheet.
**Proposed solution:** Generate the key from the path-style name and
keep it read-only until an explicit Edit action; place Value directly
after Name; use the standard tab sizing; and add an Agent access section
that reads and updates `secret_ref` / `user_secret_ref` environment
bindings through agent adapter configuration.
**Alternatives considered:** Keeping access management only on agent
configuration screens was rejected because it hides a secret-centric
question—“which agents can use this?”—and requires repetitive
navigation. Keeping the key always editable was rejected because the
generated value should be the safe default.
**Roadmap alignment:** No matching item was found in `ROADMAP.md`; this
is a focused usability and access-management improvement to the existing
secrets surface.
**Additional context:** GitHub search found no duplicate PR for this
dialog and in-sheet access change. PR #9321 also mentions user-secret
resolution but addresses unrelated skills-route behavior.
## What Changed
- Auto-generate the create-secret key from Name, keep it read-only by
default, and expose an explicit Edit action.
- Use a path-style Name placeholder (`/dev/foo/bar`) and place Value
immediately after Name for natural keyboard navigation.
- Remove tab sizing/whitespace overrides that caused uneven provider-tab
row heights.
- Add an Agent access section to the Details tab that lists referencing
agents and grants or revokes `secret_ref` / `user_secret_ref`
environment bindings in place.
- Cover company secrets and each-user definitions with focused render
tests.
## Verification
- `pnpm exec vitest run ui/src/pages/Secrets.render.test.tsx` — 17/17
passed.
- `pnpm --filter @paperclipai/ui typecheck` — passed.
- `pnpm check:token-gates` — changed files are clean; the
repository-wide command currently reports five pre-existing `#9627`
violations in unrelated files (`Sidebar.tsx`, `Inbox.tsx`,
`IssueDetail.tsx`, `Issues.tsx`, and `Routines.tsx`).
- Additional secrets verification completed during implementation: 35/35
adjacent secrets tests passed, and the flow was checked in a real
browser in light and dark themes.
## Risks
- Agent access mutations update adapter environment configuration, so
malformed legacy env entries could affect how a binding is displayed or
revoked.
- Each-user definitions use `user_secret_ref` rather than `secret_ref`;
focused tests cover selecting the correct binding type.
- No database or API contract changes are included.
> 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
- Anthropic Claude Fable 5 assisted with the implementation using
repository-aware code editing and test execution.
- OpenAI GPT-5.5 via Codex CLI assisted with PR preparation, branch
hygiene, focused verification, GitHub operations, and review/check
loops.
## 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: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - Its web UI is a long-lived single-page app; users leave tabs open
for hours/days
> - We chased multi-GB tab memory growth through several fixes (#9569,
#9624, #9627, #9701) that cut real churn — but the footprint kept
climbing
> - A heap-snapshot diff on a 12-hour tab finally showed the true cause:
of 13.2M heap nodes, **12.1M were `PerformanceMeasure` objects** — React
19.2 emits a `performance.measure()` per component render for its
DevTools "Performance Tracks" and never clears them
> - These are native objects, so `performance.memory` never reported
them (it read a flat ~74 MB while the real heap was ~308 MB), which is
why our earlier heap/DOM sampling looked stable while the footprint
ballooned
> - This pull request periodically clears the User Timing measure
buffer, since nothing in the app consumes it
> - The benefit is that long-lived tabs stop accumulating millions of
native `PerformanceMeasure` objects, eliminating the remaining unbounded
growth
## Linked Issues or Issue Description
No public GitHub issue exists; describing inline per CONTRIBUTING.md →
"Link Issues or Describe Them In-PR", following the bug report template.
This is the root-cause fix for the memory growth chased in #9569 / #9624
/ #9627 / #9701.
**What happened?**
Long-lived browser tabs grew to multiple GB of memory footprint over
hours/days. A heap snapshot of a 12h tab showed **13.2M nodes, of which
12.1M were `PerformanceMeasure`** (vs ~1.0M total nodes on a fresh tab).
Live capture showed **~340 `performance.measure()` calls/sec**, named
after React components with a `detail.devtools` payload — React 19.2's
"Performance Tracks". Nothing ever clears them, so they accumulate
without bound.
**Expected behavior**
A long-lived tab should not accumulate millions of `PerformanceMeasure`
entries. Idle/long-running tabs should hold a bounded footprint.
**Steps to reproduce**
Open the app (React 19.2 production build), leave a tab open with normal
activity, and run `performance.getEntriesByType('measure').length`
periodically — it climbs unbounded (12.3M after ~12h).
`performance.clearMeasures()` drops it to near-zero and frees the
memory.
**Paperclip version or commit**
Branch `fix/react-perf-measure-leak`, off `master` (after #9701).
**Deployment mode**
Local dev (`pnpm dev` build served by the dev instance), web UI. React
19.2.7. Not adapter-specific.
## What Changed
- **`ui/src/lib/perf-measure-reaper.ts` (new)** —
`startPerfMeasureReaper(intervalMs = 10_000)` clears
`performance.clearMeasures()` on a timer and returns a stop function. It
never calls `getEntriesByType('measure')` (which would materialize the
huge buffer). Honors a `window.__paperclipKeepPerfMeasures = true`
opt-out so a developer recording a React Performance Track in DevTools
can keep the entries.
- **`ui/src/main.tsx`** — start the reaper at app boot.
- Only *measures* are cleared — React's tracks pass explicit start/end
times and leave no marks, and the app doesn't use the User Timing API at
all (verified), so nothing else is affected.
## Verification
- **Root cause proven** by heap-snapshot diff: fresh tab ~1.0M nodes vs
12h tab 13.2M nodes, 12.1M of them `PerformanceMeasure`; live capture
showed ~340 measures/sec with `detail.devtools` and React component
names.
- **Fix proven live**: `performance.clearMeasures()` on the aged tab
dropped the buffer from **12,418,266 → 1,500** and reclaimed the memory
(heap `perf.memory` 308 → 269 MB, plus the ~12M native objects, which
are the bulk of the footprint).
- `vitest`: `perf-measure-reaper.test.ts` — interval clearing, `stop()`,
opt-out flag, and no-API no-op. All pass.
- `tsc -b` clean.
## Risks
Very low, client-only.
- Clearing the User Timing measure buffer only affects the DevTools
Performance panel's React track *history*; normal users never consume
it. Developers who want to record it can set
`window.__paperclipKeepPerfMeasures = true`.
- Only `performance.clearMeasures()` is called (not `clearMarks`), so
any mark-based timing elsewhere is untouched; a grep confirmed the app
makes no `performance.mark()/measure()` calls of its own.
- Adds exactly one 10s interval (negligible), and `clearMeasures()` does
not materialize the buffer.
Note: this is a React 19.2 upstream behavior (its performance tracks are
emitted in the production build and never cleared). If React later gates
or clears them, this reaper can be removed.
## Model Used
- **Provider:** Anthropic, via the Claude Code CLI.
- **Model:** Claude Opus 4.8 (`claude-opus-4-8`).
- **Reasoning mode:** Extended thinking enabled.
- **Capabilities used:** tool use (shell, file editing), the Chrome
DevTools MCP to reproduce and profile, and a streaming heap-snapshot
parser to identify the `PerformanceMeasure` accumulation.
## 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 (continues #9701; no duplicates)
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change and contains no internal
Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have considered and documented any risks above
- [ ] I have updated relevant documentation to reflect my changes (N/A —
no user-facing docs; rationale documented inline)
- [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
🤖 Generated with [Claude Code](https://claude.com/claude-code)
## Thinking Path
> - Paperclip is the open source control plane people use to manage
AI-agent companies and supervise governed work.
> - Decisions capture high-value operator judgment, and the
decision-training foundation merged in #9702 freezes that evidence for
later evaluation and learning.
> - Operators still need the UI from the closed stacked PR #9718 to
intentionally capture examples and inspect the resulting dataset.
> - GitHub automatically closed#9718 when its stacked base branch was
deleted after #9702 merged, leaving the server foundation on `master`
without the corresponding UI.
> - This pull request restores the final UI and its still-required
supporting API fields directly on current `master`, while excluding the
obsolete migration and duplicated server-foundation diffs.
> - The benefit is a reviewable replacement PR that preserves the
completed decision-training workflow without replaying stale stack
history.
## Linked Issues or Issue Description
- Refs #9718
- Refs #9702
## What Changed
- Restored the top-level `/training` library and record inspector with
search, filters, JSONL export, notes editing, and evidence tabs.
- Restored the Decisions-row training affordance and capture drawer,
including preview, provenance, deletion, cache refresh, and approval
consistency behavior.
- Restored the shared types and focused server support needed by the UI
without reintroducing decision-training migrations or the already-merged
server foundation.
- Restored focused UI and attention-service tests from the final #9718
state.
- Credit to the authors and reviewers of #9718; this recovery
transplants their final reviewed delta after the stacked base deletion.
## Verification
- `pnpm exec vitest run ui/src/pages/Training.test.tsx
ui/src/components/DecisionTrainingDrawer.test.tsx
ui/src/components/AttentionQueueRow.test.tsx
server/src/__tests__/attention-service.test.ts
server/src/__tests__/decision-training.test.ts` — 5 files, 48 tests
passed.
- `pnpm --filter @paperclipai/shared typecheck` — passed.
- `pnpm --filter @paperclipai/server typecheck` — passed.
- `pnpm --filter @paperclipai/ui typecheck` — passed.
- `pnpm exec vitest run server/src/__tests__/openapi-routes.test.ts` — 3
tests passed; confirms exact route/OpenAPI parity for the restored
preview endpoint.
- `pnpm check:token-gates` — the restored files are clean; the
repository-wide command currently reports five pre-existing false
positives where comments reference GitHub issue `#9627` as if it were a
color literal.
## Risks
- Low migration risk: this PR contains no database migrations and is
based directly on current `master`.
- The main behavioral risk is cache invalidation across Decisions and
Training views; focused tests cover capture, update, deletion, row
state, and approval refresh behavior.
- The token-gate baseline remains red on unrelated `#9627` comment
references; this PR does not modify those files.
> 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.3-Codex, reasoning with repository/tool access and
code execution. Context window not exposed by the runtime.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip is the open source control plane people use to manage
AI-agent companies and their work
> - Human approvals, issue interactions, and execution decisions already
capture high-value decision moments
> - Those moments are currently transient and cannot be reused as stable
evaluation or training examples
> - Reusable examples need a server-owned, immutable snapshot so later
comments or runs cannot leak into the recorded state
> - Human notes need to remain editable and auditable without changing
the captured state
> - This pull request adds the database model, snapshot capture service,
API, export format, and attention-feed enrichment for decision training
> - The benefit is a durable, inspectable foundation for evaluating
whether agents can reproduce good human decisions from only the context
available at decision time
## Linked Issues or Issue Description
### Subsystem affected
Cross-cutting (`server/`, `packages/db`, and `packages/shared`).
### Problem or motivation
Paperclip has no durable dataset for converting human decisions into
evaluation-ready examples. Teams need to capture pending or resolved
decisions with the exact issue context, comments, runs, and repository
evidence available at a cutoff, while preventing future context from
leaking into the example.
### Proposed solution
Store immutable, schema-versioned snapshots anchored to durable
interaction, approval, or execution-decision records; keep notes
separately editable with history; expose human-only CRUD, list, and
JSONL export APIs.
### Alternatives considered
Client-generated snapshots were rejected because they duplicate cutoff
logic and cannot reliably enforce no-leakage boundaries. Automatic
outcome backfill was deferred so captured examples remain faithful to
what was known at capture time.
### Roadmap alignment
Supports the roadmap direction of turning completed work and decision
patterns into reusable organizational knowledge.
### Additional context
The implementation records explicit commit-resolution confidence
(`exact`, `nearest_run`, `workspace`, or `none`) so downstream
evaluation can distinguish evidence quality.
## What Changed
- Added the `decision_training_examples` schema and idempotent migration
with company, issue, and source/author indexes.
- Added shared types for decision-training records, notes history, and
versioned snapshots.
- Added a single server-side snapshot capture path with inclusive
comment cutoffs, pre-cutoff run capture, durable decision payloads, and
explicit commit-resolution confidence.
- Added create, list, detail, notes-only update, delete, and JSONL
export routes with human-only write authorization and activity logging
that skips no-op note submissions.
- Added per-user `trainingExampleId` enrichment to attention items.
- Added focused embedded-Postgres tests for cutoff boundaries,
post-cutoff leakage, immutable snapshots, human-only writes, duplicate
prevention, notes history, attention enrichment, and export shape.
- Updated UI test and Storybook attention-item factories for the new
required `trainingExampleId` contract.
## Verification
- `pnpm exec vitest run server/src/__tests__/decision-training.test.ts`
— 10 tests passed.
- `pnpm --filter @paperclipai/db typecheck` — passed, including
migration numbering and safety checks.
- `pnpm --filter @paperclipai/shared typecheck` — passed.
- `pnpm --filter @paperclipai/server typecheck` — passed.
- `pnpm --filter @paperclipai/ui typecheck` — passed.
## Risks
- The migration adds a new table and indexes only; it does not rewrite
existing rows or install resolve-time hooks.
- Snapshot JSON can grow with long comment threads and run histories; v1
intentionally favors complete, inspectable examples over aggressive
truncation.
- Commit SHA resolution is evidence-based and records `exact`,
`nearest_run`, or `none` so downstream consumers can account for
confidence.
- The API is additive, but future UI work must continue to treat the
snapshot as immutable and use notes-only updates.
> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.
## Model Used
- OpenAI Codex using `gpt-5.3-codex`, with repository tool use, terminal
execution, and code-editing capabilities; context-window size is not
exposed by the runtime.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [ ] 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>
## Thinking Path
> - Paperclip is the open source control plane people use to organize,
govern, and understand AI-agent work
> - Operators need concise, current status views across projects and
execution workspaces without manually reading every issue and run
> - Paperclip already has auditable issues, documents, built-in agents,
routines, and live run events, but no first-class summary-slot workflow
connecting those systems
> - A built-in Summarizer can generate status prose through ordinary
governed tasks while summary slots provide stable, revisioned
destinations for that output
> - The UI needs to show current summaries, generation progress,
failures, revisions, and streaming draft status in the places operators
already work
> - This pull request adds the end-to-end summary-slot data, API, agent,
orchestration, and UI surfaces behind an experimental setting
> - The benefit is decision-oriented status context that remains
company-scoped, auditable, retryable, and inexpensive by default
## Linked Issues or Issue Description
No public GitHub issue exists for this feature.
**Problem**
Operators currently have to reconstruct project and workspace status by
reading many issues, runs, and comments. This makes it hard to identify
decisions, review queues, recent work, and the next event worth
watching.
**Proposed capability**
Add an experimental summary system with revisioned summary slots for
projects and workspaces, a paused-by-default built-in Summarizer agent,
governed generation tasks, live draft status, and reusable UI cards.
**Expected behavior**
- Summary data remains company-scoped and revisions remain auditable.
- Generation runs through normal issue/agent orchestration and
deduplicates active requests.
- Only the linked built-in Summarizer generation task can author a slot
revision.
- Operators can generate, retry, inspect revisions, and follow draft
progress from project and workspace views.
- The feature remains opt-in and background generation remains paused by
default.
## What Changed
- Added summary-slot schema, idempotent migrations, shared contracts,
validators, API paths, and service tests.
- Added company-scoped summary-slot routes for reading revisions,
requesting generation, and guarded Summarizer writes with activity
logging.
- Added terminal generation finalization, failure reasons, assignment
wakeups, and orchestration integration.
- Added the paused-by-default built-in Summarizer bundle, low-cost
runtime defaults, status-summarization skill, and stale-summary routine.
- Added summary cards, revision selection, retry/configuration states,
live draft streaming, transcript chunk handling, and project/workspace
integrations.
- Updated Claude local parsing for streamed status output and expanded
server, adapter, shared, database, catalog, and UI coverage.
## Verification
- `pnpm -r typecheck`
- `pnpm exec vitest run packages/db/src/summary-slots-schema.test.ts
packages/shared/src/summary-slot.test.ts
server/src/__tests__/summary-slot-routes.test.ts
server/src/__tests__/summary-slots.test.ts
server/src/__tests__/built-in-agents.test.ts
ui/src/components/SummarySlotCard.test.tsx
ui/src/components/SummarySlotCard.status.test.tsx
ui/src/components/useSummaryDraftStream.test.tsx
ui/src/lib/summary-draft-stream.test.ts
ui/src/lib/run-log-chunks.test.ts
ui/src/context/LiveUpdatesProvider.hook.test.tsx` — 113 tests passed
- `pnpm test:run` — server and UI suites passed; one CLI AWS doctor test
was affected by inherited `AWS_ACCESS_KEY_ID`/`AWS_SECRET_ACCESS_KEY`,
and passed when those host credentials were removed
- `pnpm exec vitest run cli/src/__tests__/secrets.test.ts` with
inherited AWS credential variables removed — 8 tests passed
- `pnpm build`
- `pnpm check:token-gates` currently reports nine `#9627` comment
references introduced by current `master`; none are in this PR diff
## Risks
- Database risk is limited by incrementally ordered, idempotent
migrations and migration safety checks.
- Summary generation creates normal issues/runs, so misconfiguration can
produce failed slots; the UI exposes retryable failure reasons and agent
configuration entry points.
- Streaming draft parsing depends on the documented `STATUS:` protocol;
final persisted revisions remain the source of truth.
- The feature is experimental, opt-in, and its built-in routine is
paused with no background token spend by default.
> 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 GPT-5.3 Codex with reasoning, repository tool use, code
execution, GitHub CLI, and Paperclip control-plane integration. Earlier
branch commits also record Claude model co-authorship where applicable.
## 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: Claude Opus 4.8 <noreply@anthropic.com>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - The Inbox lets operators scan task state and distinguish unread
activity at a glance
> - Read and unread rows should keep the same status-and-title alignment
so the list remains easy to scan
> - The mark-read dot previously participated in flex layout, which
added an extra leading column and visibly indented unread rows
> - Parent rows also needed special handling because their collapse
chevron occupies the normal leading gutter
> - This pull request moves the unread dot out of desktop flex flow,
preserves a consistent leading spacer, and positions parent-row dots
after the chevron
> - The benefit is consistent alignment for read, unread, leaf, nested,
and collapsible parent rows without hiding tree controls
## Linked Issues or Issue Description
### What happened?
In the Inbox, an unread row rendered its status icon and title farther
right than an equivalent read row because the mark-read dot occupied its
own flex column. On a collapsible parent row, the dot also competed with
the leading chevron.
### Expected behavior
Read and unread Inbox rows keep identical status/title alignment while
preserving the unread affordance and any tree-expansion controls.
### Steps to reproduce
1. Run Paperclip from `master` and open the Inbox at desktop width.
2. Compare otherwise-equivalent read and unread leaf rows.
3. Expand a task tree containing an unread depth-zero parent.
4. Observe the unread row indentation and the dot competing with the
parent chevron.
### Paperclip version or commit
Reproduced against the pre-fix `master` parent of this PR.
- **Deployment mode:** Local dev (`pnpm dev`).
- **Installation method:** Built from source.
- **Agent adapters involved:** Not adapter-specific; this is a core
Inbox UI bug.
- **Database mode:** Not database-related.
- **Access context:** Board (human operator).
- **Additional context:** No logs or configuration are involved; the
regression is visual layout behavior covered by focused component/page
tests.
## What Changed
- Render the desktop unread dot as an absolute overlay so it does not
consume row width; retain the existing in-flow behavior on mobile.
- Add an `unreadDotPlacement` option so depth-zero collapsible parents
place the dot after the leading chevron.
- Reserve the same leading spacer for read and unread non-chevron Inbox
rows.
- Expand `IssueRow` and Inbox tests to cover leaf alignment, nested
rows, parent chevrons, fading state, and mobile behavior.
## Verification
- `pnpm exec vitest run ui/src/components/IssueRow.test.tsx
ui/src/pages/Inbox.test.tsx` — 2 files, 30 tests passed.
- `pnpm check:token-gates` — all three token gates clean across 630
scanned files.
- Browser QA (desktop 1280px and mobile 375px) — PASS: read/unread
desktop content measured at identical x positions; nested guides, parent
chevron hit target, fading state, and mobile mark-read hit targets
verified.
## Risks
- Low risk: the change is isolated to Inbox row presentation and has
focused regression coverage.
- The main visual risk is breakpoint-specific placement of the mark-read
dot; tests explicitly cover desktop absolute positioning and mobile
in-flow positioning.
> 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`, high reasoning effort, with
repository/tool execution. The runtime did not expose a context-window
size.
- Original implementation commit also records assistance from Claude
Opus 4.8.
## 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: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip is the open source control plane people use to coordinate
AI-agent work.
> - Execution workspaces provide the local service loop where operators
start, stop, restart, inspect, and open workspace services.
> - The existing header exposed those actions through separate controls
whose position and labeling changed across runtime states.
> - That movement made the most common development actions harder to
scan and easier to misclick, especially with multiple services or long
URLs.
> - The design therefore uses one fixed-geometry, state-aware control
bar and keeps service-specific detail behind a compact disclosure.
> - This pull request adds that control surface, maps existing runtime
data and every pending mutation into it, and integrates it into the
execution-workspace header without changing server contracts.
> - The benefit is a calmer, predictable service-control loop across
stopped, transitional, running, unhealthy, failed, multi-service, and
narrow-width states.
## Linked Issues or Issue Description
### Subsystem affected
`ui/ — React + Vite board UI`
### Problem or motivation
Execution-workspace service actions move and change shape as runtime
state changes, while URLs and multi-service status compete for header
space. During bulk actions, operators also need every targeted service
to show its transitional state immediately.
### Proposed solution
Use one fixed-geometry, state-aware service control bar in the workspace
header. Map existing runtime records into a stable status, URL, and
actions model, and track each in-flight bulk request independently until
it settles.
### Alternatives considered
Keeping the separate quick-control buttons was rejected because their
geometry changes by state. Showing every service inline was rejected
because it makes the header too wide; per-service detail remains in a
compact disclosure and the Services tab.
### Roadmap alignment
Reviewed `ROADMAP.md`; this focused execution-workspace UI improvement
does not duplicate a listed roadmap initiative.
### Additional context
The published design and state viewer is available at
https://pages.paperclip.ing/pap-14233-workspace-service-controls/.
## What Changed
- Added `WorkspaceServiceControlBar`, a fixed-geometry responsive
control for single- and multi-service runtime states.
- Added 15 Storybook states covering running, stopped, transitions,
unhealthy, failed, disabled, long-URL, mobile, and multi-service
behavior.
- Replaced `WorkspaceRuntimeQuickControls` in the execution-workspace
header with adapters that map live services and all pending requests
into the new control model.
- Added focused unit coverage for service-entry construction, bulk
pending overlays, request resolution, clipboard feedback, and header
integration.
## Verification
- `cd ui && NODE_ENV=development pnpm vitest run
src/components/WorkspaceServiceControlBar.test.tsx
src/components/WorkspaceRuntimeControls.test.tsx
src/pages/ExecutionWorkspaceDetail.test.tsx` — 29 tests passed.
- `NODE_ENV=development pnpm --dir ui typecheck` — passed.
- `pnpm check:token-gates` — all token gates clean.
- Reviewed the Storybook captures for all primary states.
## Risks
- Low-to-moderate UI risk: service controls depend on adapter mapping
from existing runtime records; focused tests cover single-service and
bulk-action mapping and integration paths.
- Multi-service bulk actions intentionally apply to all eligible
services, while per-service actions remain in the disclosure.
> 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, exact model ID `gpt-5.3-codex`; runtime-managed context
window; coding/reasoning mode with repository, terminal, Git, GitHub
CLI, and test execution 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: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - The issue detail chat lets operators send messages while an issue
run is live
> - Messages sent during a live run are shown as queued and can expose
an interrupt action
> - The UI only treated running runs as interruptible, even though
queued runs can also own the pending message
> - That mismatch hid the interrupt button after sending a message while
the agent run was still queued
> - This pull request treats queued and running issue runs as
interruptible and preserves the exact target run on optimistic and
persisted comments
> - The benefit is that operators can immediately interrupt the queued
run their message is waiting behind
## Linked Issues or Issue Description
- **What happened:** Sending a message while an issue-owned agent run
was in `queued` state showed the message as pending but did not make the
interrupt action available.
- **Expected behavior:** A message queued behind either a queued or
running issue run should retain that run as its interrupt target and
expose the interrupt control.
- **Steps to reproduce:** Open an in-progress issue with an issue-owned
run still queued, send a chat message, and inspect the queued message
actions.
- **Version/commit:** Reproduced against the pre-change `master` UI
behavior.
- **Deployment mode:** Paperclip board UI with a queued issue execution
run.
## What Changed
- Generalized issue-run resolution from running-only to
queued-or-running interruptible runs.
- Used the interruptible run consistently for optimistic queue metadata,
persisted comment decoration, cancel controls, and targeted
interruption.
- Added a regression test that sends a message behind a queued run and
verifies the exact run is cancelled.
## Verification
- `pnpm exec vitest run ui/src/pages/IssueDetail.test.tsx` — 44 tests
passed.
- `pnpm --filter @paperclipai/ui typecheck` — passed.
- `pnpm check:token-gates` — repository-wide gate currently reports nine
pre-existing `#9627` comment literals; this patch adds no token literals
or gate violations.
## Risks
- Low risk: the behavior change is limited to selecting queued
issue-owned runs as valid interrupt targets in the existing chat flow.
- Cancellation remains targeted by run ID, and the regression test
verifies the queued run ID is preserved through optimistic and persisted
comment states.
> 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 `gpt-5.4` via Codex CLI; context-window size is not exposed by
this runtime; reasoning-enabled with repository, shell, GitHub CLI, and
code-execution 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>
## Thinking Path
> - Paperclip is the open source control plane people use to manage
AI-agent companies and their work
> - The inbox is a per-user attention view, so archiving an item must
not alter the underlying issue, assignment, or status
> - Agents can help responsible users tidy resolved work only when the
action is company-scoped, reversible, policy-controlled, and fully
attributable
> - The database and authorization foundations landed in #9654 and
#9658, but the end-to-end archive routes, audit details, agent workflow
guidance, and operator UI still need to ship together
> - Separate stacked PRs #9659 and #9661 made the complete behavior
harder to review and land as one coherent capability
> - This pull request consolidates the remaining server,
shared-contract, documentation, skill, and UI work on top of current
master
> - The benefit is a single reviewable change that lets agents safely
archive responsible-user inbox items and lets users control or undo that
behavior
## Linked Issues or Issue Description
### Subsystem affected
Cross-cutting inbox management across shared contracts, server
authorization/routes/services, shipped agent skills, and the board UI.
### Problem or motivation
Agents may complete work whose issue remains in the responsible user's
Mine inbox. Existing board-user archive behavior does not provide the
agent-facing policy endpoints, target resolution, heartbeat-run
attribution, typed denials, conservative workflow guidance, or UI needed
for safe agent-managed cleanup.
### Proposed solution
Allow authorized agents to archive or unarchive responsible-user inbox
items under the user's open, allowlist, or disabled policy; preserve
actor/agent/run attribution in issue detail and activity records; expose
policy controls and agent archive attribution in the UI; and document
conservative cleanup rules for agents and PR gardening.
### Alternatives considered
- Reuse generic issue mutation permissions: rejected because inbox state
belongs to a target user and requires user-scoped authorization.
- Automatically archive every completed or closed item: rejected because
completion signals can still require human review or a decision.
- Keep the backend and UI as separate stacked PRs: superseded by this
consolidated PR so the complete user-visible behavior can be reviewed
and verified together.
### Related work
- Builds on merged foundations #9654 and #9658.
- Supersedes the remaining stacked changes in #9659 and #9661.
- `ROADMAP.md` has no overlapping inbox archive or inbox authorization
initiative.
## What Changed
- Added shared inbox-agent policy types and validators plus
company-scoped self-service policy routes and OpenAPI coverage.
- Enabled agent archive/unarchive mutations with responsible-user
targeting, policy enforcement, typed failures, attribution, idempotency,
and detailed activity auditing.
- Returned agent archive attribution in issue detail and documented
reversible inbox cleanup semantics in the implementation spec and
Paperclip skill.
- Added conservative PR-gardening inbox tidy guidance that keeps GitHub
access read-only and avoids archiving work that still needs human
action.
- Added the Profile settings policy control and Issue Properties
attribution/unarchive UI with focused component coverage and narrow-pane
handling.
## Verification
- `pnpm exec vitest run
server/src/__tests__/inbox-archive-routes.test.ts
server/src/__tests__/inbox-agent-policy-routes.test.ts
server/src/__tests__/authorization-service.test.ts
server/src/__tests__/openapi-routes.test.ts
ui/src/components/InboxAgentPolicyControl.test.tsx
ui/src/components/IssueProperties.test.tsx` — 110 passed.
- `pnpm --filter @paperclipai/db exec vitest run
src/inbox-archive-agent-policies-migration.test.ts` — 1 passed.
- `node --test
.agents/skills/pr-gardening/scripts/pr-gardening.test.mjs` — 9 passed.
- `pnpm --filter @paperclipai/server typecheck` — passed.
- `pnpm --filter @paperclipai/ui typecheck` — passed.
- `pnpm check:token-gates` — changed files are clean; the
repository-wide command currently reports nine unrelated pre-existing
`#9627` literals outside this PR's diff.
## Risks
- Agent inbox mutations broaden an existing endpoint path, so
authorization and target resolution must remain fail-closed; focused
route and authorization tests cover allowed and denied paths.
- Archive state affects only the responsible user's inbox presentation
and remains reversible; it does not mutate issue status, assignment, or
visibility.
- The UI policy defaults to the existing open behavior, while allowlist
and disabled modes can reduce agent access.
- This PR intentionally builds on #9654 and #9658 and contains no new
migration number or modification to an already-applied migration.
> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.
## Model Used
- OpenAI Codex using GPT-5.4, medium reasoning, repository tool use,
shell execution, code review, 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
(`feat/inbox-agent-archive-complete`) 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: Claude Opus 4.8 <noreply@anthropic.com>
## Thinking Path
> - Paperclip is the control plane where operators configure and inspect
AI-agent work.
> - An issue can override the assigned agent's primary model for that
task.
> - The issue-properties UI labeled that per-task setting as `Custom ·
<model>`, which could be read as a property of the model rather than a
replacement of the agent default.
> - Operators need the UI to distinguish the agent's primary model from
an issue-specific override at both the collapsed summary and selection
point.
> - This pull request renames the lane presentation to `Override` and
adds concise provenance text without changing the stored lane value or
adapter configuration behavior.
> - The benefit is that operators can immediately understand which model
will run and why it differs from the agent default.
## Linked Issues or Issue Description
No matching public GitHub issue was found. Related implementation work:
#9700 moved Codex ACPX model configuration to startup and is already on
`master`; #9355 also addresses ACP session-config rejection behavior.
Those PRs concern execution behavior, while this PR is limited to
clarifying the issue-level override UI.
Bug report details:
- **What happened?** The issue-properties Model row displayed `Custom ·
<model>` for a task-level
`assigneeAdapterOverrides.adapterConfig.model`. Operators could misread
`Custom` as describing the model itself and could not see that the value
replaced the agent's primary model for this issue.
- **Expected behavior:** The UI should explicitly identify a task-level
model as an override and explain that it replaces the agent's primary
model for the issue.
- **Steps to reproduce:** Configure an agent with a primary model, set a
different model on an issue, and inspect the Model row and model picker
in Issue Properties.
- **Paperclip version or commit:** Reproduced on the pre-change branch
derived from current `master`.
- **Deployment mode:** Local development or any deployment using the
board UI.
- **Installation method:** Built from source.
- **Agent adapters involved:** Adapter-agnostic; any adapter exposing
model selection.
- **Database mode:** Not database-related.
- **Access context:** Board operator viewing issue properties.
- **Relevant logs or output:** Not applicable; this is a presentation
ambiguity.
- **Relevant config:** An issue-level
`assigneeAdapterOverrides.adapterConfig.model` differing from the
assigned agent's primary model.
- **Additional context:** The internal lane identifier remains `custom`;
only user-facing copy and explanatory text change.
- **Privacy checklist:** No private instance links, internal ticket IDs,
secrets, usernames, or local paths are included.
## What Changed
- Renamed the collapsed issue model label from `Custom · <model>` to
`Override · <model>` and added a provenance tooltip.
- Renamed the model-picker lane from `Custom` to `Override` and added
explanatory subtext at the selection point.
- Updated the Issue Properties component test to assert the new label.
- Added an isolated Storybook fixture for the task model override state
so visual review has a stable target.
## Verification
- `pnpm exec vitest run ui/src/components/IssueProperties.test.tsx` — 43
tests passed.
- `pnpm --filter @paperclipai/ui typecheck` — passed.
- `git diff --check public-gh/master...HEAD` — passed.
- `pnpm check:token-gates` — the changed files are clean, but the
repo-wide command currently reports nine unrelated `#9627` comment
references already present on `master` as color literals.
## Risks
- Low risk: this changes display strings and explanatory copy only;
override storage, lane identifiers, API contracts, and execution
behavior are unchanged.
- The global token-gate false positive may also appear in CI until the
unrelated `#9627` references on `master` are allowlisted or the scanner
ignores issue-number comments.
> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.
## Model Used
OpenAI Codex coding agent assisted this PR preparation using medium
reasoning, repository-aware shell execution, Git/GitHub tooling, and
local test/typecheck execution. The hosted runtime did not expose an
exact model ID or context-window size to this session.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [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>
## Thinking Path
> - Paperclip is the open source control plane people use to organize
and govern AI-agent companies
> - Company skills are durable resources that users browse, import,
assign, and maintain over time
> - A flat skill list plus tags does not provide a stable location or
hierarchy for personal, company, project-imported, and bundled skills
> - Folder paths need to be canonical, company-scoped, safe to move, and
preserved across re-imports without changing skill IDs
> - The `/skills` UI also needs traversal, breadcrumbs, move/create
flows, and a dedicated My Skills namespace that work on desktop and
mobile
> - This pull request adds the folder data model and APIs, reserved-root
lifecycle, project import behavior, and the folder-first skills
experience
> - The benefit is a predictable filesystem-like organization model
while tags remain available for cross-cutting classification
## Linked Issues or Issue Description
Refs #9619 — the reviewed folder foundation was intentionally closed and
folded into this combined feature PR.
Refs #9026 — earlier flat-folder attempt superseded by this integrated
implementation.
Refs #3281 — related skill organization proposal; this PR uses canonical
persisted folders rather than deriving groups from skill keys, and does
not add hidden-skill behavior.
**Feature request**
- **Problem:** Skills currently lack a canonical hierarchical location,
making personal skills, project imports, bundled skills, and
company-authored skills difficult to traverse and manage at scale.
- **Proposed behavior:** Add nested company-scoped folders with stable
paths, reserved My/Projects/Bundled roots, subtree queries, safe
move/create operations, and a folder-first `/skills` library UI.
- **Import behavior:** New project scans file skills under
`projects/<project-slug>`; later imports update content without
overriding a user-selected folder.
- **Alternatives considered:** Tags alone remain useful for
cross-cutting classification, but they do not provide canonical
location, nesting, reserved namespaces, or stable import placement.
- **Roadmap alignment:** Extends the completed Skills Manager and
Scheduled Routines capabilities without duplicating an active roadmap
item.
## What Changed
- Adds `folders` persistence for routine and skill folders, nested
canonical paths, parent/slug/system-key fields, migration backfills, and
reapply-safe migrations `0174`–`0175` after current master migrations.
- Adds company-scoped folder CRUD, cycle/depth/namespace validation,
reserved My/Projects/Bundled lifecycle, item moves, subtree filtering,
and folder paths on skill results.
- Preserves project-import placement: first import files into the
project folder, while re-import keeps user-owned placement and stable
skill IDs.
- Adds the `/skills` folder tree rail, tags facet, breadcrumbs,
subfolder browser, move/new-folder dialog, canonical detail location,
inline tag editing, and folder-aware Studio creation.
- Keeps bundled skills read-only even when their source metadata is
incomplete by detecting the reserved Bundled folder and hiding
selection/move actions.
- Extends routine folder UI and OpenAPI coverage, and adds regression
tests across migrations, services, routes, tree helpers, pages, and
Studio creation.
## Verification
- `pnpm exec vitest run
packages/db/src/nested-skill-folders-migration.test.ts
server/src/__tests__/folders-routes.test.ts
server/src/__tests__/folders-service.test.ts
server/src/__tests__/company-skills-service.test.ts
server/src/__tests__/routines-service.test.ts
ui/src/components/folders/FolderControls.test.tsx
ui/src/components/folders/SkillFolderTree.test.tsx
ui/src/components/folders/skill-folder-tree.test.ts
ui/src/pages/CompanySkills.test.tsx ui/src/pages/Routines.test.tsx
ui/src/pages/SkillStudio.test.tsx
ui/src/lib/company-skill-routes.test.ts ui/src/lib/skill-create.test.ts`
— 13 files, 192 tests passed.
- `pnpm exec vitest run ui/src/pages/CompanySkills.test.tsx
ui/src/components/folders/SkillFolderTree.test.tsx` — 2 files, 20 tests
passed after preserving the existing PR's bundled-skill fixes.
- `pnpm -r typecheck` — passed for all workspace packages.
- `pnpm test:run` — passed in an isolated CI-like environment with
inherited Paperclip runtime identity and static AWS credential variables
removed.
- `pnpm build` — production build passed for all workspace packages.
- Greptile iteration 2 — 5/5 confidence with zero unresolved threads on
commit `ff2d67aa71`.
- Latest-head GitHub checks — all success, neutral, or skipped; PR is
mergeable with a clean merge state.
- `pnpm check:token-gates` — reports nine existing `#9627` comment false
positives already present on `master`; this PR introduces no new token
violation.
## Risks
- **Migration/backfill:** `0174` creates the foundation and `0175` adds
nested/reserved semantics. Both are ordered after current master
migration `0173`, are covered by numbering/safety checks, and are
designed to be reapply-safe.
- **Reserved namespaces:** My, Projects, and Bundled roots are
service-managed. Regression coverage prevents namespace squatting,
cross-company folder use, bundled writes, cycles, and excessive depth.
- **Behavioral change:** Project scans choose a project folder only on
initial creation; existing skills deliberately retain their current
folder during refresh.
- **UI scope:** The folder rail applies to the Installed library;
Catalog retains the discovery-oriented category sidebar.
> 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 `gpt-5.5` in Codex CLI, medium reasoning mode; runtime did not
expose a context-window value. Used repository/file tools, terminal
execution, Git/GitHub operations, test execution, and code editing.
## 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>
Fixes#3759
## Thinking Path
> - Paperclip orchestrates AI agents and issue workflows, so the comment
composer has to stay stable under normal typing.
> - The affected subsystem is the shared markdown comment editor used
across issue and workflow surfaces.
> - That editor decorates mention links after Lexical updates the
editable DOM.
> - The current mention decoration effect recreates its
`MutationObserver` whenever `value` changes, which happens on every
keystroke.
> - That observer also reacts to the DOM mutations produced by mention
decoration itself, creating unnecessary observer churn in Chrome.
> - This pull request keeps one observer instance alive, batches
decoration work into `requestAnimationFrame`, and disconnects the
observer while decoration writes run.
> - The benefit is lower observer churn while preserving the existing
mention chip behavior.
## What Changed
- Removed `value` from the mention-decoration observer effect dependency
list so the observer is not recreated on every external value update.
- Batched mention decoration with `requestAnimationFrame` and
temporarily disconnected the observer while DOM decorations are applied
to avoid self-triggered feedback loops.
- Added a regression test that verifies external value changes do not
recreate the mention decoration observer.
## Verification
- `pnpm install --frozen-lockfile`
- `pnpm --filter @paperclipai/ui exec tsc --noEmit`
- `pnpm --filter @paperclipai/ui exec vitest run
src/components/MarkdownEditor.test.tsx` currently fails before test
collection on the existing repo baseline with `TypeError: undefined is
not an object (evaluating 'z.string')` from
`packages/shared/src/adapter-type.ts`
## Risks
- Low risk. The change is scoped to the mention decoration observer
lifecycle and keeps the existing decoration logic intact.
- The main behavior change is deferring decoration to the next animation
frame instead of running immediately on every observed mutation.
## Model Used
- OpenAI Codex, GPT-5-based coding agent with local tool use in the
Codex CLI 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)
- [ ] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [ ] If this change affects the UI, I have included before/after
screenshots
- [ ] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] I will address all Greptile and reviewer comments before
requesting merge
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - Its web UI keeps live views fresh with a live-events websocket plus
React Query, and #9627 began replacing polling with event-sourcing
(pushing data into the cache)
> - After the churn fixes (#9624) and #9627 shipped, a long-lived tab's
memory footprint was still climbing, so I re-profiled with the Chrome
DevTools MCP
> - The dominant remaining churn is React Query re-arming a polled
query's `refetchInterval` timer on **every** observer notification — and
our live-event handlers `setQueryData(liveRuns(companyId))` on nearly
every event, so each pushed update re-arms every `liveRuns` observer's
timer (the sidebar is always mounted)
> - #9627 event-sourced the live-runs data but left the now-redundant
`refetchInterval` in place, so we did half the fix — the poll is pure
waste and the thing re-arming timers
> - This pull request removes `refetchInterval` from the event-sourced
company live-runs queries so the frequent cache writes have no timer to
re-arm
> - The benefit is that the steady-state timer churn on the
most-observed resource collapses, so the off-heap footprint stops
climbing
## Linked Issues or Issue Description
No public GitHub issue exists; describing inline per CONTRIBUTING.md →
"Link Issues or Describe Them In-PR", following the bug report template.
Continues #9569 / #9624 / #9627.
**What happened?**
With the earlier fixes deployed, a browser tab left open on the app kept
growing its memory footprint. MCP profiling showed ~100+ `setInterval`
create/clear cycles per 5s on an aged tab (vs ~16 fresh), all from React
Query's refetch-interval timers being re-armed on every `setQueryData`
to the frequently-written `liveRuns(companyId)` query.
**Expected behavior**
A resource whose data is pushed (event-sourced) should not also poll;
cache writes should not repeatedly re-arm interval timers. Idle tabs
should hold a bounded footprint.
**Steps to reproduce**
Open a tab with agents streaming, leave it open, and instrument
`setInterval`/`clearInterval`: the churn rate climbs and traces to
`QueryObserver.updateTimers` (`refetchInterval`) for `liveRuns`,
re-armed by every live-event cache write.
**Paperclip version or commit**
Branch `perf/drop-live-runs-refetch-interval`, off `master` (after
#9627).
**Deployment mode**
Local dev (`pnpm dev`), web UI. Core UI live-updates plumbing; not
adapter-specific.
## What Changed
- Set `refetchInterval: false` on every site that polls the **plain**
`queryKeys.liveRuns(companyId)` query (event-sourced by #9627):
`Sidebar`, `SidebarAgents`, `Issues`, `Inbox`, `IssueDetail`
(companyLiveRuns), `ProjectDetail` (×2), `Routines`,
`ExecutionWorkspaceDetail`, `bridge-init`.
- Removed the now-unused `useVisibilityRefetchInterval` interval
vars/imports in `Issues`, `Inbox`, `IssueDetail`.
- **Left variant-key sites polling on purpose** — `Agents` page
(`[...liveRuns, "agents-page"]`) and `ActiveAgentsPanel` (`[...liveRuns,
scope, …]`) are NOT event-sourced by #9627 (different exact cache key),
so dropping their poll would make them stale. Those are a later phase.
- Freshness for the converted queries now comes from event-sourcing
(#9627) + its reconnect reconcile; the initial mount fetch and cross-tab
publish (`usePublishSharedQueryData`) still happen.
## Verification
- MCP profiling identified the churn: the single churning callback is
React Query's `refetchInterval` timer, re-armed by
`setQueryData(liveRuns)` on live events.
- `vitest`: all affected suites pass (`Sidebar`, `SidebarAgents`,
`Issues`, `Inbox`, `IssueDetail`, `ProjectDetail`, `Routines`,
`ExecutionWorkspaceDetail`, `LiveUpdatesProvider`) — 153 tests.
- Updated two `SidebarAgents` linger-window tests: they advanced fake
timers to the *exact* linger-expiry boundary and had relied on
poll-induced re-renders to flush. The linger self-schedules its own
`setTimeout`, so the tests now cross the boundary with a small margin +
an explicit flush (no product change).
- `tsc -b` clean.
- End-to-end footprint reduction should be re-measured against a rebuilt
bundle with the same instrumentation.
## Risks
Low, client-only.
- `liveRuns(companyId)` freshness now depends entirely on event-sourcing
+ reconnect reconcile (both from #9627). If an event path is missed, the
reconnect handler refetches once; durable replay is a planned later
phase.
- Variant-key run lists (Agents page, ActiveAgentsPanel) are unchanged
and still poll, so they don't regress.
- Issue-scoped run queries (`issues.liveRuns/activeRun/runs`) are
**not** touched here — they aren't event-sourced yet and are a separate
phase.
## Model Used
- **Provider:** Anthropic, via the Claude Code CLI.
- **Model:** Claude Opus 4.8 (`claude-opus-4-8`).
- **Reasoning mode:** Extended thinking enabled.
- **Capabilities used:** tool use (shell, file editing), and the Chrome
DevTools MCP to re-profile the live instance and pinpoint the
`refetchInterval` timer churn.
## 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 (a perf/plumbing change)
- [x] I have searched GitHub for duplicate or related PRs and linked
them above (continues #9627; no duplicates)
- [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 considered and documented any risks above
- [ ] I have updated relevant documentation to reflect my changes (N/A —
no user-facing docs; rationale documented inline)
- [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
🤖 Generated with [Claude Code](https://claude.com/claude-code)
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - The control plane records a successful-run handoff when productive
work ends without a durable next-step disposition
> - That handoff state was derived only from the latest activity event,
without checking whether a corrective run or wake was currently alive
> - As a result, actively progressing issues could still show a
high-severity missing-disposition alarm and blocked-inbox row
> - The same stale required event could also remain indefinitely when a
later successful run correctly skipped recovery because another valid
continuation path already existed
> - This pull request makes the derived state liveness-aware, suppresses
attention only while the live path exists, and resolves stale required
events on valid-path skips
> - The benefit is that productive work stays calm while genuine stalls
still resurface automatically when liveness disappears
## Linked Issues or Issue Description
- **Bug:** An issue whose latest successful-run handoff event is
`required` continues to report a missing disposition even while a
heartbeat run, scheduled retry, or queued/deferred/claimed wake is
actively targeting that issue.
- **Expected behavior:** The API should expose current continuation
liveness, the blocked inbox should suppress the alarm only while that
path remains live, and a later successful run that skips recovery
because a valid path exists should durably resolve the stale event.
- **Related but distinct:** #9370 changes disposition freshness at
detection time; #8748 adds an explicit policy opt-out. This PR preserves
detection/escalation policy and fixes read-time/current-liveness state.
## What Changed
- Extended `SuccessfulRunHandoffState` with `hasLiveContinuation` and
optional `liveRunId` evidence.
- Added bounded liveness hydration for required handoff states using
active heartbeat-run and wake-request signals.
- Suppressed `missing_disposition` blocked-inbox rows only while a run,
scheduled retry, or live wake targets the issue.
- Added durable `issue.successful_run_handoff_resolved` logging when
handoff detection skips because another valid continuation path owns the
next action.
- Added focused regressions for live/absent derived state, self-healing
attention suppression, valid-path skip classification, and
resolved-event logging.
- Updated UI normalization and fixtures for the shared contract without
changing rendering behavior.
## Verification
- `pnpm --filter @paperclipai/shared typecheck`
- `pnpm --filter @paperclipai/server typecheck`
- `pnpm --filter @paperclipai/ui typecheck`
- `pnpm vitest run
server/src/services/recovery/successful-run-handoff.test.ts
server/src/__tests__/issue-list-assignee-filter-routes.test.ts
server/src/__tests__/issue-blocker-attention.test.ts` — 56 passed
- `pnpm vitest run
server/src/__tests__/heartbeat-process-recovery.test.ts -t "queues one
finish-handoff wake when a successful run leaves in-progress work
without a next action"` — 1 passed
- `git diff --check`
## Risks
- Low risk: no schema or migration changes, and detection, bounded
correction attempts, and escalation behavior are unchanged.
- Liveness lookups are limited to issues whose latest handoff state is
`required`; blocked-inbox suppression reuses rows already loaded by that
query path.
- Suppression is read-time and self-healing: when the run or wake stops,
the alarm returns on the next fetch.
> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.
## Model Used
- OpenAI Codex using `gpt-5.4`, tool-enabled software-engineering
workflow with repository, shell, test, Git, GitHub, and Paperclip
control-plane access. Context-window size is not exposed by this
runtime.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have 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>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - The Inbox is the primary triage surface; it supports both mouse
hover and `j`/`k` keyboard navigation over a flat, expandable list of
rows
> - Hover and keyboard selection are meant to be a single shared
"cursor": hovering a row and then pressing `j`/`k` should continue from
the hovered row, not jump elsewhere
> - A prior hover-perf rewrite moved the hovered row into a numeric
`hoveredIndexRef` and nulled it whenever the list reshaped; because the
inbox polls constantly, any refresh between hovering and pressing a key
dropped the hovered index
> - With the hovered index dropped, the next keypress fell back to
selection index `0`, stranding the cursor at the top of the list instead
of continuing from the hovered row
> - This pull request tracks the hovered row's stable identity (a nav
key) alongside the numeric index and re-anchors it by key across
reshapes, mirroring the existing keyboard-selection reconciliation
> - The benefit is that mouse hover and keyboard navigation stay in sync
exactly as intended, even while the inbox is polling
## Linked Issues or Issue Description
**Bug report**
- **What happened:** In the Inbox, hovering a row with the mouse and
then pressing `j`/`k` (or another keyboard shortcut) does not continue
from the hovered row. If the list refreshes (which happens on the
inbox's constant polling) in the moment between hovering and pressing a
key, keyboard selection snaps back to the top row instead.
- **Expected behavior:** The keyboard cursor should be in sync with the
hovered row — pressing `j`/`k` after hovering should move relative to
the row the mouse is over.
- **Steps to reproduce:**
1. Open the Inbox with several rows.
2. Hover the mouse over a row partway down the list.
3. Wait for (or trigger) a background poll/refresh of the list.
4. Press `j` or `k`.
5. Observe selection jumps to the top of the list instead of continuing
from the hovered row.
- **Root cause:** The `[flatNavItems]` effect nulled `hoveredIndexRef`
on every list reshape. Since hover also clears the keyboard selection
band to `-1`, the fallback selection index resolved to `0`.
## What Changed
- Hoisted `navEntryKey` to module scope so it can compute a stable,
index-independent identity for a nav row from both the hover handler and
the reshape effect.
- Added `hoveredNavKeyRef` to track the hovered row's stable key
alongside the existing numeric `hoveredIndexRef`, set whenever the
pointer selects a row.
- On list reshape, re-anchor the hovered index by key (find the row with
the same key) instead of unconditionally dropping it; only drop the
hover when the row is actually gone. This mirrors the existing
`selectedIndex` key-based reconciliation.
- Added a unit test that hovers a row, reshapes the list via a simulated
poll, presses `j`, and asserts selection continues from the hovered row.
## Verification
- `pnpm --filter ./ui exec vitest run src/pages/Inbox.test.tsx` → 15/15
passing, including the new hover→`j`/`k` sync test and the existing
keyboard-nav tests.
- The new test explicitly covers the reshape-during-hover path that the
prior test suite had deferred to live/e2e verification.
## Risks
- Low risk. The change is confined to the Inbox's in-memory
hover/selection bookkeeping (two refs and one effect); it adds no new
renders (hover still paints via CSS `:hover`) and touches no data
fetching, routing, or persistence. Behavior is unchanged when the list
does not reshape; when it does, the hover now follows the same row
instead of being dropped.
## Model Used
- Claude Opus 4.8 (Anthropic), model id `claude-opus-4-8`, used with
extended thinking and tool use (file edits, running the UI test suite
locally).
## 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
- [ ] I will address all Greptile and reviewer comments before
requesting merge
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - When an agent's task is `blocked`, humans often comment on the issue
thread expecting it to reopen and resume
> - If the issue stays `blocked` because an unresolved (not-done)
blocker still gates the reopen path, the UI said nothing — the human
"sent a message and nothing happened" (a real silent-failure report)
> - That silence makes the product feel broken even though the gate is
working as designed
> - This pull request adds Rule C copy to the existing
`IssueBlockedNotice` surface: when a comment won't reopen a blocked
issue, it explains why and names the deepest unresolved blocker leaf
with its status
> - The benefit is that the human immediately understands the task is
*paused, not stuck*, and knows exactly which task to act on to unblock
it
## Linked Issues or Issue Description
<!-- No public GitHub issue — describing the underlying problem inline
(path B), following the feature issue template. -->
**Problem or motivation**
A human comments on a `blocked` issue expecting it to move back to
`todo` and resume. When the reopen gate keeps it blocked because an
unresolved (not-`done`) blocker remains, nothing in the UI communicates
this, so the user perceives a dropped message ("I sent a message and
nothing happened").
**Proposed solution**
Reuse the existing amber `IssueBlockedNotice` surface (the designated
blocked/recovery surface — no new component). When a message won't
reopen a blocked issue, the notice states that it won't reopen yet,
names the unresolved blocker leaf with its status (e.g. "Still blocked
by PAP-XXXXX (in progress)"), and reassures that it reopens
automatically once the blocker is done. UI-only; no server change.
**Alternatives considered**
Adding a server signal (a reopen-suppressed reason on the comment/notice
payload) was considered but rejected as unnecessary — the
unresolved-blocker set is already available client-side, so the copy is
derived in the component. Done-but-pending-finalize blockers are `done`,
so they fall out of the unresolved set into the standard reopen (Rule B)
path and are correctly not shown as reopen-suppressed.
## What Changed
- `ui/src/components/IssueBlockedNotice.tsx`: added reopen-suppressed
messaging for `blocked` issues that still have unresolved blockers — a
lead sentence ("a message won't reopen it yet, then it reopens
automatically"), the named unresolved blocker leaf with its status, and
an "and N other task(s)" summarization when multiple blockers remain.
- `ui/src/components/IssueBlockedNotice.test.tsx`: added/updated tests
covering the single, nested-chain (deepest-leaf), multiple-blocker, and
empty-blocker states, plus the not-a-reopen-case (`in_progress`) path.
- `ui/storybook/stories/issue-blocked-notice.stories.tsx`: new Storybook
stories rendering each notice state for copy review.
## Verification
- `cd ui && tsc -b` — typecheck clean (previously failed TS2322 on the
story meta; fixed by a default `args`).
- Vitest: `IssueBlockedNotice.test.tsx` green (single / nested /
multiple / empty / in-progress states).
- Storybook stories rendered at 1440×900 for all four states;
screenshots posted on the tracking issue.
- UXDesigner reviewed the notice copy and signed off (no copy edits
required).
## Risks
Low risk. UI-only, additive copy on an already-shipped amber notice
surface; no server or schema changes. The reopen behavior itself is
unchanged — this only explains the existing gate. Worst case is copy
wording, which had a design review.
## Model Used
Claude — claude-opus-4-8 (Opus 4.8), extended thinking, with tool use /
code execution in the Paperclip harness.
---
- [x] I searched the GitHub PR list for similar/duplicate PRs before
opening this one.
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - Each company workspace in the web UI is mounted under a URL prefix
(e.g. `/NEU/company/...`), and `Link` from `@/lib/router` applies that
prefix automatically via `applyCompanyPrefix`
> - Company Settings rendered its Org Chart, Export, Import, and Cloud
Upstream buttons as raw `<a href>` anchors, which drop the prefix, so
those pages 404 on prefixed instances (#2910);
`CompanyExport.filePathFromLocation` also failed to locate
`/company/export/files/` inside prefixed URLs, breaking export file
previews
> - #2951 fixed the Settings links with `<Link to>` plus tests, but the
sandbox-settings work in #4415 reverted the links back to `<a href>`,
silently reintroducing the bug (#6647)
> - This pull request restores the prefix-aware `<Link>` for all four
Settings links, normalizes the pathname with `toCompanyRelativePath()`
before matching the export-files marker, and adds regression tests
covering every route so the fix cannot be lost again
> - The benefit is that export/import/org-chart/cloud-upstream
navigation and export file previews work on every prefixed deployment
## Linked Issues or Issue Description
Fixes: #6647
Refs #2910 (original report: `/company/export` → prefix `COMPANY` → not
found)
Refs #2951 (original fix with `<Link to>` + tests — merged, then lost)
Refs #4415 (sandbox settings PR that reverted Settings back to `<a
href>`)
## What Changed
- **`ui/src/pages/CompanySettings.tsx`**: use `Link` from `@/lib/router`
for the Org Chart, Export, Import, and Cloud Upstream buttons (replacing
raw `<a href>`)
- **`ui/src/pages/CompanyExport.tsx`**: resolve file paths from prefixed
URLs by normalizing with `toCompanyRelativePath()` before matching
`/company/export/files/`
- **`ui/src/lib/company-routes.test.ts`**: regression tests for
export/import/cloud-upstream/org prefix rewriting, double-prefix
prevention, and export file URL normalization
## Verification
```bash
pnpm vitest run ui/src/lib/company-routes.test.ts
```
Manual:
1. Open `http://localhost:3100/NEU/company/settings` (or your company
prefix).
2. Click **Export** / **Import** — URL should stay under
`/:prefix/company/...`.
3. On export, select a file — URL should be
`/:prefix/company/export/files/...` and preview should load.
The change is navigation-target-only (no visual/layout changes), so
before/after is shown as the resolved URLs:
| Link | Before (prefix dropped → not found) | After |
|------|-------------------------------------|-------|
| Export | `/company/export` | `/NEU/company/export` |
| Import | `/company/import` | `/NEU/company/import` |
| Org Chart | `/org` | `/NEU/org` |
| Cloud Upstream | `/company/settings/cloud-upstream` |
`/NEU/company/settings/cloud-upstream` |
## Risks
Low — same approach as #2951; only navigation/parsing, no API changes.
## Model Used
- Original implementation: authored by @qbamca in Cursor (agentic
editor; the session's exact model ID was not recorded)
- Follow-up commit (merge-conflict resolution) and this description
update: Claude Fable 5 (Anthropic, `claude-fable-5`, extended thinking,
agentic tool use), operated by the Commit Capital triage team
## 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
doc changes required — behavior matches documented routing)
- [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
(pending re-review of the conflict-resolution commit)
- [x] I will address all Greptile and reviewer comments before
requesting merge
Co-authored-by: Jakub Mikiciuk <jmikiciuk@igus.net>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Andrew Aymeloglu <aaymeloglu@gmail.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
## Thinking Path
> - Paperclip is the open source control plane operators use to manage
AI-agent companies.
> - Operators can store runtime secrets in provider vaults such as AWS
Secrets Manager.
> - The server already preserves sanitized AWS failure details,
including the failed operation, required IAM capability, region, and
safe recovery options.
> - The create-secret dialog reduced that structured response to a
generic message, leaving operators unable to understand or fix failed
AWS writes.
> - This pull request keeps the safe structured error through the UI and
presents concise, actionable diagnostics without exposing raw AWS
principals or account details.
> - The benefit is that operators can correct IAM access or link an
existing AWS secret immediately instead of debugging an opaque failure.
## Linked Issues or Issue Description
No public issue was found for this exact UI gap.
Bug report:
- What happened: creating a Paperclip-managed value in AWS Secrets
Manager could fail with a generic dialog error even though the API
returned safe, actionable provider details.
- Expected behavior: the dialog should identify the AWS operation,
required IAM capability, region, provider vault, and safe alternative
while keeping raw cloud-provider details redacted.
- Steps to reproduce: configure an AWS Secrets Manager provider vault
without `secretsmanager:CreateSecret`, then create a Paperclip-managed
secret using that vault.
- Paperclip version/commit: current `master` before this PR.
- Deployment mode: Paperclip server with an AWS Secrets Manager provider
vault.
Related prior server-side propagation work:
- Refs #9161
## What Changed
- Preserve the structured `ApiError` returned by failed create-secret
mutations instead of reducing it to a string.
- Render AWS-specific, sanitized diagnostics with the required IAM
capability, region, provider vault, operation, and external-reference
recovery option.
- Add a full dialog render regression test that verifies actionable
details appear and raw AWS ARN/account information does not.
## Verification
- `pnpm exec vitest run ui/src/pages/Secrets.render.test.tsx` — 1 file
passed, 15 tests passed.
- `pnpm --filter @paperclipai/ui typecheck` — passed.
- `pnpm check:token-gates` — all gates clean.
- `git diff --check public-gh/master...HEAD` — passed.
### Visual Verification
QA verified both states in Chromium at head `357cd2271` using the real
`Secrets` component and confirmed that raw AWS account, ARN,
assumed-role, and provider exception details are absent from the
rendered DOM.
**AWS access-denied diagnostics**

**Generic non-AWS fallback**

## Risks
Low risk. The change only affects failed create-secret presentation in
the UI; successful secret creation, API contracts, schema, and
migrations are unchanged. The structured details are server-sanitized,
and the regression test confirms raw AWS principal/account data is not
rendered.
> 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 `gpt-5.4` via Codex CLI, with tool-enabled repository editing,
shell execution, Git, GitHub, and Paperclip API access. Reasoning mode
and exact context-window size were not exposed by the runtime.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have 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>
## Thinking Path
> - Paperclip is the open source control plane people use to run
AI-agent companies.
> - Scheduled routines support catch-up policies when the server resumes
after missed cron ticks.
> - The existing capped replay policy dispatched once per missed tick,
which can flood the board after downtime for frequent schedules.
> - Sub-hourly routines usually need one prompt catch-up execution
rather than historical per-tick replay, while hourly-or-slower schedules
may rely on the existing behavior.
> - This pull request coalesces missed sub-hourly ticks into one
execution and keeps the slower-schedule behavior unchanged.
> - The benefit is bounded recovery work without changing the semantics
of lower-frequency scheduled routines.
## Linked Issues or Issue Description
### What happened?
When a scheduled routine using `enqueue_missed_with_cap` resumes after
several missed sub-hourly cron ticks, Paperclip dispatches one catch-up
execution for every missed tick. Those executions arrive in a
same-second burst and can flood the board with duplicate-looking work.
### Expected behavior
Sub-hourly schedules should advance past all missed ticks but dispatch
exactly one catch-up execution. Hourly-or-slower schedules should retain
capped per-tick replay.
### Steps to reproduce
1. Build Paperclip from `master` and create a routine with a sub-hourly
cron schedule and `catchUpPolicy: enqueue_missed_with_cap`.
2. Set its persisted `nextRunAt` far enough in the past to cover several
scheduled occurrences.
3. Run routine catch-up processing.
4. Observe multiple catch-up dispatches instead of one coalesced
execution.
### Paperclip version or commit
Reproduced on `master` before this PR.
### Deployment mode
Built from source in local development with embedded PGlite.
## What Changed
- Classify sub-hourly cadence from timezone-aware scheduled occurrences,
avoiding daily multi-minute false positives while supporting schedules
restricted to active days.
- Coalesce all missed sub-hourly ticks into one catch-up dispatch while
advancing `nextRunAt` to the next future occurrence.
- Preserve capped per-tick replay for hourly-or-slower schedules.
- Clarify the catch-up policy labels in both routine editing surfaces.
- Add regression coverage for both the coalesced and preserved
behaviors.
## Verification
- `pnpm exec vitest run server/src/__tests__/routines-service.test.ts
--testNamePattern='coalesces multiple missed sub-hourly ticks|continues
replaying each missed hourly tick|continues replaying missed ticks for
daily schedules with multiple minute values|coalesces sub-hourly
schedules restricted to weekdays'` — 4 passed.
- `pnpm check:token-gates` — all gates clean.
- `git diff --check origin/master...HEAD` — clean.
## Risks
- Low-to-moderate behavioral risk: sub-hourly routines using
`enqueue_missed_with_cap` now intentionally receive one recovery
execution instead of one per missed tick.
- Hourly-or-slower schedules retain their previous capped replay
behavior, limiting the compatibility surface.
- No schema, migration, workflow, or lockfile 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 CLI with GPT-5.5, medium reasoning, code execution and
repository tool use; 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
- [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>
## Thinking Path
> - Paperclip is the open source control plane people use to manage
AI-agent companies and review work that needs human attention.
> - The Decisions page condenses approvals, failed runs, reviews, and
issue interactions into a scannable attention queue.
> - Some decision rows already include screenshot evidence, but the
collapsed thumbnail stack is too small for meaningful inspection.
> - Image-only rows were not expandable because expansion was previously
reserved for inline decision resolvers.
> - Reviewers therefore had to leave the Decisions page before they
could understand the visual evidence attached to a row.
> - This pull request makes rows with images expandable and presents a
readable, linked gallery while preserving the compact collapsed view.
> - The benefit is faster evidence review without sacrificing queue
density or existing inline-resolution behavior.
## Linked Issues or Issue Description
No public GitHub issue currently tracks this feature.
### Subsystem affected
`ui/` — React + Vite board UI
### Problem or motivation
Screenshot evidence in Decisions rows is only visible as small
overlapping thumbnails, and non-inline rows cannot expand to show it.
This makes visual review unnecessarily slow and forces reviewers to
navigate away from the queue.
### Proposed solution
Treat active rows with images as expandable, render the first three
images at a readable size in an expanded gallery, and link images plus
any remaining-image affordance to the related issue.
### Alternatives considered
Always rendering large images would make the queue difficult to scan;
opening the issue immediately preserves density but prevents in-context
review. An explicit expandable gallery keeps both behaviors available.
### Roadmap alignment
`ROADMAP.md` does not list overlapping Decisions image-gallery work.
This is a focused improvement to the existing review surface rather than
a new product area.
### Additional context
The Storybook variants document both the collapsed thumbnail treatment
and deterministic expanded gallery state for reviewer inspection.
## What Changed
- Allow active Decisions rows with screenshot evidence to expand even
when they have no inline resolver.
- Keep compact thumbnails in collapsed rows and render up to three
larger, linked images when expanded.
- Add an accessible remaining-image tile that links to the related issue
when more screenshots exist.
- Add component coverage for image-only expansion and the
remaining-image issue link.
- Add collapsed and expanded image-gallery Storybook variants, including
deterministic initial expansion.
## Verification
- `pnpm exec vitest run ui/src/components/AttentionQueueRow.test.tsx`
- `pnpm check:token-gates`
- `pnpm --dir ui typecheck`
- `pnpm --dir ui build-storybook`
- QA visual verification (light + dark, no defects):
https://github.com/paperclipai/paperclip/pull/9532#issuecomment-4964769485
## Risks
- Low risk: the change is isolated to Decisions row rendering and
Storybook fixtures.
- Rows with images gain a new expansion interaction, but existing inline
resolver behavior and deep links remain intact.
- The gallery intentionally limits the in-row preview to three images to
avoid unbounded row height.
> 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
- Anthropic Claude Opus 4.8 assisted with the original implementation
and tests.
- OpenAI `gpt-5.4` via Codex CLI assisted with current-master rebase
integration, verification, and PR preparation. The runtime
context-window size is not exposed; capabilities used include reasoning,
repository tool use, 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>
## Thinking Path
> - Paperclip is the open source control plane people use to coordinate
AI-agent companies and their work.
> - Its recovery subsystem detects stranded issue execution and decides
whether to retry, escalate, or request operator intervention.
> - The existing recovery path used a mostly generic owner ladder and
generic execution contract, so transient failures could wake a manager
who then performed the deliverable instead of repairing and returning
the task.
> - Provider quota failures also entered the same takeover path even
when the correct action was to wait for capacity and retry the original
assignee.
> - Recovery actions already retain the source owner and evidence needed
to choose a cause-specific route, render a scoped contract, and measure
whether work was handed back.
> - This pull request adds a cause-keyed recovery playbook, propagates
its contract through every built-in adapter, and makes resolved recovery
actions return work to the original owner by default.
> - The benefit is bounded self-recovery that preserves task ownership,
avoids needless management takeover, and makes recovery outcomes
observable.
## Linked Issues or Issue Description
No matching public GitHub issue was found.
Related recovery work was reviewed but is not duplicated here: #9630
restores bounded recovery continuations, #8807 changes one
assignee-ranking case, and #9404 records runtime-failure transition
evidence. This change instead introduces cause-specific routing and
recovery contracts across the recovery lifecycle.
### What happened?
When an issue became stranded, recovery generally selected an owner
through the same fallback ladder and rendered the normal execution
contract. That made the recovery wake look like ordinary deliverable
work, even when the correct action was to retry the original agent,
repair its runtime, or wait for a provider quota reset.
### Expected behavior
Recovery should select a response by failure cause, tell the recipient
to recover rather than complete the deliverable, suppress takeover wakes
for provider quota waits, and return repaired work to its original
assignee unless the recovery owner explicitly completes it.
### Actual behavior
Recovery could escalate transient failures to management, omit the
cause-specific next action from the wake, and leave the recovery owner
assigned after the runtime problem was resolved.
### Impact
The generic path creates avoidable management work, ownership churn, and
budget consumption while obscuring whether recovery successfully
returned work to the responsible agent.
## What Changed
- Added cause-keyed routing for process loss, missing disposition,
provider quota limits, Codex output inactivity, workspace validation
failures, and fallback recovery causes.
- Added recovery-scoped wake rendering that replaces the generic
execution contract with the failure summary, original assignee, attempt
count, next action, and cause-specific playbook instruction.
- Propagated the structured recovery contract through all built-in
adapter execution paths, including Hermes local and gateway adapters.
- Added provider-quota wait monitoring so capacity failures schedule the
original assignee instead of enqueueing a takeover wake.
- Added hand-back behavior and `handed_back` / `owner_completed` outcome
accounting when recovery actions are resolved.
- Added focused routing, renderer, quota-monitor, and hand-back
regression coverage plus implementation-spec documentation.
## Verification
- `pnpm exec vitest run packages/adapter-utils/src/server-utils.test.ts
server/src/__tests__/heartbeat-process-recovery.test.ts
server/src/__tests__/heartbeat-workspace-branch-containment.test.ts
server/src/__tests__/issue-recovery-actions.test.ts`
- 4 test files passed; 194 tests passed.
- Targeted `pnpm --filter ... typecheck` across
`@paperclipai/adapter-utils`, `@paperclipai/shared`,
`@paperclipai/server`, `@paperclipai/ui`, and all nine changed adapter
packages.
- 13 affected workspace packages passed typecheck.
- `pnpm check:token-gates`
- All UI token gates passed.
## Risks
- Recovery routing behavior changes for stranded work, so an incorrectly
classified cause could select a different recipient than before;
fallback causes retain the existing management ladder.
- Provider quota detection depends on structured failure evidence and
conservative text matching; unmatched failures continue through fallback
recovery.
- Adapter prompt plumbing changes across built-ins, covered by shared
renderer tests and compile-time call signatures.
> 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 exact model ID `gpt-5.6-sol`, using reasoning, tool
use, and code execution. The runtime does not expose its configured
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>
## Thinking Path
> - Paperclip is the open source control plane people use to manage
AI-agent companies.
> - Company skills make reusable agent behavior discoverable and
editable from one place.
> - Projects already contain skill directories, but operators had to
import each skill path manually.
> - Copying those skills would break the desired write-through workflow
between Skill Studio and the source project.
> - The server therefore needs a safe preview/select/import contract
that only accepts rediscovered, workspace-contained candidates.
> - The UI needs a guided project picker that explains reference
semantics, handles conflicts, and remains usable on mobile.
> - This pull request adds that end-to-end project skill import flow
with authorization, tenant-scope, traversal, and symlink regression
coverage.
> - The benefit is faster bulk onboarding while keeping project files as
the single source of truth.
## Linked Issues or Issue Description
**Feature request**
**Problem:** Importing several skills already stored in a Paperclip
project requires operators to discover and submit each local path
individually. This is slow, hides which well-known directories were
searched, and makes conflict/already-imported states difficult to
evaluate before mutation.
**Proposed solution:** Add an “Import skills from project” flow that
previews skills from well-known directories, lets operators selectively
import eligible candidates, and stores local-path references so Skill
Studio edits write through to the project files.
**Alternatives considered:** Copying files into company-managed skill
storage was rejected because it creates divergent copies. Trusting
client-supplied paths was rejected because imports must be constrained
to server-rediscovered, workspace-contained candidates.
**Additional context:** GitHub duplicate search found no existing issue
or PR for this exact workflow. Refs #3799 for related skill-import
inventory behavior; this PR does not claim to close that issue.
## What Changed
- Extend `scan-projects` with backward-compatible preview and
selective-import modes, typed validation, candidate statuses, and
OpenAPI coverage.
- Discover project skills under `skills`, `.agents/skills`,
`.claude/skills`, `.codex/skills`, `.cursor/skills`, `.opencode/skills`,
and `.gemini/skills`.
- Re-discover selections server-side, enforce company/project/workspace
scope, and reject traversal or symlink escapes before creating
`local_path` references.
- Add the Skills-page menu entry and responsive project import dialog
with project selection, grouped candidates, select all/deselect all,
conflicts, empty/error/403 states, and import results.
- Add route, service, and component regressions for preview
authorization, cross-tenant selections, traversal/symlink safety,
selection counts, grouping, and result semantics.
### Screenshots
**Choose a project**

**Review discovered skills**

**Mobile selection footer**

**Import result**

## Verification
- `pnpm exec vitest run
server/src/__tests__/company-skills-service.test.ts
server/src/__tests__/company-skills-routes.test.ts
ui/src/pages/skills/ImportSkillsFromProjectDialog.test.tsx` — 3 files,
81 tests passed.
- `pnpm check:token-gates` — all token gates clean.
- `pnpm --filter @paperclipai/shared typecheck` — passed.
- `pnpm --filter @paperclipai/server typecheck` — passed.
- `pnpm --filter @paperclipai/ui typecheck` — passed.
- Security review passed after adding tenant-scope and
unauthorized-preview regressions; UX re-review approved desktop/mobile
surfaces; QA passed all seven acceptance areas including write-through
editing, deduplication, conflicts, empty state, and permission denial.
## Risks
- Files remain referenced in project workspaces, so moving or deleting a
source directory can make an imported skill unavailable; the UI
explicitly communicates the reference behavior.
- New well-known directory scans may discover more candidates than older
versions, but preview mode prevents mutation until the operator confirms
a selection.
- The endpoint remains backward compatible: omitting `mode` preserves
the prior full-import behavior.
- No schema migration or telemetry event 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
- Anthropic Claude Opus 4.8 with tool use/code execution assisted with
the UI implementation and UX polish. OpenAI Codex CLI with tool use/code
execution assisted with server implementation, security fixes,
regression coverage, integration, and PR preparation; the runtime did
not expose Codex's exact backing model ID or context-window size.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - Its web UI keeps live views fresh with React Query, coordinated by a
live-events websocket (`/api/companies/:id/events/ws`) and cross-tab
polling
> - We already cut the worst live-update churn (#9569, #9624), but the
deeper issue is that even the "push" path is *push-the-signal,
pull-the-data*: a websocket event triggers `invalidateQueries` → an HTTP
refetch
> - The company live-runs list (`queryKeys.liveRuns`) is the
most-observed resource — the sidebar renders it on nearly every page —
so its refetch is the most ambient source of churn, fired on every
`heartbeat.run.queued` / `heartbeat.run.status` event
> - Those events already carry enough (`runId`, `status`) to update the
cached list directly, so this pull request event-sources that list
instead of refetching it
> - The benefit is that the always-observed live-runs list stops
refetching on run lifecycle events — the first concrete step of the
push-over-poll redesign
## Linked Issues or Issue Description
No public GitHub issue exists; describing inline per CONTRIBUTING.md →
"Link Issues or Describe Them In-PR", following the bug report template.
This continues the memory/CPU-churn work from #9569 and #9624.
**What happened?**
Live agent-run tabs accrue high idle CPU and off-heap memory because
live-update events cause HTTP refetches. Profiling showed the company
live-runs list — observed on almost every page via the sidebar — being
refetched on every run status change, one of the most frequent ambient
refetches.
**Expected behavior**
A websocket event that already carries the changed data should update
the cached list directly, without an HTTP round-trip, so the
always-observed live-runs list does no refetch on routine run lifecycle
events.
**Steps to reproduce**
Open the app with agents running and watch the network panel: `GET
/api/companies/:id/live-runs` fires on each `heartbeat.run.status` /
`heartbeat.run.queued` event even though the event payload already
describes the change.
**Paperclip version or commit**
Branch `perf/live-runs-event-sourced`, off `master` (after #9624).
**Deployment mode**
Local dev (`pnpm dev`), web UI. Core UI live-updates plumbing; not
adapter-specific.
## What Changed
- **`ui/src/lib/live-runs-cache.ts` (new)** — pure `removeRunFromList` /
`patchRunStatusInList` helpers for the cached `LiveRunForIssue[]`.
- **`ui/src/context/LiveUpdatesProvider.tsx`** — on
`heartbeat.run.queued` / `heartbeat.run.status`, patch
`liveRuns(companyId)` in place instead of invalidating it:
- terminal status → remove the run from the list,
- status change on a run already in the list → update it in place,
- a genuinely new run (can't be reconstructed from the event) → fall
back to a single `invalidateQueries` refetch.
- Removed the blanket `liveRuns` invalidation from
`invalidateHeartbeatQueries`.
- On websocket **reconnect**, refetch `liveRuns` once to reconcile
events missed while disconnected (durable replay is a later phase).
- Other resources these events invalidate (`dashboard`, `costs`,
`sidebarBadges`, `agents.list`, agent detail) are unchanged — they're
lower-frequency / less-often-observed and are follow-up phases. This
keeps the change scoped and **client-only** (no server changes).
## Verification
- `vitest`: new `live-runs-cache.test.ts` (remove/patch/no-op/undefined)
and new lifecycle-handler cases in `LiveUpdatesProvider.test.ts`
(terminal→remove, present→patch, new→needs-refetch) via
`__liveUpdatesTestUtils`. All existing `LiveUpdatesProvider` tests still
pass (33 total across the two files).
- `tsc -b` clean.
- Runtime: the event-sourced path is covered by unit tests; end-to-end
refetch reduction should be re-measured against a rebuilt bundle with
the network panel / MCP instrumentation.
## Risks
Low, and client-only.
- **Staleness across a dropped connection:** an event missed while the
socket is down isn't replayed yet, so the reconnect handler refetches
`liveRuns` once to reconcile. Durable event replay (Last-Event-ID) is a
planned later phase; until then reconnect-reconcile covers the gap.
- **New-run fallback:** a genuinely new run still triggers one refetch
(it can't be reconstructed from the event alone), so no new runs are
missed.
- Aggregate resources (dashboard/costs/badges) are untouched and still
invalidate (already coalesced), so their behavior is unchanged.
Follow-up phases (from the design discussion): event-source
`activity`/comments and the remaining class-B resources; give pure-poll
resources events and drop their intervals; add durable event sequence +
reconnect replay; and a shared bus (Postgres `LISTEN/NOTIFY`) only when
the API tier scales to >1 replica.
## Model Used
- **Provider:** Anthropic, via the Claude Code CLI.
- **Model:** Claude Opus 4.8 (`claude-opus-4-8`).
- **Reasoning mode:** Extended thinking enabled.
- **Capabilities used:** tool use (shell, file editing), sub-agent
fan-out to inventory the client polling and server push infrastructure,
and the Chrome DevTools MCP to reproduce/profile the churn that
motivated this redesign.
## 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 (a perf/plumbing change, not planned core feature
work)
- [x] I have searched GitHub for duplicate or related PRs and linked
them above (continues #9569 / #9624; no duplicates)
- [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 considered and documented any risks above
- [ ] I have updated relevant documentation to reflect my changes (N/A —
no user-facing docs; rationale documented inline)
- [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
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - Its web UI keeps live views (issue threads, run transcripts,
dashboard) fresh via React Query polling plus a live-events websocket,
coordinated across tabs with a `BroadcastChannel` layer
> - Long-lived tabs viewing live agent runs grew to multi-GB memory
footprints while their JS heap stayed ~60–200 MB — so the memory is
off-heap (Blink/native + committed allocator arenas), not a classic JS
leak
> - Live profiling of a reproduced 2.7 GB / 66 MB tab showed 15–30% idle
CPU, ~3 fetches/sec across overlapping poll loops, and ~8 `setInterval`
create/clear cycles per second whose rate grew ~7× as the tab aged —
relentless allocation churn that inflates committed memory the OS never
reclaims, amplified across tabs by the cross-tab fan-out
> - This pull request cuts that churn at its four largest sources
(invalidation storm, per-instance 1 s timers, redundant polling,
unbounded streamed-run set)
> - The benefit is that idle tabs do far less periodic work, so their
off-heap footprint stops ballooning over a long session
## Linked Issues or Issue Description
No public GitHub issue exists; describing inline per CONTRIBUTING.md →
"Link Issues or Describe Them In-PR", following the bug report template.
**What happened?**
Browser tabs viewing live agent runs grew to 8–16 GB memory footprint
over a long session (multiple tabs open), while each tab's "live" JS
heap stayed only ~150–250 MB. Every idle tab also burned 15–30% CPU.
Tabs eventually approached the ~4 GB V8 heap ceiling / OS pressure and
could crash.
**Expected behavior**
Tabs viewing live runs should hold a bounded footprint and do minimal
work while idle, regardless of how long they stay open or how many tabs
are open.
**Steps to reproduce**
Open several issue/run tabs that have agents actively streaming and
leave them open for a while. Watch Chrome's Task Manager: Memory
Footprint climbs into the GBs while "JavaScript Memory" stays small, and
CPU stays high on idle tabs. Reproduced in ~90 minutes: a tab reached
2.7 GB footprint on a 66 MB JS heap, and the per-second timer-churn rate
was ~7× higher on a 90-minute-old tab than a fresh one.
**Paperclip version or commit**
Branch `fix/live-updates-churn`, off `master`.
**Deployment mode**
Local dev (`pnpm dev`), web UI. Not adapter-specific — core UI
live-updates plumbing (observed with `claude_local` / `codex_local`
runs).
## What Changed
- **`ui/src/lib/query-invalidation-batcher.ts` (new)** —
`createInvalidationBatcher` throttles + de-dupes React Query
invalidations into one flush per ~300 ms, and
`createCoalescingQueryClient` wraps the client via a `Proxy` so only
`invalidateQueries` is batched (optimistic `setQueryData` writes stay
immediate). Wired into `LiveUpdatesProvider`, which previously
invalidated synchronously on every websocket event.
- **`ui/src/hooks/useSecondTick.ts` (new)** — one shared, ref-counted,
page-wide 1 s ticker. `useLiveElapsed` in `IssueChatThread` now uses it
instead of a per-instance `setInterval` that forced a full-thread
re-render every second per live element.
- **`ui/src/components/transcript/useLiveRunTranscripts.ts`** — when the
realtime websocket is enabled, the recurring log poll backs off to a 30
s safety-net cadence instead of polling every 2 s on top of the live
stream. Added a marker for the durable poll→push rearchitecture.
- **`ui/src/lib/issueChatTranscriptRuns.ts`** —
`resolveIssueChatTranscriptRuns` now caps the streamed run set
(live/active runs always kept; most-recent linked runs fill up to 20) so
a large run history can't open a live-transcript poll per historical
run.
- **`ui/src/main.tsx`** — explicit `gcTime` so cross-tab-published cache
entries for unobserved resources are collected promptly.
- Tests for the batcher, shared ticker, and run cap.
## Verification
- `vitest`: new suites `query-invalidation-batcher.test.ts` (batcher
collapses 20 invalidations → 1 flush; keeps distinct keys/variants;
dispose cancels; proxy passes non-invalidate methods through),
`useSecondTick.test.tsx` (single ref-counted timer, stops when idle),
`issueChatTranscriptRuns.test.ts` (cap keeps newest + live). All pass.
- Existing affected suites pass: `LiveUpdatesProvider` (23),
`IssueChatThread` (), `useLiveRunTranscripts`,
`AgentDetail.instructions` — 109 tests across affected files.
- `tsc -b` clean.
- Behavior confirmed by live profiling before the change (2.7 GB / 66 MB
tab, ~8 interval churns/sec growing 7× with age). Runtime churn
reduction should be re-measured against a rebuilt bundle with the same
instrumentation.
## Risks
Low-to-moderate; all changes reduce work rather than add features.
- **Invalidation batching** delays live-driven refetches by up to ~300
ms. Optimistic `setQueryData` writes (e.g. the visible issue's new
comment) remain immediate, so foreground updates still feel instant;
only the safety-net refetch is throttled. Non-live invalidations (user
actions, mutations) are unaffected — they use the real client.
- **Poll back-off** relies on the websocket as the live source when
realtime is enabled; a 30 s fallback poll still covers gaps/reconnects
(both the transcript hook and `LiveUpdatesProvider` also
auto-reconnect).
- **Run cap (20)** means an issue with a very large run history streams
live transcripts only for its live/active + 20 most-recent runs; older
runs still open normally via their run pages.
- Downstream test fallout (timing-sensitive tests around
invalidation/polling) may need adjustment — flagged intentionally for
follow-up.
Durable follow-up (out of scope, marked in code): replace transcript/run
polling with server push (SSE/websocket deltas) so idle tabs do no
periodic work at all.
## Model Used
- **Provider:** Anthropic, via the Claude Code CLI.
- **Model:** Claude Opus 4.8 (`claude-opus-4-8`).
- **Reasoning mode:** Extended thinking enabled.
- **Capabilities used:** tool use (shell, file editing), sub-agent
fan-out for codebase analysis, and the Chrome DevTools MCP to reproduce
and profile the memory/CPU churn on a live instance.
## 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 (a bug/perf fix, not planned core feature work)
- [x] I have searched GitHub for duplicate or related PRs and linked
them above (none found)
- [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 considered and documented any risks above
- [ ] I have updated relevant documentation to reflect my changes (N/A —
no user-facing docs; rationale documented inline)
- [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
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
## Thinking Path
> - Paperclip uses company skills to make agent capabilities reusable
across an organization.
> - Skill operations currently mix capability availability with
permission checks, which creates avoidable setup friction and
inconsistent denial handling.
> - The policy contract needs to remain open by default while allowing
company-scoped restrictions for governed deployments.
> - Core owns the canonical policy actions, persistence, evaluation, API
behavior, safe import boundaries, and generic denial/read-only UI.
> - Enterprise policy-editor implementation belongs in the separate
`paperclip-ee` repository and is intentionally excluded from this PR.
### Problem or motivation
Company skill operations can encounter permission dead ends even when no
explicit restriction has been configured, and import-source
classification can drift between policy evaluation and execution.
### Proposed solution
Define eight canonical skill policy actions, default all actions to
allowed, persist company-scoped restrictions, expose policy evaluation
APIs, normalize import sources at the boundary, and update Skill Studio
to present actionable restriction states without embedding Enterprise
Edition implementation in the core repository.
### Alternatives considered
Keeping capability checks distributed across routes and UI surfaces was
rejected because it duplicates policy logic and makes denial behavior
inconsistent. Shipping the Enterprise policy editor in this repository
was rejected because `paperclip-ee` is a separate repository and must
receive its own PR.
### Roadmap alignment
Extends the completed **Skills Manager** roadmap area by adding coherent
governance and removing workflow dead ends.
### Additional context
The core API contract remains suitable for a separate Enterprise Edition
editor, but this PR contains no `paperclip-ee` package or EE-specific UI
integration code.
## What Changed
- Added the company skill policy contract to product and implementation
documentation, including the open-by-default rule, eight canonical
actions, decision shape, and core/EE ownership boundary.
- Added the company-scoped policy schema, migration `0170`, shared
validators, policy service, REST routes, OpenAPI coverage, and focused
server tests.
- Hardened import policy enforcement by normalizing import sources and
keeping source classification consistent between policy evaluation and
execution.
- Updated core Skill Studio behavior to remove generic permission dead
ends and show actionable policy/platform denial states only when an
operation is actually denied.
- Removed the `plugin-paperclip-ee` package, Docker wiring, EE
discovery/deep-link helpers, and EE-specific UI tests/stories from this
PR so that implementation can be submitted separately to the EE
repository.
- Preserved open-by-default behavior when no explicit company
restriction exists.
## Verification
- `pnpm --filter @paperclipai/ui exec vitest run
src/components/skill-studio/SkillPolicySurfaces.test.tsx
src/lib/skill-policy-denial.test.ts` — 20/20 passed.
- `pnpm --filter @paperclipai/ui exec tsc --noEmit` — passed.
- `pnpm --filter @paperclipai/server exec vitest run
src/__tests__/worktree-config.test.ts` — 12/12 passed.
- `pnpm check:token-gates` — passed with all gates clean.
- `git diff --check` — passed.
- `git diff --name-only origin/master | rg
'paperclip-ee|ee-skill-policy'` — no matches.
## Risks
- Migration `0170` introduces company policy persistence; rollout
depends on the migration applying before policy routes are exercised.
- Open-by-default is an intentional behavioral policy: deployments
expecting implicit denials must configure explicit restrictions.
- Import normalization is security-sensitive and should retain focused
review.
- The separate EE editor must stay contract-compatible with the core
policy API as policy actions evolve.
## Model Used
- OpenAI Codex CLI, runtime model identifier and context-window size not
exposed by this execution environment; reasoning, repository tool use,
shell execution, and code review capabilities 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 available to this runtime)
- [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 or
described the result 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 focused 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 on the latest head
- [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: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Evyatar Bluzer <bluzername@users.noreply.github.com>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - Governed MCP access spans contracts, runtime enforcement, adapters,
UI surfaces, and operator verification
> - The parity reference PR #9534 is too large for effective automated
or human review
> - The feature therefore needs a linear stack whose individual diffs
stay below the 100-file review limit
> - This pull request is split 7/8 and focuses on Apps/Gateways UI and
feature-flagged activation
> - The benefit is a standalone, testable review boundary while
preserving byte-for-byte parity at the top of the stack
## Linked Issues or Issue Description
- Related parity reference: #9534
- Problem: The standalone UI foundation needs feature-flagged routes,
navigation, app detail flows, gateways, Storybook scenarios, and QA
configuration.
- Proposed solution: Adds Apps/Gateways pages and components,
navigation/route activation, remaining page integrations, Storybook
stories, and QA Vite configuration.
- Alternatives considered: keeping #9534 as one 403-file review, or
rewriting the feature to manufacture seams; both were rejected in favor
of path extraction plus compile-driven boundary moves.
- Roadmap alignment: this advances the existing governed MCP/tool-access
work already represented by #9534; it does not introduce a separate
roadmap initiative.
- Stack position: base branch is
`pap10341-split/06-ui-tools-foundation`.
- Merge policy: merge bottom-up, in order, only after the complete
eight-PR stack has been reviewed and the top-of-stack parity gate
remains empty.
- Requested review: UXDesigner sanity pass on Apps/Gateways flows and
flag-off behavior; Greptile on every PR.
## What Changed
- Adds Apps/Gateways pages and components, navigation/route activation,
remaining page integrations, Storybook stories, and QA Vite
configuration.
- Keeps this PR below 100 changed files and independently typecheckable.
- Preserves the final tree from #9534 when combined with the other seven
stack levels.
## Verification
- `pnpm typecheck`
- `pnpm check:token-gates` — all gates clean
- Focused UI Vitest run with `NODE_ENV=test` — 17 files, 147 tests
passed
## Risks
- Navigation or flag regressions could expose incomplete experiences;
activation remains controlled by existing experimental settings.
- Stack risk: merging out of order can expose incomplete layers;
mitigate by following the documented bottom-up merge policy.
- Parity risk: later edits to an intermediate branch can drift from
#9534; mitigate by re-running the empty top-of-stack diff before merge.
> 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, exact model ID `gpt-5.4`; runtime-managed context
window; medium reasoning with repository, shell, Git, GitHub CLI, and
code-execution tools 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] Internal references are omitted except the execution-plan link
explicitly required for this coordinated split stack
- [x] My branch name describes the change and contains no internal
Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [ ] All Paperclip CI gates are green
- [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
## Stack Coordination
- Internal execution plan:
[PAP-13874](/PAP/issues/PAP-13874#document-plan)
- Parity reference: #9534
- Stack: #9556 → #9557 → #9558 → #9559 → #9560 → #9561 → #9562 → #9563
- Merge bottom-up only after full-stack review and an empty parity diff
at #9563.
## UI Evidence
QA captured these from the live Garden MCP split stack at 1440px and
verified clean rendering:



---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - Governed MCP access spans contracts, runtime enforcement, adapters,
UI surfaces, and operator verification
> - The parity reference PR #9534 is too large for effective automated
or human review
> - The feature therefore needs a linear stack whose individual diffs
stay below the 100-file review limit
> - This pull request is split 6/8 and focuses on UI API, shared
components, and Tools/Profile surfaces
> - The benefit is a standalone, testable review boundary while
preserving byte-for-byte parity at the top of the stack
## Linked Issues or Issue Description
- Related parity reference: #9534
- Problem: Operators need typed clients and administration surfaces that
compile independently before navigation exposes them.
- Proposed solution: Adds UI APIs, hooks, libraries, shared components,
Tools/Profiles pages, and the plugin settings consumer required by the
new company-scoped API.
- Alternatives considered: keeping #9534 as one 403-file review, or
rewriting the feature to manufacture seams; both were rejected in favor
of path extraction plus compile-driven boundary moves.
- Roadmap alignment: this advances the existing governed MCP/tool-access
work already represented by #9534; it does not introduce a separate
roadmap initiative.
- Stack position: base branch is
`pap10341-split/05-runtime-integration`.
- Merge policy: merge bottom-up, in order, only after the complete
eight-PR stack has been reviewed and the top-of-stack parity gate
remains empty.
- Requested review: UXDesigner sanity pass on Tools/Profile surfaces;
Greptile on every PR.
## What Changed
- Adds UI APIs, hooks, libraries, shared components, Tools/Profiles
pages, and the plugin settings consumer required by the new
company-scoped API.
- Keeps this PR below 100 changed files and independently typecheckable.
- Preserves the final tree from #9534 when combined with the other seven
stack levels.
## Verification
- `pnpm typecheck`
- `pnpm check:token-gates` — all gates clean
- Focused UI Vitest run with `NODE_ENV=test` — 28 files, 183 tests
passed
## Risks
- Large dead-code UI additions can drift from activation routes; PR 7
supplies the registration layer and top-level parity catches omissions.
- Stack risk: merging out of order can expose incomplete layers;
mitigate by following the documented bottom-up merge policy.
- Parity risk: later edits to an intermediate branch can drift from
#9534; mitigate by re-running the empty top-of-stack diff before merge.
> 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, exact model ID `gpt-5.4`; runtime-managed context
window; medium reasoning with repository, shell, Git, GitHub CLI, and
code-execution tools 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] Internal references are omitted except the execution-plan link
explicitly required for this coordinated split stack
- [x] My branch name describes the change and contains no internal
Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [ ] All Paperclip CI gates are green
- [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
## Stack Coordination
- Internal execution plan:
[PAP-13874](/PAP/issues/PAP-13874#document-plan)
- Parity reference: #9534
- Stack: #9556 → #9557 → #9558 → #9559 → #9560 → #9561 → #9562 → #9563
- Merge bottom-up only after full-stack review and an empty parity diff
at #9563.
## UI Evidence
QA captured these from the live Garden MCP split stack at 1440px and
verified clean rendering:



---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - Its server and UI test suites protect company-scoped plugin access
and instance settings behavior
> - Recent governed-access contracts intentionally added company
invocation scope and new experimental-setting defaults
> - Four existing tests were not updated consistently with those
contracts, causing current-master CI failures unrelated to the changes
under review
> - The runtime behavior is intentional, so changing production code
would weaken the new authorization and settings contracts
> - This pull request aligns the stale tests with current behavior and
removes one UI assertion accidentally pulled forward from a later
stacked feature
> - The benefit is a focused, low-risk repair that restores master CI
without changing application behavior
## Linked Issues or Issue Description
- **Bug:** Current master has four regression failures in plugin
authorization, plugin execution-workspace bridging, instance settings
normalization, and experimental settings UI tests.
- **Expected behavior:** Tests provide required company/invocation
scope, use the governed object-shaped secret reference contract, include
all current defaults, and only assert UI controls implemented at this
stack level.
- **Actual behavior:** Tests exercised obsolete request shapes or
expected a later-stack Apps toggle that is not present on current
master.
- **Reproduction:** Run the four test files listed in the Verification
section on master before this commit.
## What Changed
- Updates plugin config authorization coverage to include company scope
and an object-shaped `secret_ref` binding.
- Supplies invocation company scope to execution-workspace host-client
tests.
- Adds `enableApps` and `enableSmokeLab` to normalized settings
expectations.
- Removes the premature Apps toggle UI test introduced without its
later-stack implementation.
## Verification
- `pnpm exec vitest run server/src/__tests__/plugin-routes-authz.test.ts
server/src/__tests__/plugin-execution-workspace-bridge.test.ts
server/src/__tests__/instance-settings-service.test.ts
ui/src/pages/InstanceExperimentalSettings.test.tsx` — 73 tests passed.
- `pnpm exec vitest run
packages/plugins/sdk/tests/host-client-factory.test.ts
server/src/__tests__/plugin-secrets-handler.test.ts
server/src/__tests__/instance-settings-routes.test.ts
ui/src/lib/instance-settings.test.ts` — 39 tests passed.
- `git diff --check` — passed.
## Risks
- Low risk: test-only changes with no production runtime, schema, API,
or UI behavior changes.
- The removed Apps toggle assertion should return in the later stacked
change that introduces the actual control.
> 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, exact model ID `gpt-5.4`; runtime-managed context
window; medium reasoning with repository, shell, GitHub CLI, and
code-execution tools 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
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - Governed MCP access spans contracts, runtime enforcement, adapters,
UI surfaces, and operator verification
> - The parity reference PR #9534 is too large for effective automated
or human review
> - The feature therefore needs a linear stack whose individual diffs
stay below the 100-file review limit
> - This pull request is split 2/8 and focuses on database schema and
shared governance contracts
> - The benefit is a standalone, testable review boundary while
preserving byte-for-byte parity at the top of the stack
## Linked Issues or Issue Description
- Related parity reference: #9534
- Problem: The governed access model needs additive persistence and
synchronized shared types before server enforcement can compile.
- Proposed solution: Adds migrations 0148–0169, tool-access and Smoke
Lab schema, shared types/validators/gallery helpers, and the minimal
compile-required contract consumers identified by boundary testing.
- Alternatives considered: keeping #9534 as one 403-file review, or
rewriting the feature to manufacture seams; both were rejected in favor
of path extraction plus compile-driven boundary moves.
- Roadmap alignment: this advances the existing governed MCP/tool-access
work already represented by #9534; it does not introduce a separate
roadmap initiative.
- Stack position: base branch is `pap10341-split/01-demo-servers`.
- Merge policy: merge bottom-up, in order, only after the complete
eight-PR stack has been reviewed and the top-of-stack parity gate
remains empty.
- Requested review: QA for migrations/validators; Greptile on every PR.
## What Changed
- Adds migrations 0148–0169, tool-access and Smoke Lab schema, shared
types/validators/gallery helpers, and the minimal compile-required
contract consumers identified by boundary testing.
- Keeps this PR below 100 changed files and independently typecheckable.
- Preserves the final tree from #9534 when combined with the other seven
stack levels.
## Verification
- `pnpm typecheck` — passed, including migration numbering and safety
checks
- `pnpm --filter @paperclipai/db test` — passed
- `pnpm --filter @paperclipai/shared test` — passed
## Risks
- Migration or contract mistakes could affect every upper layer; all
migrations are additive/idempotent and compile consumers are included in
this boundary.
- Stack risk: merging out of order can expose incomplete layers;
mitigate by following the documented bottom-up merge policy.
- Parity risk: later edits to an intermediate branch can drift from
#9534; mitigate by re-running the empty top-of-stack diff before merge.
> 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, exact model ID `gpt-5.4`; runtime-managed context
window; medium reasoning with repository, shell, Git, GitHub CLI, and
code-execution tools 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] Internal references are omitted except the execution-plan link
explicitly required for this coordinated split stack
- [x] My branch name describes the change and contains no internal
Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [ ] All Paperclip CI gates are green
- [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
## Stack Coordination
- Internal execution plan:
[PAP-13874](/PAP/issues/PAP-13874#document-plan)
- Parity reference: #9534
- Stack: #9556 → #9557 → #9558 → #9559 → #9560 → #9561 → #9562 → #9563
- Merge bottom-up only after full-stack review and an empty parity diff
at #9563.
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - Agents stream their run output live into the web UI, viewed per-run
in the `AgentDetail` transcript viewer
> - Browser tabs holding these views were sitting at 8–16 GB of memory
footprint while their JS heap stayed at ~256 MB — a 60×+ gap, meaning
the cost is in retained DOM / render objects, not JS objects
> - The `LogViewer` in `AgentDetail.tsx` kept every streamed
stdout/stderr line and structured event in unbounded React state and
rendered each into a rich DOM block (the default "nice" mode has no
virtualization), so a run streaming for hours grew an unbounded live DOM
tree
> - The sibling `useLiveRunTranscripts` hook (used by `IssueChatThread`)
already bounds its buffers and virtualizes; `LogViewer` bypassed it and
managed its own uncapped state — that inconsistency is the bug
> - This pull request caps the live buffers and bounds the live DOM
render, aligning `LogViewer` with the already-bounded path
> - The benefit is that long-lived streaming tabs no longer grow without
bound, cutting multi-GB tabs back to a bounded footprint
## Linked Issues or Issue Description
No public GitHub issue exists; describing inline per CONTRIBUTING.md →
"Link Issues or Describe Them In-PR", following the bug report template.
**What happened?**
Chrome's Task Manager showed multiple long-lived Paperclip tabs
(agent-run / task views) each consuming 8–16 GB of memory footprint,
while each tab's JS heap stayed at only ~150–256 MB. Memory grew
monotonically the longer a run streamed.
**Expected behavior**
A tab viewing a live agent run should hold a bounded amount of memory
regardless of how long the run streams.
**Steps to reproduce**
Open an agent run with a long-running / high-volume stream in
`AgentDetail`, leave the tab open while output streams for an extended
period, and watch the tab's memory footprint climb without bound in
Chrome's Task Manager.
**Paperclip version or commit**
`3991a19a` (branch `fix/agent-run-transcript-memory`, off `master`).
**Deployment mode**
Local dev (`pnpm dev`), web UI. Not adapter-specific — core UI bug in
the shared transcript viewer.
## What Changed
- Add `ui/src/lib/live-log-buffer.ts`: a pure `appendCapped(prev,
additions, max)` helper plus caps `MAX_LIVE_LOG_LINES=5000`,
`MAX_LIVE_EVENTS=2000`, and `LIVE_TRANSCRIPT_RENDER_LIMIT=1500`, with
rationale documented in the module.
- Add `ui/src/lib/live-log-buffer.test.ts`: 6 unit tests (append,
trim-to-cap, oversized batch, exact-cap, no-mutation, referential
bail-out).
- `ui/src/pages/AgentDetail.tsx` (`LogViewer`): route all four
live-append sites (WebSocket log / progress / event, plus the poll
fallback) through `appendCapped`, and pass
`limit={LIVE_TRANSCRIPT_RENDER_LIMIT}` to `RunTranscriptView` for live
runs so the "nice" view mounts only the most recent blocks.
- The terminated-run "Load more log" pagination is deliberately left
**uncapped** (guarded by `isLive`), so no historical output is lost —
older output remains on the server and reachable there.
## Verification
- `vitest run src/lib/live-log-buffer.test.ts` → 6/6 pass.
- Existing suites `RunTranscriptView.test.tsx`,
`AgentDetail.instructions.test.tsx`, `useLiveRunTranscripts.test.tsx` →
22/22 pass.
- `tsc -b` (UI) → clean.
- Manual/behavioral: live runs tail the last ~1500 blocks; terminated
runs still render full history via "Load more log". Follow-up planned to
profile before/after with the Chrome DevTools MCP.
## Risks
Low risk. Changes only bound **in-memory state for live runs**; the
terminated-run paginated path is untouched (still uncapped, guarded by
`isLive`). No API, schema, or persistence changes. Worst case for a live
run is that only the most recent 5000 lines / 1500 rendered blocks are
visible in the tab — which is the intended "tail" behavior, and full
history remains on the server.
## Model Used
- **Provider:** Anthropic, via the Claude Code CLI.
- **Model:** Claude Opus 4.8 (`claude-opus-4-8`).
- **Reasoning mode:** Extended thinking enabled.
- **Capabilities used:** tool use (shell execution, file editing),
sub-agent fan-out for the codebase memory sweep, and the Chrome DevTools
MCP for the diagnosis phase.
## 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 (the ROADMAP "Memory" item is about company/agent
knowledge, unrelated to this browser-tab memory fix)
- [x] I have searched GitHub for duplicate or related PRs and linked
them above (none found among open PRs)
- [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 considered and documented any risks above
- [ ] I have updated relevant documentation to reflect my changes (N/A —
no user-facing docs affected; rationale is documented inline in
`live-log-buffer.ts`)
- [ ] All Paperclip CI gates are green (in progress at time of writing)
- [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
(the only open P2 was this missing template, which this update resolves;
awaiting re-review)
- [x] I will address all Greptile and reviewer comments before
requesting merge
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Bumps [react-i18next](https://github.com/i18next/react-i18next) from
17.0.8 to 17.0.9.
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/i18next/react-i18next/blob/master/CHANGELOG.md">react-i18next's
changelog</a>.</em></p>
<blockquote>
<h2>17.0.9</h2>
<ul>
<li>fix: allow TypeScript 7 in the optional <code>typescript</code> peer
dependency range (<code>^5 || ^6 || ^7</code>). With
<code>typescript@7.0.2</code> in a project, <code>npm install</code>
failed with an <code>ERESOLVE</code> peer conflict. Fixes <a
href="https://redirect.github.com/i18next/react-i18next/issues/1927">#1927</a>,
thanks <a
href="https://github.com/andikapradanaarif"><code>@andikapradanaarif</code></a>.</li>
<li>fix(types): <code><Trans t={t} ns="ns" …></code>
with a <code>t</code> from <code>useTranslation(['ns'])</code> now
typechecks under TypeScript 7. TS7 intersects the <code>Ns</code>
inference candidates coming from the <code>t</code> prop (<code>readonly
['ns']</code>) and the <code>ns</code> prop (<code>'ns'</code>) into an
unsatisfiable <code>'ns' & readonly ['ns']</code>, where TS6
resolved them. The <code>ns</code> prop on <code>TransProps</code>,
<code>TransSelectorProps</code> and
<code>IcuTransWithoutContextProps</code> now also accepts a single
namespace out of an array-typed <code>Ns</code> (<code>Ns | (Ns extends
readonly (infer S extends string)[] ? S : never)</code>) — which matches
runtime behavior and is unchanged under TS5/TS6.</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="8b4a9ea139"><code>8b4a9ea</code></a>
17.0.9</li>
<li><a
href="422bab13d4"><code>422bab1</code></a>
fix: support typescript 7 — widen peer range and fix Trans ns inference
under...</li>
<li><a
href="6e18aa95b5"><code>6e18aa9</code></a>
README: mention npx i18next-cli localize as the zero-to-localized
path</li>
<li>See full diff in <a
href="https://github.com/i18next/react-i18next/compare/v17.0.8...v17.0.9">compare
view</a></li>
</ul>
</details>
<br />
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
<details>
<summary>Dependabot commands and options</summary>
<br />
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
</details>
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps
[@storybook/react-vite](https://github.com/storybookjs/storybook/tree/HEAD/code/frameworks/react-vite)
from 10.4.6 to 10.5.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/storybookjs/storybook/releases">@storybook/react-vite's
releases</a>.</em></p>
<blockquote>
<h2>v10.5.0</h2>
<h2>10.5.0</h2>
<blockquote>
<p><em>Foundational changes for new AI workflows</em></p>
</blockquote>
<p>Storybook 10.5 contains hundreds of fixes and improvements:</p>
<ul>
<li>⚡️ Angular-vite framework: Modern, fast dev, docs, and test
(preview)</li>
<li>🌈 Vitest initialGlobals: Test across themes, viewports, locales</li>
<li>🤖 Agentic review: AI-curated visual changesets and search results
(experimental)</li>
<li>⚛️ React docgen service: Unified metadata across MCP, Docs, and
Controls (experimental)</li>
<li>🧑💻 Claude / Codex plugins: One-click ADE integration
(experimental)</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md">@storybook/react-vite's
changelog</a>.</em></p>
<blockquote>
<h2>10.5.0</h2>
<blockquote>
<p><em>Foundational changes for new AI workflows</em></p>
</blockquote>
<p>Storybook 10.5 contains hundreds of fixes and improvements:</p>
<ul>
<li>⚡️ Angular-vite framework: Modern, fast dev, docs, and test
(preview)</li>
<li>🌈 Vitest initialGlobals: Test across themes, viewports, locales</li>
<li>🤖 Agentic review: AI-curated visual changesets and search results
(experimental)</li>
<li>⚛️ React docgen service: Unified metadata across MCP, Docs, and
Controls (experimental)</li>
<li>🧑💻 Claude / Codex plugins: One-click ADE integration
(experimental)</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="9dafcd22ed"><code>9dafcd2</code></a>
Bump version from "10.5.0-beta.2" to "10.5.0" [skip
ci]</li>
<li><a
href="448db85e65"><code>448db85</code></a>
Bump version from "10.5.0-beta.1" to "10.5.0-beta.2"
[skip ci]</li>
<li><a
href="a4ce9790a7"><code>a4ce979</code></a>
Bump version from "10.5.0-beta.0" to "10.5.0-beta.1"
[skip ci]</li>
<li><a
href="f0bf138a0a"><code>f0bf138</code></a>
Bump version from "10.5.0-alpha.11" to
"10.5.0-beta.0" [skip ci]</li>
<li><a
href="fac05a5741"><code>fac05a5</code></a>
Bump version from "10.5.0-alpha.10" to
"10.5.0-alpha.11" [skip ci]</li>
<li><a
href="4057c4169f"><code>4057c41</code></a>
Bump version from "10.5.0-alpha.9" to
"10.5.0-alpha.10" [skip ci]</li>
<li><a
href="da84210b49"><code>da84210</code></a>
Bump version from "10.5.0-alpha.8" to
"10.5.0-alpha.9" [skip ci]</li>
<li><a
href="c347410b9f"><code>c347410</code></a>
Bump version from "10.5.0-alpha.7" to
"10.5.0-alpha.8" [skip ci]</li>
<li><a
href="d16ab3008a"><code>d16ab30</code></a>
React: Align react-docgen versions</li>
<li><a
href="c9a1ac9f72"><code>c9a1ac9</code></a>
Bump version from "10.5.0-alpha.6" to
"10.5.0-alpha.7" [skip ci]</li>
<li>Additional commits viewable in <a
href="https://github.com/storybookjs/storybook/commits/v10.5.0/code/frameworks/react-vite">compare
view</a></li>
</ul>
</details>
<br />
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
<details>
<summary>Dependabot commands and options</summary>
<br />
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
</details>
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
## Thinking Path
> - Paperclip is the open source control plane people use to manage
AI-agent companies
> - Operators need to identify the exact build running from the
persistent account menu
> - Formal releases already have a concise public version, but source
builds include a long derived version string
> - The derived version identifies a commit but does not expose the
source branch or a direct path to inspect the code
> - Server Git metadata is auth-sensitive, so the UI must also refresh
it when the current session changes
> - This pull request shows linked branch and commit metadata for source
builds while preserving `v<version>` for formal releases
> - The benefit is faster build diagnosis with correct metadata across
sign-in and sign-out transitions
## Linked Issues or Issue Description
### Pre-submission checklist
- [x] I searched existing open and closed issues and found no duplicate
implementing this exact account-menu behavior.
- [x] The behavior reproduces on `master`.
- [x] The behavior originates in Paperclip's core UI, not an adapter,
provider, or local configuration.
### What happened?
Source builds displayed the full derived version, such as
`2026.626.0+58.git.518fc71ce`, without linking the operator to the
corresponding source branch or commit.
### Expected behavior
Source builds should show the concise branch and short commit SHA with
links to GitHub, while formal releases should continue showing their
public version. Auth transitions should refresh the health metadata that
supplies those Git details.
### Steps to reproduce
1. Run Paperclip from a commit after a release tag.
2. Open the account menu.
3. Inspect the build label beneath the user identity.
4. Sign in or out and reopen the menu.
### Paperclip version or commit
Any source build whose server version uses the
`<version>+<count>.git.<sha>[.dirty]` format.
### Deployment mode
Local dev (`pnpm dev`) or authenticated deployments.
### Installation method
Built from source.
## What Changed
- Detect source-derived version strings and render the source branch
plus seven-character commit SHA in `SidebarAccountMenu`.
- Link source branches and commits to the canonical
`paperclipai/paperclip` GitHub repository.
- Extend server Git metadata with the full SHA and expose it through
health/OpenAPI contracts.
- Refresh auth-sensitive health metadata after sign-in and every
sign-out entry point.
- Preserve the existing `v<version>` label for formal releases and add
focused regression coverage.
## Verification
- `pnpm --filter @paperclipai/ui exec vitest run src/pages/Auth.test.tsx
src/components/SidebarAccountMenu.test.tsx
src/components/SidebarServerInfo.test.tsx`
- `pnpm --filter @paperclipai/server exec vitest run
src/__tests__/health.test.ts src/__tests__/server-info.test.ts`
- `pnpm --filter @paperclipai/ui typecheck`
- `pnpm check:token-gates`
- `git diff --check public/master...HEAD`
## Risks
- Low risk: formal release rendering retains the existing fallback
behavior when the source-version pattern does not match.
- Source links assume the build came from the canonical public
repository; fork-only branches or commits may not resolve there.
- Health metadata is invalidated after auth transitions, adding one
bounded refetch so the displayed Git details match the new session.
> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.
## Model Used
- OpenAI Codex using GPT-5.4 with medium reasoning, repository/tool
access, shell execution, and code editing; context-window size was not
exposed by the runtime.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - Operators tune instance behavior through Settings → Experiments,
where experimental features are toggled on and off
> - The task graph liveness auto-recovery experiment shows a
confirmation dialog (preview of what would be recovered) before it is
enabled
> - After confirming with "Enable only" or "Enable and run", the
dialog's Radix overlay and the `pointer-events: none` body lock were
left behind, dimming the page and blocking all interaction until a
refresh
> - The dialog was unconditionally mounted and only closed inside the
mutation's `onSuccess`, so the overlay teardown depended on the mutation
outcome and could race or never happen
> - This pull request closes the dialog before the mutation fires in
both confirm flows, clears the pending preview alongside the open flag,
and mounts the dialog conditionally so its overlay fully unmounts
> - The benefit is that enabling an experiment behaves like every other
settings change: the dialog goes away, the page stays interactive, and
errors surface in the page-level error banner instead of a dead UI
## Linked Issues or Issue Description
No public GitHub issue exists for this bug; description follows the bug
report template. Refs #4587 (the PR that introduced the configurable
liveness auto-recovery controls this dialog belongs to).
**What happened?** In Settings → Experiments, toggling on "Task graph
liveness auto-recovery" and confirming via "Enable only" left the whole
UI dimmed and unclickable. The dialog content disappeared, but the modal
overlay and the `pointer-events: none` lock on `<body>` remained until a
full page refresh.
**Expected behavior:** Confirming (or dismissing) the auto-recovery
dialog should close it completely and return the page to a fully
interactive state, with the toggle reflecting the new setting.
**Steps to reproduce:**
1. Open Settings → Experiments.
2. Toggle on "Task graph liveness auto-recovery"; the confirmation
dialog with the recovery preview appears.
3. Click "Enable only".
4. The dialog content disappears but the page stays dimmed and nothing
is clickable; refreshing restores the UI and shows the setting was
applied.
**Paperclip version or commit:** master @ 634ae12 · **Deployment mode:**
local instance · **Area:** UI only
(`ui/src/pages/InstanceExperimentalSettings.tsx`).
## What Changed
- Added a `closeRecoveryPreview()` helper that resets both
`previewDialogOpen` and `pendingPreview` together, and used it
everywhere the dialog closes (confirm flows, run-mutation success, and
user dismissal).
- "Enable only" and "Enable and run" now close the dialog *before*
firing the mutation, so overlay teardown no longer depends on the
mutation outcome; mutation errors roll back the optimistic toggle and
surface in the existing page-level error banner.
- The `RecoveryPreviewDialog` is now conditionally mounted
(`previewDialogOpen ? <RecoveryPreviewDialog … /> : null`), guaranteeing
the Radix overlay and body pointer-events lock are fully removed when
closed.
- Added a regression test that walks the real flow — toggle on → preview
dialog appears → "Enable only" — and asserts the update payload, that
the dialog text and `[data-slot="dialog-overlay"]` element are gone, and
that the toggle reads enabled.
Credit: the implementation commit was authored by Cody — thanks! This PR
packages that fix for upstream review.
## Verification
- `pnpm vitest run src/pages/InstanceExperimentalSettings.test.tsx` in
`ui/` — 17/17 tests pass, including the new regression test.
- Independently re-verified beyond the committed assertions: with a
temporary assertion (not committed), confirmed
`document.body.style.pointerEvents` is `none` while the dialog is open
and released after "Enable only" — the actual "can't interact with
anything" symptom, not just overlay DOM removal.
- Manual check: open Settings → Experiments, toggle the auto-recovery
feature, click "Enable only" — the dialog closes, the page stays
interactive, and the toggle shows enabled without a refresh.
## Risks
- Low risk: change is confined to one page component's dialog lifecycle;
no API, schema, or shared-package changes.
- Behavioral shift: the dialog now closes immediately on confirm instead
of staying open with a pending spinner until the mutation resolves.
Errors are still surfaced via the page-level error banner, and the
optimistic toggle rolls back on failure.
## Model Used
- Implementation commit authored by the AI coding agent "Cody"
(Anthropic Claude-based agent). Review, independent verification, and PR
preparation by Claude (Anthropic), model ID `claude-fable-5`, extended
thinking with tool use (code execution, test runs).
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (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: Cody <noreply@paperclip.ing>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - The web UI leans on a shared design-token + component system so
surfaces stay visually consistent as they grow
> - Two small inconsistencies had crept in: several distinct blues were
used to signal "live/running" agent state across the sidebar, task
header, and chat thread; and in the Inbox an unread task's mark-read dot
was pushing that row's status icon and title one column right of read
rows
> - Both read as "not quite aligned" in daily use and undercut the
polish of the lists work that just landed
> - This pull request consolidates the live/running blues onto one
shared recipe and stops the unread dot from indenting the row
> - The benefit is one consistent "live" blue everywhere and Inbox rows
that line up whether read or unread
## Linked Issues or Issue Description
No public GitHub issue exists for this work; describing inline per the
bug-report template.
- **Problem**: (1) the same concept — an agent actively working —
rendered in three visibly different blues: the sidebar `N live` dot, the
task-detail "Live" badge, and the chat-thread "RUNNING" badge each used
a different token/recipe. (2) In the Inbox, unread rows carry a leading
mark-read dot that occupies the chevron column, but a per-row spacer was
still rendering in that same column — so on unread rows the status icon
+ title were shifted one column (~24px) further right than read rows.
Most visible when grouped by workspace.
- **Steps to reproduce**: open the Inbox with a mix of read and unread
tasks (group by workspace). The unread rows' status icons sit further
right than the read rows'. Separately, compare the blue of the sidebar
`N live` dot, a task's "Live" header badge, and a chat "RUNNING" badge —
they don't match.
- **Expected behavior**: unread and read rows align on the same status
column, with the unread dot centered on the workspace group chevron; and
all three "live/running" affordances share one blue.
## What Changed
- Added a shared `liveBlueBadge` recipe in `ui/src/lib/status-colors.ts`
and pointed the task-detail **Live** badge (`IssueDetail.tsx`) and the
chat-thread **RUNNING** badge (`IssueChatThread.tsx`) at it; removed the
now-redundant `brandChipBadge` usage from the chat thread and a stray
`🔵` breadcrumb prefix.
- Changed the sidebar **`N live`** dot (`SidebarNavItem.tsx`) to the
same `blue-600 / dark:blue-400` as its adjacent label text.
- **Inbox** (`Inbox.tsx`): skip the per-row leading spacer when the
unread mark-read dot is present, so the dot alone fills the chevron
column. Unread rows' status icon + title now sit in the same column as
read rows, and the dot centers on the workspace group chevron.
- **Test** (`Inbox.test.tsx`): added a regression test asserting an
unread leaf row renders the mark-read dot and drops the spacer, while a
read row keeps the spacer.
## Verification
- `pnpm typecheck` — clean (all packages)
- `pnpm check:token-gates` — 3/3 CLEAN
- `cd ui && pnpm vitest run src/pages/Inbox.test.tsx` — 14/14 (includes
the new regression test)
- Full Storybook visual suite (514 stories, both themes) — green locally
(CI cannot run this suite yet — the baseline-manifest archive is
unpublished, a pre-existing condition from #9134)
- Manual (workspace-grouped Inbox, 2× dark): measured the unread badge
center at the same x as the workspace chevron (276 = 276) and the
unread-row status icon at the same x as read-row status icons (292 =
292). Before/after screenshots in a PR comment below.
## Risks
Low risk — presentation only. No data, routing, or state changes. The
blue consolidation is a token/class swap; the Inbox change removes a
redundant spacer element on unread rows only (read rows and
non-grouped/mobile views are unaffected). The unread-row behavior is
covered by the new unit test.
## Model Used
Claude (Anthropic), Opus 4.8 — model id `claude-opus-4-8`; extended
thinking + tool use, driving local verification (typecheck, token gates,
vitest, Playwright visual suite + pixel measurements).
## 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: Claude Fable 5 <noreply@anthropic.com>
## Thinking Path
> - Paperclip is the open source control plane people use to coordinate
AI-agent companies
> - Human operators use the Decisions attention queue to review and
resolve work that needs them
> - Decision rows were composed as a fixed content column plus a
right-side controls column
> - At phone widths, timestamps, actions, menus, and evidence thumbnails
compressed the decision headline until it was barely readable
> - This pull request makes each row respond to its own container width
and stacks metadata, content, evidence, and actions on narrow surfaces
while preserving the dense desktop layout
> - The benefit is a useful, thumb-reachable Decisions workflow on
phones and narrow side panels without regressing wide-screen density or
scrolling performance
## Linked Issues or Issue Description
No public GitHub issue exactly matches this bug, so it is described here
using the bug-report fields.
**What happened**
Decision rows used a fixed two-column layout. On narrow screens, the
right-hand timestamp, overflow menu, decision buttons, and optional
thumbnails squeezed the headline into a truncated sliver.
**Expected behavior**
Decision headlines should remain readable on mobile, supporting context
should flow below the headline, and primary actions should remain easy
to tap. Wide rows should retain the compact desktop presentation.
**Steps to reproduce**
1. Open the Decisions / What needs me surface with populated attention
items.
2. Reduce the row container to a phone-width layout (approximately
390px).
3. Observe rows with multiple actions or evidence thumbnails.
**Paperclip version / deployment mode**
Current `master`, board UI in local or hosted deployments.
**Related public work found during dedup search**
- Refs: #9311 — original What needs me attention queue work.
- Refs: #9468 — recent Decisions scrolling performance work preserved by
this change.
## What Changed
- Reworked `AttentionQueueRow` into a container-query-driven vertical
stack on narrow surfaces, with the existing compact layout restored at
wide row widths.
- Made decision titles wrap to two lines, moved project/evidence context
below the headline, and promoted actions to full-width mobile tap
targets.
- Preserved upstream row memoization and `content-visibility` scrolling
optimizations while rebasing onto current `master`.
- Added three 390px Storybook scenarios covering populated rows,
type/detail variants, and snoozed/dismissed curtains.
- Updated the focused row test to assert the new thumbnail/context
alignment.
## Verification
- `pnpm exec vitest run ui/src/components/AttentionQueueRow.test.tsx` —
1 file passed, 16 tests passed.
- `pnpm check:token-gates` — all token gates clean.
- `pnpm --filter @paperclipai/ui typecheck` — passed.
- `pnpm --filter @paperclipai/ui build-storybook` — completed
successfully.
- `git diff --check public/master...HEAD` — passed.
## Risks
- Low risk: the behavior is isolated to the Decisions row presentation
and its Storybook coverage.
- Container-query breakpoints could need future visual tuning for
unusual embedded widths, but the wide layout remains available at the
row-level breakpoint.
- The mobile layout increases row height by design in exchange for
readable content and usable actions.
> 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 CLI coding agent. The exact model ID and context-window
size are not exposed to this runtime; reasoning, repository editing,
shell execution, and test execution capabilities 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>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - Operators use the Decisions page to review an uncapped attention
feed across active, snoozed, and dismissed items
> - Large feeds mounted every row eagerly, and routine interactions
re-rendered the full queue
> - That made initial paint and scrolling progressively slower as
decision history accumulated
> - This pull request bounds rendering, stabilizes row props, and lets
off-screen rows skip layout and paint work
> - The benefit is a responsive Decisions page even for companies with
large attention histories
## Linked Issues or Issue Description
### What happened?
Opening `/decisions` for a company with a large attention history
eagerly mounted every visible-feed row. Expanding, selecting,
dismissing, snoozing, or restoring an item could also re-render the
entire queue.
### Expected behavior
The page should render a bounded initial window, progressively reveal
more rows near the scroll boundary, and avoid re-rendering unaffected
rows during interactions.
### Steps to reproduce
1. Populate a company with hundreds of attention items.
2. Open `/decisions`.
3. Scroll and interact with individual rows.
4. Observe increasing initial render, layout, paint, and interaction
cost on the previous implementation.
### Paperclip version or commit
Reproduced on `master` before this PR.
### Deployment and installation
Local development, built from source. This is a core UI issue, not
adapter- or database-specific.
### Additional context
Searched open public issues and PRs; no duplicate was found.
## What Changed
- Added a pure `planAttentionRenderRows` helper that allocates one
render budget across active groups and open snoozed/dismissed curtains
in document order.
- Render 50 rows initially and add 100 more when the Decisions page
approaches the scroll boundary.
- Memoized `AttentionQueueRow`, stabilized parent callbacks and inbox
dismissal actions, and passed row items through a shared expand
callback.
- Added `content-visibility: auto` and intrinsic containment so
accumulated off-screen rows avoid unnecessary layout and paint work.
- Added render-plan coverage and a regression test proving identical row
props do not re-render after a parent update.
## Verification
- `pnpm -C ui typecheck`
- `pnpm -C ui exec vitest run src/lib/attention.test.ts
src/components/AttentionQueueRow.test.tsx
src/components/Sidebar.test.tsx src/pages/Inbox.test.tsx` — 96 tests
passed
- `pnpm check:token-gates` — all gates clean
## Risks
- Low risk: the change is UI-only and does not alter API or database
contracts.
- The main behavioral risk is incorrect row-budget accounting across
collapsed groups or open curtains; the pure planner has focused tests
for ordering, truncation, and collapsed/closed sections.
- Progressive rendering means rows beyond the current budget are
intentionally absent until scrolling nears the boundary, matching the
existing Issues list pattern.
> This is a targeted performance fix and does not overlap planned core
feature work in `ROADMAP.md`.
## Model Used
- Anthropic Claude Fable 5 assisted with the implementation using
repository tools and code execution.
- OpenAI Codex `gpt-5.6-sol` prepared and verified the PR with high
reasoning effort, repository tools, shell execution, and
GitHub/Paperclip API access. 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 changes were required for this UI-only behavior)
- [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 Fable 5 <noreply@anthropic.com>
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - Execution workspaces can run managed services that must report
reliable lifecycle and readiness state
> - Service startup previously waited for readiness before committing
the starting row, making concurrent control actions see stale state
> - Fixed service ports also needed clearer configuration and ownership
diagnostics to avoid cross-workspace collisions
> - This pull request persists startup state before readiness, validates
port ownership, and exposes configurable service ports in the workspace
UI
> - The benefit is dependable service controls and actionable
diagnostics when workspace runtimes start slowly or compete for ports
## Linked Issues or Issue Description
### What happened?
Slow-starting workspace services could remain invisible to concurrent
stop/restart controls until readiness completed, and fixed-port
conflicts lacked enough ownership context for safe repair.
### Expected behavior
A starting service is persisted immediately, control operations can
observe it, configured ports are editable, and conflicts identify the
owning process/workspace.
### Steps to reproduce
1. Configure a workspace service that delays binding its HTTP port.
2. Start the service and immediately request another control action.
3. Observe stale persisted state before this change.
4. Configure two workspaces for the same fixed port and observe limited
conflict diagnostics.
### Paperclip version or commit
`origin/master` at `02e2dd271`
### Deployment mode
Local dev; built from source; not adapter-specific; database-backed
workspace runtime state.
## What Changed
- Commit the `starting` runtime-service row before waiting for readiness
and transition it after the probe completes.
- Add port-owner inspection and cross-workspace conflict details to
local service supervision.
- Preserve configurable runtime service ports through workspace
configuration updates.
- Surface service-port editing and validation in the execution workspace
details UI.
- Add server and UI regression coverage for slow readiness, concurrent
controls, port persistence, and conflict diagnostics.
## Verification
- `vitest --project @paperclipai/server
src/__tests__/workspace-runtime.test.ts
src/__tests__/execution-workspaces-service.test.ts` — 118 tests passed.
- `vitest --project @paperclipai/ui
src/pages/ExecutionWorkspaceDetail.service-ports.test.ts` — 4 tests
passed.
- `node scripts/check-token-gates.mjs` — all token gates clean.
## Risks
- Moderate risk: changes touch workspace service lifecycle persistence
and local process/port inspection.
- No schema migration is required; tests exercise slow readiness,
concurrent control, persisted ports, and cross-workspace conflicts.
> 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.3 Codex, reasoning with repository tool use and
code execution; context-window size was not exposed by the runtime.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - The board coordinates repeated API polling across tabs to reduce
redundant requests
> - The shared polling coordinator retained cached result and
publication entries after the last subscriber left
> - Dynamic polling keys could therefore grow those maps for the
lifetime of the page
> - This pull request evicts inactive keys while preserving useful
short-lived handoff state and request deduplication
> - The benefit is bounded client memory without regressing cross-tab
polling behavior
## Linked Issues or Issue Description
### What happened?
Shared polling cached result/publication entries indefinitely after a
polling key no longer had subscribers.
### Expected behavior
Inactive keys are eventually removed, while recently published values
remain available long enough for normal subscriber handoff.
### Steps to reproduce
1. Create and unsubscribe many distinct shared polling keys in one page
lifetime.
2. Inspect the coordinator's cached results and publication timestamps.
3. Observe that the old maps retain every historical key.
### Paperclip version or commit
`origin/master` at `02e2dd271`
### Deployment mode
Local dev; built from source; not adapter-specific; not
database-related.
## What Changed
- Track inactive polling keys and schedule bounded cache eviction.
- Preserve cached data while a key is active or inside its retention
window.
- Cancel stale cleanup timers when polling resumes and clear coordinator
caches during disposal.
- Add focused fake-timer coverage for retention, resubscription, and
disposal behavior.
## Verification
- `vitest --project @paperclipai/ui src/lib/cross-tab-poll.test.ts` — 11
tests passed.
## Risks
- Low-to-moderate risk: eviction timing affects client polling
coordination.
- Tests cover the retention boundary, resumed subscriptions, and
coordinator cleanup to reduce regression risk.
> 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.3 Codex, reasoning with repository tool use and
code execution; context-window size was not exposed by the runtime.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - Company skills expose their source metadata in a narrow details
sidebar
> - Long filesystem paths and repository locators were truncated, hiding
the part operators often need to distinguish sources
> - The sidebar can preserve the complete value by wrapping at arbitrary
path boundaries instead of ellipsizing it
> - This pull request renders full source paths and repository labels
without widening the layout
> - The benefit is that operators can inspect and copy the actual skill
source from the UI
## Linked Issues or Issue Description
### Pre-submission checklist
- [x] I have searched existing open and closed issues and this is not a
duplicate.
- [x] I can reproduce this on `master`.
- [x] I have confirmed the behavior originates in Paperclip itself, not
an agent adapter, API provider, or local configuration.
### What happened?
Long company-skill source paths and repository locators were truncated
in the skill details sidebar.
### Expected behavior
The complete source value remains visible and wraps within the available
sidebar width.
### Steps to reproduce
1. Open a company skill whose source path is longer than the details
sidebar.
2. View the Source field.
3. Observe that the old UI replaces the middle or end of the value with
an ellipsis.
### Paperclip version or commit
`origin/master` at `02e2dd271`.
### Deployment mode
Local dev (`pnpm dev`).
### Installation method
Built from source (`pnpm dev` / `pnpm build`).
### Agent adapter(s) involved
None; this is a company-skills UI layout issue.
### Logs, configuration, or screenshots
Not applicable; the behavior is directly visible in the Source field.
### Additional context
The narrow sidebar should remain width-constrained. Wrapping
intentionally trades vertical space for full source inspectability.
## What Changed
- Replace source-path truncation with width-constrained arbitrary
wrapping.
- Apply the same wrapping behavior to linked repository/source labels.
- Add a regression test proving the full long path is rendered without
ellipsis.
## Verification
- `vitest --project @paperclipai/ui src/pages/CompanySkills.test.tsx` —
11 tests passed.
- `node scripts/check-token-gates.mjs` — all token gates clean.
## Risks
- Low risk: the change is limited to text layout in the company skill
details view.
- Very long unbroken values may make the Source section taller,
intentionally trading vertical space for inspectability.
> 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.3 Codex, reasoning with repository tool use and
code execution; context-window size was not exposed by the runtime.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [ ] 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>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - Operators scan task state constantly, so the task **status**
vocabulary (backlog / todo / in progress / in review / done / blocked /
cancelled) has to read instantly
> - Those statuses render through one shared component, `StatusGlyph`,
whose icons were hand-rolled SVG geometry lifted from an internal spec
> - Hand-rolled glyphs are harder to reason about, drift from the rest
of the UI (which uses Lucide everywhere else), and mix fill/stroke
styles across statuses
> - This pull request swaps the hand-rolled geometry for named Lucide
icons — one clean, consistent icon family — with no change to colours,
sizing, or accessibility
> - The benefit is a status icon set that is consistent with the rest of
the app's iconography, trivially adjustable (change a mapping, not SVG
path math), and simpler to maintain
## Linked Issues or Issue Description
No existing public GitHub issue. Describing the change in-PR
(feature/polish):
**Problem / motivation.** The task status icons in `StatusGlyph` were
bespoke inline SVGs (a half-filled disc for *in progress*, a filled disc
+ knockout check for *done*, ring+bar for *blocked*, ring+slash for
*cancelled*, etc.). The rest of the UI uses [Lucide](https://lucide.dev)
icons, so the status set was the odd one out — and its mixed fill/stroke
shapes were harder to scan and to tweak.
**Proposed solution.** Map each status to a Lucide icon and render that
instead:
| Status | Lucide icon |
| --- | --- |
| backlog | `circle-dashed` |
| todo | `circle` |
| in_progress | `rotate-cw` |
| in_review | `circle-dot` |
| done | `circle-check` |
| blocked | `circle-minus` |
| cancelled | `ban` |
| in_queue (covered-blocked) | `circle-minus`, recoloured blue |
Colours (the `--status-task-icon-*` tokens), the `sm/md/lg` size scale,
`currentColor` recolouring, and the `role="img"` / `aria-label`
behaviour are all unchanged — only the shapes change.
**Alternatives considered.** Keeping the bespoke geometry (rejected:
inconsistent with the app and harder to maintain).
**Related PRs** (linked for reviewer context, not dependencies):
- Refs #8580 — the merged PR that established the current hand-rolled
status glyphs this PR restyles.
- Refs #8838 — open PR forwarding Radix trigger props through
`StatusGlyph`; touches the same component (no overlap with this change).
- Refs #1760 — open proposal to redesign the *cancelled* status icon
specifically; this PR moves cancelled to Lucide `ban`.
## What Changed
- `ui/src/components/StatusGlyph.tsx`: replaced the per-status
hand-rolled SVG `glyphBody()` geometry with a `status → Lucide icon` map
(`circle-dashed`, `circle`, `rotate-cw`, `circle-dot`, `circle-check`,
`circle-minus`, `ban`). Kept the token-driven colour wiring, size scale,
`currentColor` recolouring, a11y label handling, and the `in_queue` =
blocked-icon-recoloured-blue behaviour.
- `ui/src/components/StatusGlyph.test.tsx`: updated to lock the new icon
mapping (per-status Lucide class, size scale, colour var, `in_queue`,
a11y) instead of the old geometry.
Net: two files, +74 / −138 (the component got smaller). Because every
status surface (list, board, detail header, status picker,
sub-task/blocked-by pills, chips) routes through `StatusGlyph`, this
single-component edit covers them all.
## Verification
- `pnpm check:token-gates` → **3/3 clean** (no hardcoded
colour/spacing/font values introduced).
- `pnpm typecheck` → clean across all packages.
- `cd ui && pnpm vitest run` → **2509/2509 passing**, including the
updated `StatusGlyph` test.
- Manual: ran the worktree dev server and confirmed the new icons render
everywhere (task list, task detail, related-task chips, and the status
picker showing all seven).
**Storybook visual-regression note:** this is an intentional visual
change, so the status-icon stories will diff against the published
baseline. The baseline snapshots need to be regenerated and republished
by a maintainer (`pnpm test:storybook-visual:update` from a trusted
environment) as part of accepting this change — the visual-regression CI
check is expected to be red until then. No baseline is published in the
environment this PR was authored in, so that step is left to a
maintainer.
## Risks
- **Low risk / cosmetic.** No logic, data, or API changes — only the
rendered icon shapes. Colours, sizes, and accessibility labels are
unchanged.
- The most noticeable shifts are *in progress* (half-disc → rotating
arrow), *done* (solid disc+check → outline circle+check), and
*cancelled* (ring+slash → ban). These are deliberate.
- The only CI check expected to fail is the Storybook visual-regression
job, pending a maintainer baseline update (see Verification).
## Model Used
Claude Opus 4.8 (`claude-opus-4-8`), run in Claude Code with extended
thinking and tool use (file edits, local test runs, browser-driven
visual 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
- [ ] 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>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - Instance settings include an Environments section where operators
configure execution environments, each with an environment-variables
editor for run-time bindings
> - The editor showed a bare "Unsaved changes" banner that never said
which variables changed, sometimes appeared the moment a saved config
was opened (a lossy round-trip through the editor's emit rules made
clean values look dirty), and the environment form let you navigate away
without any confirmation, silently dropping the draft
> - Operators could not tell what was unsaved, distrusted the phantom
banner, and lost half-finished environment edits to a stray click — the
agent configuration page already confirms before discarding, so
environments behaved inconsistently
> - This pull request lists the new/edited/removed variable names under
the banner, normalizes both sides of the dirty comparison so saved
values no longer look dirty on open, and confirms before cancel, in-app
navigation, or tab unload while the form has unsaved changes
> - The benefit is that the banner is trustworthy and specific, and
unsaved environment edits can no longer be lost without an explicit
confirmation
## Linked Issues or Issue Description
Related (not fixed by this PR): #8930 introduced the current
environment-variables editor and its unsaved-changes banner; #9386 moved
environment create/edit from a modal to routed pages, which this PR's
navigation guard builds on.
No existing public issue for the defects themselves; described per the
bug report template:
**What happened?**
The environment-variables editor in Environments settings showed a bare
"Unsaved changes" banner with no indication of which variables changed.
For some saved configurations (names with surrounding whitespace,
incomplete secret references, duplicate names differing only by
whitespace) the banner appeared immediately on opening the edit form,
before any user input. Navigating away from the environment form —
cancel, an in-app link, or closing the tab — silently discarded the
draft with no confirmation.
**Expected behavior**
The banner should say which variables are new, edited, or removed; a
freshly opened saved configuration should show no banner; and leaving
the form with unsaved changes should require an explicit confirmation,
consistent with the agent configuration page.
**Steps to reproduce**
1. Open Settings → Instance settings → Environments and edit an
environment whose saved config round-trips lossily (e.g. an env var name
stored with trailing whitespace) — the "Unsaved changes" banner appears
with no user edits.
2. Add or edit a variable — the banner gives no hint of what is unsaved.
3. With a dirty draft, click any in-app link or Cancel — the draft is
dropped with no confirmation.
**Deployment mode**
Self-hosted (local development instance), reproducible on `master`.
## What Changed
- The unsaved-changes banner in `EnvironmentVariablesEditor` now renders
a change summary line — `New: … · Edited: … · Removed: …` — showing up
to three names per group with a `+N more` overflow and the full list in
a `title` tooltip. A rename shows as one addition plus one removal.
- Dirty detection normalizes both the committed value and the draft
through the same rules the editor uses when emitting values (trimmed
names, incomplete secret refs dropped, last-writer-wins on trimmed
duplicates), so a saved config that round-trips lossily no longer shows
a phantom banner on first open.
- The editor exposes an `onDirtyChange` callback and warns via
`beforeunload` while its local draft is dirty.
- The environment create/edit page (`CompanyEnvironments`) tracks a
payload-level baseline fingerprint of the form as initialized and treats
the page as having unsaved changes when the current form differs from it
or the editor draft is dirty. While dirty it confirms ("Discard unsaved
environment changes?") on Cancel, intercepts same-origin in-app link
clicks, and warns on tab unload.
## Verification
- `node_modules/.bin/vitest run
ui/src/pages/CompanyEnvironments.test.tsx
ui/src/components/environment-variables-editor/EnvironmentVariablesEditor.test.tsx`
— 48 tests pass, including new coverage for: the change-summary banner
text, no phantom banner for lossy round-trip values, beforeunload only
while dirty, cancel confirmation on the edit page, and unload/link-click
warnings after edits are staged into the form.
- `tsc -b` in `ui/` passes.
- Manual: edit an environment, add/edit/remove variables, observe the
summary line; click Cancel or an in-app link and observe the
confirmation; save and observe navigation proceeds without prompting.
## Risks
- Low risk, UI-only. The click interceptor is scoped to same-origin
anchor navigation while the environment form page has unsaved changes
and is removed on cleanup; modified-key/middle-button clicks and
external links are left alone.
- The dirty-normalization intentionally ignores differences the editor
could never persist (incomplete secret refs, untrimmed duplicate names);
those were previously reported as unsaved changes that could not be
saved away.
## Model Used
- Claude Fable 5 (`claude-fable-5`, Anthropic) via Claude Code, with
extended thinking and tool use (code editing, 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
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - Instance settings include an Environments section where operators
configure sandbox/SSH/local execution environments, including
interactive custom-image setup sessions with a browser terminal
> - The environment create/edit form was rendered inside a modal dialog,
so pressing Escape anywhere — including inside the embedded SSH terminal
while capturing a snapshot — closed the whole modal and destroyed the
in-progress session
> - Environment editing is a heavyweight, long-lived flow; losing it to
a reflexive Escape keypress is destructive and surprising
> - This pull request converts environment create/edit from a modal into
routed standalone pages, so Escape no longer dismisses the form
> - The benefit is that terminal sessions and half-completed edits
survive Escape, and the flow gets shareable URLs and normal back/forward
navigation
## Linked Issues or Issue Description
No existing public issue; described per the bug report template:
**What happened?**
While editing an environment's sandbox snapshot in the embedded SSH
terminal, pressing Escape (e.g. to exit a mode inside the terminal)
closed the entire environment edit modal, discarding the setup session
and any unsaved form state.
**Expected behavior**
Escape inside the terminal or form should not dismiss the environment
editor. A heavyweight flow like environment configuration should be a
standalone page where Escape behaves as expected within the focused
widget.
**Steps to reproduce**
1. Open Instance settings → Environments and edit a sandbox environment
2. Start a custom image setup session and focus the browser terminal
3. Press Escape
4. The modal closes and the session context is lost
## What Changed
- Converted the environment create/edit dialog in
`CompanyEnvironments.tsx` into routed pages at
`/company/settings/instance/environments/new` and
`/company/settings/instance/environments/:environmentId/edit`
- Registered the new routes in `App.tsx` and wired breadcrumbs for the
list/create/edit states
- Form state now initializes from the route (create vs edit) instead of
dialog open/close state, and successful saves navigate back to the
environments list
- Updated `CompanyEnvironments.test.tsx` and `CompanySettings.test.tsx`
to render through a router with the new routes and assert against the
routed form page instead of a dialog
## Verification
- `pnpm vitest run ui/src/pages/CompanyEnvironments.test.tsx
ui/src/pages/CompanySettings.test.tsx` — 22/22 passing
- `tsc --noEmit` on the `ui` package — clean
- Behavioral coverage: the updated tests exercise the routed create/edit
pages end to end (open edit via the list, interact with the
setup-session controls on the form page, save navigates back to the
list); with the form no longer in a dialog there is no Escape-close
handler to trigger
## Risks
- Low risk; UI-only routing change. Deep links into the old modal state
do not exist (the modal had no URL), so no redirects are needed
- The edit page resolves the environment from the route param; a
stale/unknown id falls back to the environments list
## Model Used
- Claude (Anthropic), model ID `claude-fable-5` (Fable 5), extended
thinking enabled, agentic tool use via Claude Code
## 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: Cody <cody@paperclip.ing>
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - The board UI renders agent/user-authored Markdown in task
descriptions, comments, and other work-thread surfaces
> - Those Markdown surfaces often live inside cards and containers that
constrain overflow
> - Ordered-list markers are painted outside the list content box, so
too little inline padding can clip multi-digit markers at the left edge
> - This pull request keeps the shared Markdown list gutter compact
while giving ordered lists enough marker space for two- and three-digit
counters
> - The benefit is that long numbered lists in board-facing Markdown
render correctly without widening unordered-list gutters or changing
API, data, or editor behavior
## Linked Issues or Issue Description
No public GitHub issue found.
Bug description:
- What happened: rendered Markdown ordered lists with multi-digit items
could show clipped marker digits when the list was flush against an
overflow-constrained container.
- Expected behavior: ordered-list markers such as `10.` and `100.`
should render fully in task descriptions and comments.
- Steps to reproduce: render a `.paperclip-markdown` ordered list with
at least 100 items inside a container that clips overflow and has no
extra left gutter.
- Paperclip version/commit: current `master` before this PR.
- Deployment mode: board UI, deployment-mode independent.
Related search result:
- Refs #2049 because it also touches rendered Markdown list
presentation, but it styles GFM task-list checkboxes and does not
address ordered-list marker clipping.
## What Changed
- Set the shared `.paperclip-markdown` list padding to a compact
`1.5rem` baseline for bullets and lists.
- Added an ordered-list-only `2.5rem` padding override so
outside-positioned multi-digit ordered-list markers have enough
inline-start room.
- Added a focused stylesheet regression test that verifies
unordered-list gutters stay compact while ordered lists keep the larger
marker gutter.
- Restored the exact maintainer-skill marker phrase expected by the
existing server skill utility contract test, fixing an unrelated
latest-head CI failure from current `master`.
## Verification
- `pnpm --filter @paperclipai/ui exec vitest run
src/components/MarkdownListStyles.test.ts`
- `pnpm check:token-gates`
- `pnpm --filter @paperclipai/ui build`
- `pnpm exec vitest run
server/src/__tests__/paperclip-skill-utils.test.ts`
## Risks
- Low risk: ordered lists in rendered Markdown get a larger left gutter;
unordered lists keep a smaller shared gutter.
- Low risk: the skill-doc marker change is text-only and matches the
existing server test contract.
- No database, API, migration, auth, adapter, or telemetry 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 coding agent, tool-enabled software-engineering
session. Context window size was not exposed by the runtime.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip is the control plane for autonomous AI companies, where
operators need a reliable way to find and act on work awaiting their
input.
> - The attention and issue-thread interaction subsystems expose those
decision points across server APIs and the board UI.
> - The previous navigation and interaction presentation left these
actions fragmented and did not offer a controlled rollout for the
Decisions surface.
> - This branch adds the attention feed, richer interaction cards,
grouping, dismiss/snooze behavior, and a gated Decisions sidebar entry.
> - It also keeps experimental settings and API contracts synchronized,
with an idempotent migration for the new dismissal state.
> - This pull request delivers the complete, tested attention/Decisions
experience as one reviewable unit.
## Linked Issues or Issue Description
- Adds an operator-focused attention queue and Decisions experience:
grouped decision cards, semantic interaction actions, dismiss/snooze
handling, resilient interaction states, and an experimental flag to
control the Decisions navigation entry.
## Feature Context
### Problem or Motivation
Operators currently have to hunt across approvals, interactions, failed
runs, and budget alerts to find decisions that need their action.
### Proposed Solution
Provide a gated Decisions attention queue that groups actionable items,
supports direct resolution, and preserves operator control through
dismiss and snooze actions.
### Alternatives Considered
Keep separate, source-specific views only; this leaves cross-cutting
operator decisions fragmented and harder to prioritize.
### Roadmap Alignment
This improves the V1 control-plane operator workflow by making pending
governed actions discoverable in one company-scoped surface.
## What Changed
- Added server attention-feed services, routes, interaction handling,
dismiss/snooze support, and an idempotent `0145` inbox-dismissal
migration.
- Added shared attention, inbox-dismissal, and experimental-settings
contracts.
- Added Decisions/attention UI, interaction-card states, sidebar
badge/navigation integration, grouping, keyboard support, and Storybook
coverage.
- Added tests for attention behavior, thread interactions, settings
normalization, dismissals, and API behavior.
- Removed generated screenshots from the final PR diff and rebased the
branch onto current `master`.
## Verification
- `pnpm check:token-gates` — passed.
- `pnpm exec vitest run
packages/shared/src/issue-thread-interactions.test.ts
server/src/__tests__/attention-service.test.ts
server/src/__tests__/inbox-dismissals.test.ts
server/src/__tests__/issue-thread-interactions-service.test.ts
server/src/__tests__/issue-thread-interaction-routes.test.ts
ui/src/lib/attention.test.ts
ui/src/components/AttentionQueueRow.test.tsx
ui/src/components/IssueThreadInteractionCard.test.tsx
ui/src/pages/InstanceExperimentalSettings.test.tsx` — passed: 158 tests
across 9 focused files.
- GitHub Actions for `ad636f560`: build and typecheck/release-registry
have passed; remaining general-server and Greptile checks are in
progress.
## Risks
- Moderate: this is a cross-layer attention/interaction feature with a
new migration and navigation behavior.
- The `enableDecisions` experimental setting defaults to off, limiting
rollout impact.
- Existing dismissal data is backfilled to `dismiss`; the migration is
idempotent and uses guarded constraint creation.
> ROADMAP.md was checked; no duplicate planned core feature was
identified. Related open pull requests were searched before opening this
PR.
## Model Used
- OpenAI GPT-5.5 via Codex CLI, with tool use and local code execution.
Context-window size unavailable 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 public PR branch name describes the change and contains no
internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally; focused tests pass and the remaining
unrelated AWS test failure is documented above
- [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: Paperclip <noreply@paperclip.ing>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - Agent configuration includes adapter-specific model settings and a
built-in adapter test action so operators can verify runtime
configuration before saving changes.
> - Cody/Codex-style local adapters can use an adapter default model
when the user clears the explicit model field.
> - The adapter test path still passed an object containing `model:
undefined` in some create/edit flows, which is different from omitting
the model and can break default-model behavior.
> - The previous fix was reverted because it also included an unrelated
skill documentation edit.
> - This pull request reapplies only the UI default-model test-config
fix, with no doc or skill changes.
> - The benefit is that testing Cody/Codex adapter settings with the
default model follows the same contract as saving default model
settings: no explicit model key is sent.
## Linked Issues or Issue Description
Bug report:
- Summary: Testing a Cody/Codex local agent after selecting the default
model could send an adapter config with an undefined model value instead
of omitting the model key.
- Expected behavior: Clearing the model to use the adapter default
should test with `adapterConfig: {}` unless another model is explicitly
selected.
- Actual behavior: The UI test-config path could preserve `model:
undefined`, causing the adapter test to fail instead of exercising the
default model.
- Related PRs: Reapplies the UI-only portion of #9361 after #9363
reverted the original PR.
## What Changed
- Exported and reused `omitUndefinedEntries` so adapter test config
payloads drop undefined adapter config entries before calling the test
endpoint.
- Hardened the current model display value so create-mode values that
are nullish or non-string do not crash the model selector/test flow.
- Added render coverage for editing a Codex agent back to the default
model and for testing a create form with the default model.
## Verification
- `pnpm exec vitest run
ui/src/components/AgentConfigForm.render.test.tsx`
- `pnpm check:token-gates`
- Confirmed `git diff origin/master --name-only` contains only:
- `ui/src/components/AgentConfigForm.render.test.tsx`
- `ui/src/components/AgentConfigForm.tsx`
- `ui/src/lib/agent-config-patch.ts`
## Risks
Low risk. The change only removes `undefined` adapter config entries
from the UI adapter-test payload and adds focused render coverage.
Explicit model values and other adapter config fields are preserved.
> 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 coding agent, tool-use enabled. Context window size
not exposed in this runtime.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have 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: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - The agent detail page uses route references that can be based on an
agent's URL key.
> - Renaming an agent can change that URL key while the browser is still
on the old route.
> - After save or rollback, refetching the stale route reference can
render an "Agent not found" state even though the agent still exists.
> - This pull request redirects the detail page to the updated canonical
route when the saved agent's route reference changes.
> - The benefit is that agent renames keep users on the same
configuration workflow without landing on a stale URL.
## Linked Issues or Issue Description
- Refs #1848
- Related public search performed for agent rename/not-found issues and
PRs; no closer in-flight PR was found.
- Bug context: after saving a renamed agent or rolling back to a
revision with a different name-derived URL key, the agent detail page
could continue using the old URL and show "Agent not found".
## What Changed
- Added a small route-sync helper that compares the previous and updated
agent route refs after mutations.
- Redirects the agent detail page with `replace: true` when a save or
rollback changes the canonical route ref.
- Removes the stale detail-query cache entry so the old route reference
is not refetched after a rename.
## Verification
- Local outgoing patch scan for common secrets, private paths/emails,
and internal issue/link references: no matches.
- `corepack pnpm install --frozen-lockfile`
- `corepack pnpm --dir ui run typecheck`
- `corepack pnpm --dir ui exec vitest run
src/pages/AgentDetail.progress.test.ts src/App.test.tsx`
- `corepack pnpm check:token-gates`
## Risks
- Low risk: the redirect only runs when the updated agent resolves to a
different route ref than the current agent.
- If a future mutation response omits both URL key and name, the
existing route-ref fallback behavior still applies.
## Model Used
OpenAI Codex, GPT-5 coding agent via the local Codex adapter, with
tool-assisted repository inspection, shell execution, and GitHub API
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
---------
Co-authored-by: Claude <noreply@paperclip.ing>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - Sandbox environments can capture reusable custom images (provider
snapshots) so agents boot with pre-installed tools and CLI logins
> - The custom-image runtime fingerprint check included provider
secret-ref paths (e.g. the Daytona `apiKey`), while capture-time
fingerprinting excluded them, so any config carrying a credential never
matched its captured snapshot
> - As a result, agent config tests and environment probes silently
booted the provider base image instead of the snapshot, test sandboxes
were deleted before operators could inspect them, and any environment
save orphaned the snapshot without warning
> - The UI compounded the confusion by displaying an internal template
id that matches nothing in the provider dashboard
> - This pull request aligns runtime fingerprints with capture-time
exclusions, re-stamps fingerprints on saves that cannot affect the
snapshot (warning when they can), archives test/probe sandboxes instead
of deleting them, and surfaces the provider snapshot ref in the UI
> - The benefit is that custom images actually apply to config tests and
probes, survive unrelated config edits, and are debuggable against the
provider dashboard
## Linked Issues or Issue Description
No public GitHub issue exists for this; describing it in-PR per the bug
template. Related: Refs #9329 (saved-environment probe company context —
this branch carries an equivalent fix), Refs #8794 (introduced reusable
sandbox custom images).
**What happened?**
With a Daytona environment whose provider config stores the API key as a
secret reference and an active captured custom-image snapshot:
- Agent config tests and environment probes booted the provider base
image (`daytonaio/sandbox:0.8.0`) instead of the captured snapshot, so
CLI upgrades/logins baked into the snapshot were missing and the probe
reported "login required" and an outdated CLI.
- The environment card showed an internal template id (e.g.
`b5be03e1-ca5…`) that does not correspond to any snapshot name in the
provider dashboard, making the active image impossible to correlate.
- Test/probe sandboxes were deleted immediately after the run, so the
sandbox a test used could not be inspected afterwards.
- Saving the environment config (even fields unrelated to the image)
changed the stored fingerprint, silently detaching the snapshot with no
warning.
**Expected behavior**
Config tests and probes boot the captured snapshot when one is active;
the UI shows the provider-facing snapshot/template ref; test sandboxes
stay inspectable for a short window; unrelated config edits keep the
snapshot linked, and edits that genuinely invalidate it produce an
explicit warning.
**Steps to reproduce**
1. Configure a sandbox environment on Daytona with the API key stored as
a company secret reference.
2. Capture a custom image snapshot from the environment page and mark it
active (e.g. after installing/logging into a CLI in the setup sandbox).
3. Run the agent config test or an environment probe: the sandbox boots
the base image, not the snapshot, and the sandbox is deleted immediately
after the test.
4. Save the environment config with an unrelated field change: the
snapshot silently stops applying.
**Paperclip version or commit**
`master` at the merge-base of this branch.
**Deployment mode**
Self-hosted local instance (macOS, pnpm dev server) with the Daytona
sandbox provider plugin.
## What Changed
- Runtime custom-image fingerprint checks now exclude provider
secret-ref paths, matching capture-time exclusions, so configs carrying
credentials match their captured snapshots
(`environment-custom-image-runtime.ts`).
- Agent config tests and saved-environment probes force fresh,
non-reused sandboxes and pass company context so lease-backed probes can
resolve company secrets and boot the real snapshot
(`environment-probe.ts`, `routes/agents.ts`, `routes/environments.ts`).
- Test/probe sandboxes are released by archiving (stop + 60-minute
provider-side auto-delete) instead of immediate deletion, so operators
can inspect the exact sandbox a test used (Daytona plugin).
- On environment PATCH save, changes that cannot affect the captured
snapshot re-stamp the template's source fingerprint so the snapshot
stays linked; boot-source or provider-identity changes (new manifest
field `templateIdentityPaths`) mark the template detached and the save
response reports it (`environment-custom-images.ts`, shared plugin
types/validators).
- The custom-image overview exposes `activeTemplateMatchesConfig`; the
environments UI shows the provider snapshot/template ref (internal id
moved to a tooltip), warns via toast when a save detaches the snapshot,
and shows a persistent "Not in use" warning when the active template no
longer matches the saved config (`CompanyEnvironments.tsx`,
`api/environments.ts`).
## Verification
- `pnpm vitest run
server/src/__tests__/environment-custom-images-service.test.ts
server/src/__tests__/environment-probe.test.ts
server/src/__tests__/environment-routes.test.ts
server/src/__tests__/agent-test-environment-routes.test.ts` — server
coverage for fingerprint exclusions, re-stamp/detach on save, probe
company context, and fresh-sandbox test behavior.
- `pnpm vitest run
packages/plugins/sandbox-providers/daytona/src/plugin.test.ts` —
archive-on-release and snapshot ref handling.
- `pnpm vitest run ui/src/pages/CompanyEnvironments.test.tsx` — snapshot
ref display, detach toast, and "Not in use" warning.
- Manually verified end-to-end on a live self-hosted instance against
real Daytona: config test boots the captured snapshot (CLI login and
version persist), the test sandbox remains visible in the provider
dashboard as archived, and saving unrelated fields keeps the snapshot
applied.
## Risks
- Fingerprint exclusion widening: a provider credential rotation alone
no longer detaches a captured snapshot; that is the intended behavior
(the snapshot content does not depend on the credential), and
provider-identity fields (e.g. Daytona `apiUrl`) still detach via
`templateIdentityPaths`.
- Archived test sandboxes consume provider-side resources for up to
their auto-delete window instead of being freed immediately; bounded (60
minutes) and only for test/probe sandboxes.
- New optional manifest field `templateIdentityPaths` is
backward-compatible; providers that omit it keep current matching
behavior.
## Model Used
- Claude Fable 5 (`claude-fable-5`), extended thinking, agentic tool use
via Claude Code / Claude Agent SDK.
## 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
## Thinking Path
> - Paperclip is the open-source control plane people use to manage AI
agents and their work.
> - Its scheduler, routines, and heartbeat services decide when agents
automatically begin work.
> - Experimental per-worktree execution is useful for isolated
development, but enabling it previously allowed automatic services to
consider an existing backlog.
> - A worktree activation must therefore create a durable eligibility
boundary rather than merely toggle execution on.
> - This pull request records an activation cutoff and applies it
consistently to automatic routine and heartbeat dispatch.
> - The result is that an enabled worktree executes only work created
after its own activation, while non-worktree behavior remains unchanged.
## Linked Issues or Issue Description
**Problem type:** Bug / safety regression
**Summary:** Enabling experimental run execution in an existing worktree
could start automatic scheduler, routine, watchdog, and heartbeat
activity for work created before that worktree was explicitly armed.
**Expected behavior:** A worktree that has execution enabled only
considers automatically dispatched work created on or after its
activation timestamp. Ambiguous activation state fails closed.
Non-worktree instances keep their existing behavior.
**Related public work:** Refs #8275 (runtime worktree policy gating);
this PR adds an activation-time boundary for automatic execution rather
than changing the general runtime policy.
## What Changed
- Persist a worktree execution activation timestamp and originating
instance ID; stamp them only when the experimental toggle changes from
disabled to enabled.
- Resolve activation state fail-closed when the cutoff is missing,
invalid, disabled, or belongs to another instance.
- Gate automatic routine scheduling, webhooks, watchdog activity, and
heartbeat selection at the activation cutoff; manual runs remain
available.
- Share the canonical worktree truthy-environment helper across routine
dispatch and agent inbox filtering.
- Add cutoff and truthy-runtime regression coverage, plus
experimental-settings UI states that explain armed and suppressed
execution.
## Verification
- `pnpm exec vitest run server/src/__tests__/routines-service.test.ts
server/src/__tests__/instance-settings-service.test.ts` — passes: 2
files, 60 tests.
- `pnpm --filter @paperclipai/server typecheck` — passes.
- Existing CI completed successfully before the follow-up review fixes;
this branch was rebased onto the latest `origin/master` before
retesting.
## Risks
- **Behavioral:** Automatic worktree execution is intentionally more
restrictive; pre-existing work is suppressed until newly created after
activation.
- **Operational:** A malformed or cross-instance activation record fails
closed, requiring an operator to disable and re-enable the experimental
toggle on the intended worktree.
- **Compatibility:** The worktree environment now accepts all canonical
truthy values (`1`, `true`, `yes`, and `on`) consistently; non-worktree
instances are unaffected.
- **Branch metadata:** This existing execution-workspace branch predates
the current naming rule and cannot be renamed under this task's
workspace contract; the code and PR title do not include internal ticket
references.
> `ROADMAP.md` was checked; this targeted execution-safety fix does not
duplicate planned core work.
## Model Used
- Anthropic Claude Code — assisted with the original implementation;
exact model identifier and context window were not recorded in the
repository metadata.
- OpenAI Codex CLI — assisted with PR preparation and review fixes;
exact model identifier and context window are not exposed in this
execution environment. Used with terminal tooling, code editing, and
targeted 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)
- [ ] 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: Paperclip <noreply@paperclip.ing>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - The heartbeat/recovery subsystem decides whether an agent run has a
durable continuation path after the process stops.
> - External waits need stricter semantics than local background
watchers: a killed local process is not durable, while a first-class
blocker/monitor/scheduled wake is.
> - Without that distinction, recovery can repeatedly treat
adapter-failed continuations as live work and obscure the real reason a
task stopped.
> - This pull request adds explicit durable external-wait liveness
handling and documents the expected execution semantics.
> - It also improves operator-visible recovery evidence so invalid
external-wait paths explain why they were rejected.
> - The benefit is clearer recovery behavior, fewer duplicate
continuation recoveries, and a safer contract for monitor-backed
external waits.
## Linked Issues or Issue Description
- Refs #5978
- Related PRs: #4988, #7495, #8502
## What Changed
- Added durable external-wait liveness classification so
local/background watchers are not accepted as durable live paths after
the owning process exits.
- Preserved first-class blocker/monitor/scheduled wake paths as valid
external-wait continuations.
- Added backend regression coverage for killed watcher failure,
monitor-backed durable wait resumption, normal completion, blocker
behavior, and no duplicate recovery.
- Added adapter utility coverage for terminal cleanup behavior used by
local process adapters.
- Surfaced invalid external-wait recovery evidence in the recovery
action card and run ledger.
- Updated execution semantics documentation and the V1 implementation
contract.
## Verification
- `pnpm check:token-gates` passed.
- `pnpm -r typecheck` passed.
- `node scripts/run-vitest-stable.mjs --mode general --group
general-server` equivalent lane passed in CI-clean env: 238 files, 2164
tests passed, 1 skipped.
- `node scripts/run-vitest-stable.mjs --mode general --group
general-workspaces-a` passed in fully Paperclip-env-clean env: UI 305
files / 2430 tests; CLI 43 files / 230 tests.
- `node scripts/run-vitest-stable.mjs --mode general --group
general-workspaces-b` passed in fully Paperclip-env-clean env:
shared/db/adapters/plugin packages all green.
- `node scripts/run-vitest-stable.mjs --mode serialized` passed in fully
Paperclip-env-clean env: 107 serialized server suites green, including
84/84 heartbeat-process-recovery tests.
- `pnpm build` passed in fully Paperclip-env-clean env.
Notes: running `pnpm test:run` directly inside the Paperclip heartbeat
environment exposed local harness env contamination in existing tests
(`PAPERCLIP_CONFIG`, `PAPERCLIP_DB_BACKUP_DIR`, and
`PAPERCLIP_WORKTREE_START_POINT`). Re-running the same lanes with
inherited `PAPERCLIP_*` and port env removed produced the CI-equivalent
green results above.
## Risks
- Medium behavioral risk: this changes recovery classification for
stopped local external-wait processes, so adapters relying on unmanaged
background watchers must use blockers, monitors, scheduled wakes, or
explicit durable handoff instead.
- Low UI risk: recovery-card copy changes are covered by component tests
and Storybook screenshot QA.
- No database migration is included.
> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.
## Model Used
- OpenAI Codex, GPT-5-based coding agent, tool-enabled terminal/code
execution. Exact context-window metadata was not exposed in the runtime.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [ ] 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>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - Issue and item detail pages use a shared issue chat thread to show
comments, runs, activity, and interactions.
> - That thread still defaulted to landing on the latest comment when
messages first loaded.
> - On long issue/item pages, that default can yank the operator away
from the top of the page before they choose to inspect the newest
message.
> - Deep links to comment hashes can create the same kind of initial
viewport jump when they are used as generic navigation targets.
> - This pull request makes initial latest-comment and initial
thread-hash scrolling opt-in instead of default behavior.
> - The benefit is stable initial page position across issue-thread
surfaces while keeping the explicit Jump to latest control available.
## Linked Issues or Issue Description
No exact public GitHub issue was found for this bug.
Bug description:
- What happened: opening a page with a shared issue conversation thread
could automatically move the viewport toward the newest comment/thread
target.
- Expected behavior: ordinary page loads should keep the initial
viewport stable unless the user explicitly clicks Jump to latest.
- Steps to reproduce: open an issue or item detail page with a long
conversation thread and observe whether the page jumps to the newest
thread entry on initial load.
- Paperclip version/commit: reproduced while working on the current
`master` branch lineage.
- Deployment mode: local trusted/dev UI.
Related public thread/comment UX work: Refs #3916, Refs #7972, Refs
#8800.
## What Changed
- Changed `IssueChatThread` so initial latest-comment scrolling defaults
to off.
- Added a separate opt-in for initial thread-hash scrolling, also
defaulting to off.
- Preserved stale deleted-comment hash cleanup without scrolling the
page.
- Updated regression coverage so default initial load stays put, comment
hashes do not scroll by default, and manual Jump to latest still
scrolls.
## Verification
- `pnpm --filter @paperclipai/ui typecheck` passed on the clean PR
branch.
- `pnpm --dir ui exec vitest run src/pages/IssueDetail.test.tsx -t
"loads from the pending state into issue detail without changing hook
order"` passed on the clean PR branch.
- `pnpm --dir ui exec vitest run
src/components/IssueChatThread.test.tsx` was attempted on the clean PR
branch, but the file fails before changed assertions with the existing
`TypeError: act is not a function` test-harness issue across 58 tests;
14 tests passed.
- Static check: no `autoScrollToLatestOnInitialLoad={true}` or
`autoScrollToHashOnInitialLoad={true}` call sites remain in `ui/src`.
## Risks
Low risk. This only changes initial scroll defaults in the shared issue
thread. The main behavioral shift is that direct comment/thread hashes
no longer auto-scroll on first load unless a caller explicitly opts in;
the Jump to latest button and post-submit scroll behavior are unchanged.
> 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 GPT-5 via Codex coding-agent runtime; exact context window not
exposed in this environment; tool-enabled repository inspection,
editing, testing, git, GitHub CLI, and Paperclip API usage.
## 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
- [ ] 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: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - The board UI includes an agents sidebar so operators can see which
agents are currently active.
> - That sidebar depends on live-run polling, heartbeat events, and
cross-tab cache sharing to stay current without overloading the API.
> - The sidebar was visually churning because agents could leave the
live section immediately after a run ended, while progress events and
cross-tab broadcasts kept forcing hot query updates.
> - This pull request stabilizes live sidebar membership and makes
shared polling broadcasts monotonic/deduplicated.
> - The benefit is a calmer operator sidebar that still reflects real
live state without flashing between stale and fresh snapshots.
## Linked Issues or Issue Description
No public GitHub issue exists for this operator-facing bug.
Bug summary:
- What happened: the agents sidebar could flash or reshuffle around
active agents while live-run and heartbeat data was updating.
- Expected behavior: active and recently-active agents should remain
visually stable, and cross-tab cache sharing should not overwrite
fresher data with older snapshots.
- Reproduction context: run Paperclip with multiple tabs or rapid
live-run/progress updates and watch the agents sidebar while agents
enter/leave live execution.
- Deployment mode: local/operator board UI.
Related PR:
- Supersedes #9357, which carried the same fixes on a branch/title/body
that were not suitable for public contribution hygiene.
## What Changed
- Restored the maintainer-only warning wording in the developer skill
guide so the existing server skill-utils CI gate passes on current
master.
- Added a 120-second linger window for streamlined sidebar agent rows so
an agent does not immediately disappear from the live section as soon as
its last run ends.
- Deferred the recent-agent fallback until there are no live or
lingering agents, while keeping the live badge tied only to
actually-live runs.
- Stopped broad live-runs/heartbeats/agents-list invalidation on every
run progress event, while preserving targeted agent-detail invalidation.
- Added producer timestamps to cross-tab shared polling result messages
so older-or-equal snapshots are dropped before `setQueryData`.
- Added per-resource broadcast dedupe/rate limiting so tabs do not
rebroadcast equivalent cached data in a loop.
- Added focused coverage for sidebar linger behavior, staggered
multi-agent linger expiry, live update invalidation scope, shared
polling timestamp handling, and cross-tab broadcast dedupe.
## Verification
Run locally on the rebased PR branch:
- `pnpm --filter @paperclipai/ui exec vitest run
src/components/SidebarAgents.test.tsx
src/context/LiveUpdatesProvider.test.ts` — 44 tests passed.
- `pnpm --filter @paperclipai/ui exec vitest run
src/lib/cross-tab-poll.test.ts src/hooks/useSharedPolling.test.ts` — 10
tests passed.
- `pnpm --filter @paperclipai/ui typecheck` — exit code 0.
## Risks
- Low migration risk: the sidebar/polling changes are UI/client cache
behavior only, with no database or API contract changes.
- Sidebar visibility now intentionally lingers for 120 seconds after the
last live run; stale rows could remain briefly visible, but their live
badge is removed when they are no longer actually live.
- Cross-tab broadcasts are now more conservative; a missed publish
should be corrected by the next normal poll or accepted newer timestamp.
> 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 GPT-5 Codex via the Paperclip Codex coding-agent runtime; exact
API model identifier and context-window size are not exposed in this
environment. The agent used terminal/tool execution for repository
inspection, focused tests, branch preparation, and PR creation.
## 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>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - The board UI lets operators create and edit agent adapter
configuration, including a primary model field and an adapter test
action.
> - For Cody and similar adapter forms, selecting the default model
means the model value is intentionally unset so the adapter can use its
default.
> - The adapter test path still allowed `model: undefined` to survive in
the generated adapter config, which could send an invalid test payload
instead of omitting the field.
> - This pull request normalizes create/edit adapter test config so
default-model selections omit `model` entirely.
> - The benefit is that testing an agent configured to use the adapter
default model exercises the same clean config shape that should be saved
and run.
## Linked Issues or Issue Description
No public GitHub issue was found for this local UI bug, so the problem
is described inline.
Bug description:
- What happened: using the adapter test action after choosing the
default model could include `model: undefined` in adapter config and
surface a UI/runtime error instead of testing with the adapter default.
- Expected behavior: choosing the default model should omit the `model`
field from adapter config so the adapter default is used.
- Steps to reproduce: edit a Codex/Cody-style agent with a concrete
model, switch the model selector to Default, then run the adapter Test
action.
- Paperclip version/commit: current `master` before this PR.
- Deployment mode: board UI, deployment-mode independent.
## What Changed
- Sanitized adapter test config assembly so undefined adapter config
entries are omitted before the test request is sent.
- Made create-mode current model display resilient when the model is
unset for adapter defaults.
- Added regression coverage for editing an existing agent from a
concrete model back to Default and testing it.
- Added regression coverage for create-mode testing with an
unset/default model.
- Hardened the developer skill wording used by the existing server skill
utility contract test.
## Verification
- `pnpm --filter @paperclipai/ui exec vitest run
src/components/AgentConfigForm.render.test.tsx`
- `pnpm exec vitest run
server/src/__tests__/paperclip-skill-utils.test.ts`
- GitHub PR checks on this branch are green, including Typecheck +
Release Registry, Build, General tests, e2e, verify, security scans, and
Greptile Review.
## Risks
- Low risk: this only removes undefined values from adapter test config
payloads, which aligns with the existing persisted patch behavior.
- Low risk: default-model display now treats unset create-mode model
values as an empty string.
- No database, API schema, migration, auth, or adapter runtime contract
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 coding agent, tool-enabled software-engineering
session. Exact context window size was not exposed by the runtime.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Cody <cody@paperclip.ing>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - The issue thread UI renders live agent output from adapter run logs
and transcript parsing.
> - Some adapter streams emit many small or repeated token chunks, and
live UI updates can expose partial words, duplicated slices, or
transient markdown placeholders.
> - That makes active run updates look like gibberish even when the
underlying agent output is valid.
> - The fix needs to preserve raw logs while making the live thread view
stable, readable, and ordered.
> - This pull request adds monotonic run-log sequencing, safer live
transcript dedupe/order handling, markdown placeholder hiding, and
readable live text stabilization.
> - The benefit is a live issue thread that updates smoothly without
showing confusing partial parser artifacts.
## Linked Issues or Issue Description
No public GitHub issue exists yet, so this PR includes the bug details
inline.
### What happened?
Live run updates in the issue thread can show confusing repeated or
partial text while an adapter is streaming. The visible text appears to
lose parsing boundaries during active updates, especially with ACP-style
token deltas, so the live output can briefly render duplicated chunks,
incomplete words, or HTML-comment placeholders.
### Expected behavior
Live text should remain readable while preserving the underlying run
output for raw inspection.
### Steps to reproduce
1. Start a live agent run whose adapter emits small stdout token deltas.
2. Watch the issue thread while the run is still active.
3. Observe transient duplicated chunks, incomplete words, or markdown
placeholder artifacts in the live rendered text.
### Paperclip version or commit
Reproduced against current `master` before this PR branch.
### Deployment mode
Local dev issue-thread UI with live local adapter runs.
### Additional context
GitHub PR search for `live run streaming text markdown transcript` found
one broad merged PR, `#252` (“Dotta updates - sorry it's so large”), but
no targeted duplicate for this live streaming readability bug.
## What Changed
- Added per-run monotonic sequence numbers to persisted and live run-log
chunks.
- Dedupe and order live transcript chunks by sequence before falling
back to timestamp ordering.
- Hide markdown HTML comment placeholder text from rendered markdown
output.
- Smooth live issue-thread text updates so partial additions reveal at
readable word boundaries and sliding-window removals do not produce
gibberish.
- Added coverage for run-log ordering/deduping, markdown comment hiding,
live issue-thread stabilization, and Greptile-reviewed edge cases where
overlap rewrites could synthesize text or no-boundary additions could
stay hidden.
## Verification
- `pnpm --filter @paperclipai/ui exec vitest run
src/lib/issue-chat-messages.test.ts src/components/MarkdownBody.test.tsx
src/components/transcript/useLiveRunTranscripts.test.tsx` passed before
the review fix: 3 files, 86 tests.
- `pnpm --filter @paperclipai/ui exec vitest run
src/lib/issue-chat-messages.test.ts` passed after the review fix: 1
file, 30 tests.
- `pnpm --filter @paperclipai/ui exec vitest run
src/lib/issue-chat-messages.test.ts
src/components/transcript/useLiveRunTranscripts.test.tsx` passed after
the final Greptile overlap fix: 2 files, 40 tests.
- `pnpm check:token-gates` passed.
- Local PII/secret scan of touched files found only expected code/test
words such as `secret`, `token`, and redaction-related strings; no
literal credentials found.
- `pnpm -r typecheck` passed after restoring declared dependencies with
`CI=1 pnpm install --frozen-lockfile` and running with a short `TMPDIR`
because `tsx` IPC sockets fail under the long sandbox temp path.
- `pnpm build` passed with existing Vite CSS/font/chunk warnings.
- GitHub PR checks passed on head
`4c052dfe86aecb5feb73504e6b48843f68fce813`: build, typecheck/release
registry, server and workspace test shards, serialized server suites,
e2e, canary dry run, policy, review, Socket, Superagent, Snyk, and
verify.
- Greptile review passed on head
`4c052dfe86aecb5feb73504e6b48843f68fce813` with confidence score 5/5 and
no blocking issues found.
- `pnpm test:run` failed in unrelated server workspace tests on this
macOS local environment:
- `server/src/__tests__/heartbeat-workspace-branch-containment.test.ts`:
two assertions compare `/tmp/...` with `/private/tmp/...`.
- `server/src/__tests__/heartbeat-worktree-suppression.test.ts`:
expected one heartbeat run but observed two, followed by cleanup fallout
in the full run.
- Isolated rerun of those two server suites reproduced the same three
failures.
## Risks
- Low product risk for the UI changes: the readable smoothing only
affects active live-run display stabilization, not stored comments or
raw run logs.
- Moderate verification risk: local full Vitest did not pass because of
unrelated server workspace tests. Targeted tests for this change,
typecheck, token gates, and build passed.
- Run-log sequence fields are optional for compatibility with older log
rows that do not include `seq`.
> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.
## Model Used
- OpenAI Codex, GPT-5-based coding agent, tool-using local workspace
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 searched the GitHub PR list for similar PRs and confirmed this
is not a duplicate
- [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
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - Skill Studio is the UI surface for editing skill package files,
including `SKILL.md`.
> - Markdown files are split into frontmatter fields plus a rich
markdown body editor.
> - The dirty-state guard intentionally ignores markdown editor
normalization during initial mount.
> - That guard was too narrow: rich markdown body edits could happen
before the file was marked as user-interacted, so edits did not reliably
enable Save.
> - This pull request broadens the user-interaction signals around the
markdown body editor and adds a regression test for saving body edits.
> - The benefit is that editing `SKILL.md` in Skill Studio now behaves
like normal file editing: changes show as unsaved and Save persists the
full markdown document.
## Linked Issues or Issue Description
No public GitHub issue exists. Inline bug description follows.
### What happened?
Editing a Skill Studio markdown body did not reliably mark the file
dirty, so the Save action could remain unavailable or fail to persist
the body edit.
### Expected behavior
User edits in the markdown body editor should mark the file unsaved and
allow saving the updated `SKILL.md` content.
### Steps to reproduce
1. Open a Skill Studio markdown file such as `SKILL.md`.
2. Edit the markdown body in the rich editor.
3. Observe whether the unsaved state appears and Save becomes enabled.
4. Save and reload the file.
### Paperclip version or commit
Reproduced on `master` before this fix.
### Deployment mode
Local dev (`pnpm dev`).
## What Changed
- Mark markdown body interaction on capture-phase key, pointer, paste,
drop, before-input, and input events around the rich editor.
- Preserve the existing guard that prevents MDXEditor mount-time
normalization from dirtying a clean file.
- Add a Skill Studio regression test that edits the markdown body,
observes the Unsaved state, enables Save, and verifies the saved
`SKILL.md` includes both frontmatter and the edited body.
## Verification
- `pnpm vitest run ui/src/pages/SkillStudio.test.tsx`
Manual reviewer path:
- Open a Skill Studio markdown file such as `SKILL.md`.
- Edit the body text in the rich markdown editor.
- Confirm the UI shows an unsaved state and the Save button is enabled.
- Save and confirm the updated markdown body persists.
## Risks
Low risk. The change only broadens interaction detection before applying
existing dirty-state logic, and the guard still prevents initial editor
normalization from marking an unopened file dirty.
> 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 CLI using GPT-5, with repository file editing, shell
execution, and GitHub CLI 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
- [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>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - Codex local is the adapter subsystem that exposes OpenAI Codex CLI
model choices to agents and issue overrides.
> - OpenAI has GPT-5.6 Codex-capable models that should appear in
Paperclip's built-in Codex model list and refresh behavior.
> - Paperclip's server model listing falls back to the adapter metadata
and merges OpenAI refresh results with known Codex defaults.
> - This pull request updates the Codex default model metadata to
include GPT-5.6 options and adds regression coverage for fallback and
refresh paths.
> - The benefit is that operators can select the new Codex models
without relying on manual model IDs, and refresh behavior keeps known
GPT-5.6 options visible.
## Linked Issues or Issue Description
Refs #9322.
Refs #9342.
Refs #9346.
### Agent or provider
Codex CLI (OpenAI).
### Why this adapter is useful
OpenAI's GPT-5.6 Codex-capable models should be available in Paperclip's
Codex adapter defaults and model refresh path.
### How the agent is invoked
`codex`
## What Changed
- Changed the `codex_local` default model metadata from `gpt-5.5` to
`gpt-5.6`.
- Added `gpt-5.6-sol`, `gpt-5.6-terra`, and `gpt-5.6-luna` to the
built-in Codex adapter model list.
- Updated adapter and server model-listing tests to cover GPT-5.6
fallback and refresh behavior.
- Aligned Codex Fast mode support and helper text with the new `gpt-5.6`
default, while preserving GPT-5.5, GPT-5.4, and manual model ID support.
## Verification
- `git diff --check origin/master...HEAD`
- `pnpm exec vitest run packages/adapters/codex-local/src/index.test.ts
packages/adapters/codex-local/src/server/codex-args.test.ts
server/src/__tests__/adapter-models.test.ts
server/src/__tests__/adapter-model-refresh-routes.test.ts`
- `pnpm check:token-gates`
- `pnpm --filter @paperclipai/adapter-codex-local typecheck`
- `pnpm --filter @paperclipai/server typecheck`
- `pnpm --filter @paperclipai/ui typecheck`
## Risks
Medium risk because changing `DEFAULT_CODEX_LOCAL_MODEL` from `gpt-5.5`
to `gpt-5.6` changes the adapter's default model selection for new blank
configurations. The model-list additions are otherwise low risk and
covered by adapter/server metadata tests. This PR intentionally overlaps
related PRs #9342 and #9346, so reviewers may prefer to close or fold it
into one of those branches.
> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.
## Model Used
OpenAI Codex coding agent based on GPT-5, with shell, git, GitHub CLI,
and repository editing tool use. Exact served model ID and context
window were not exposed by the runtime.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have 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>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - Agents often work from reviewed plans that are approved through
issue-thread interactions.
> - Accepting a plan is not just a UI decision; it must reliably resume
the assignee so approved work continues.
> - A failed continuation wake could leave an approved plan stranded in
review with no durable retry or visible recovery path.
> - This pull request makes approved plan continuations retryable,
recoverable, and visible when resume fails.
> - The benefit is that operators can trust plan approval to either
resume the agent or produce an explicit actionable failure instead of
silent limbo.
## Linked Issues or Issue Description
No public GitHub issue exists. Inline bug report follows the repository
bug template.
### Pre-submission checklist
- [x] I have searched existing open and closed issues and this is not a
duplicate.
- [x] I am on the latest released version of Paperclip or can reproduce
on `master`.
- [x] I have confirmed the error originates in Paperclip itself, not in
my agent adapter, API provider, or local configuration.
### What happened?
When a plan-confirmation interaction was accepted, the assignee
continuation wake could fail before useful agent execution. In that case
the issue could remain in review even though the plan had been approved,
because the failed wake was fire-and-forget and there was no durable
retry or recovery path for accepted continuations.
### Expected behavior
Accepted plan continuations should either wake the assignee
successfully, retry bounded infrastructure failures, recover dropped
wakes, or surface an explicit failure state that operators can act on.
### Steps to reproduce
1. Create an issue with an assignee and a plan confirmation that wakes
the assignee on accept.
2. Accept the confirmation.
3. Simulate a pre-flight continuation failure, such as process loss
before agent start or workspace validation failure.
4. Observe that the approved issue can remain in review without an
active assignee wake or visible retry/failure state.
### Paperclip version or commit
Reproducible on `master` before this PR's retry/recovery changes.
### Deployment mode
Local dev (`pnpm dev`) and server-side recovery paths.
### Installation method
Built from source (`pnpm install`, `pnpm dev`, test runner).
### Agent adapter(s) involved
Not adapter-specific; this is a core continuation/recovery bug. The
tests cover local-agent failure shapes without relying on a
provider-specific API.
### Database mode
Embedded Postgres test database for verification. The affected logic is
database-backed and applies to normal Postgres deployments as well.
### Access context
Board accepts the interaction; agent execution resumes through the
assignee wake path.
### Relevant logs or output
No sensitive logs are needed. The regression tests simulate the failed
wake and recovery states directly.
### Relevant config
No special config is required beyond an assignee with wake-on-demand
enabled.
### Additional context
This PR also prevents a stale workspace-validation payload from
quarantining another issue's active workspace and prevents unrelated
successful runs from masking a continuation that never resumed.
### Privacy checklist
- [x] I have reviewed all pasted output for PII, usernames, file paths,
API keys, tokens, and company names, and redacted where necessary.
## What Changed
- Added bounded infrastructure retries for failed accepted-interaction
continuation wakes.
- Extended stranded issue recovery so dropped accepted-plan continuation
wakes are requeued.
- Recorded and rendered explicit resume-failure state on accepted
confirmation cards.
- Added clean-workspace fallback for workspace-validation failures while
preventing cross-issue workspace quarantine.
- Tightened recovery so unrelated successful runs do not mask an
accepted continuation that never resumed.
- Added focused server/UI coverage for retry scheduling, recovery,
visible failure state, and interaction card rendering.
## Verification
- `pnpm vitest run
server/src/__tests__/heartbeat-retry-scheduling.test.ts
server/src/__tests__/heartbeat-process-recovery.test.ts` — 106 tests
passed.
- Earlier branch verification also covered the issue-thread interaction
card tests for the visible resume-failure UI.
- GitHub CI is green on the replacement PR head, and Greptile reports
5/5 with no blocking issues.
## Risks
- Medium behavioral risk: this changes recovery behavior for accepted
continuation interactions and workspace-validation retries.
- Mitigation: retries are bounded, scoped to same-company issue context,
and workspace quarantine now requires ownership by the issue being
retried.
- Existing stored confirmation results remain compatible because the new
resume-failure field is optional.
> 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 coding agent, tool-enabled terminal workflow. The
runtime does not expose an exact context-window value to the agent.
## 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>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - Local CLI adapters can run against Paperclip-managed execution
environments instead of only the host filesystem.
> - The environment picker and environment capability API derive sandbox
support from shared adapter capability lists.
> - The Grok Build adapter is implemented as a local CLI adapter, but it
was missing from those shared environment capability lists.
> - That made Grok agents look local-only even when sandbox environments
were configured.
> - This pull request registers `grok_local` in the shared adapter
constants and remote-managed environment support path.
> - The benefit is that Grok Build agents can select the same local,
SSH, and sandbox environment overrides as other local CLI adapters.
## Linked Issues or Issue Description
No public GitHub issue exists for this bug. Inline bug report follows.
### Pre-submission checklist
- [x] I have searched existing open and closed issues and this is not a
duplicate.
- [x] I am on `master`.
- [x] I have confirmed the error originates in Paperclip itself, not in
the Grok adapter provider or local configuration.
### What happened?
When configuring a Grok Build local agent in the board UI, Paperclip did
not expose configured sandbox environments as selectable environment
overrides. The shared environment capability helper treated `grok_local`
as local-only because it was missing from the remote-managed local
adapter allowlist.
### Expected behavior
Grok Build should behave like other local CLI adapters: when
environments are enabled and a runnable sandbox environment exists, the
agent configuration form should show the environment override selector
and allow the sandbox to be selected.
### Steps to reproduce
1. Enable environments in instance experimental settings.
2. Configure at least one runnable sandbox environment.
3. Open the agent configuration form for a Grok Build local agent.
4. Observe that the sandbox environment is not offered as an override
before this fix.
### Paperclip version or commit
`master` before this PR.
### Deployment mode
Local dev (`pnpm dev`).
### Installation method
Built from source (`pnpm dev` / `pnpm build`).
### Agent adapter(s) involved
- Grok Build local adapter.
- Core bug in shared environment capability logic.
### Database mode
Not database-related.
### Access context
Board human operator.
### Relevant logs or output
No runtime error is emitted; the issue is a missing UI option caused by
shared capability metadata.
### Relevant config
No secret-bearing config required. Reproduction only needs environments
enabled and a runnable sandbox environment configured.
### Privacy checklist
- [x] I have reviewed all pasted output for PII, usernames, file paths,
API keys, tokens, company names, and redacted where necessary.
## What Changed
- Added `grok_local` to the shared built-in adapter type list.
- Added `grok_local` to the remote-managed adapter set used by
environment capability helpers.
- Added shared regression coverage for Grok local sandbox provider and
driver support.
- Added a UI render regression test that confirms Grok Build agents show
the environment override when a runnable sandbox exists.
## Verification
- `git diff --check`
- Changed-file secret scan with `rg` for common token/key patterns.
- `pnpm --filter @paperclipai/shared exec vitest run
src/environment-support.test.ts`
- `pnpm --dir ui exec vitest run
src/components/AgentConfigForm.render.test.tsx`
## Risks
- Low risk. This expands environment support for an existing local
adapter to match the local CLI adapter behavior already used by Claude,
Codex, Gemini, OpenCode, Cursor, and Pi.
- Operators still need at least one configured runnable sandbox
environment before a Grok agent has a sandbox option to select.
- This PR was created from a Paperclip execution workspace branch whose
name is runtime-provided; the PR body intentionally avoids internal
issue identifiers or instance-local links.
> 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 coding agent, tool-enabled terminal/code execution
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
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - The board currently uses issues for execution, but longer-lived
content work needs a separate object that can survive beyond a single
task thread.
> - The Cases subsystem adds an experimental, company-scoped record for
content artifacts and their supporting metadata.
> - The backend needs durable storage, API routes, revision history,
issue linkage, and company-boundary enforcement before the UI can depend
on Cases.
> - The UI needs an opt-in navigation surface, list/detail views,
reference chips, and issue-page context so operators can inspect Cases
without making them the default workflow.
> - The agent-facing skills need a contract for creating and updating
Cases so automated content workflows can dogfood the feature.
> - This pull request ships that experimental end-to-end path behind the
`enableCases` flag.
> - The benefit is a first-class place to collect content work,
references, attachments, revisions, and related execution threads
without polluting the core issue model.
## Linked Issues or Issue Description
No public GitHub issue exists for this experimental feature.
Feature request fields:
### Problem
Content-oriented work such as release notes, announcements, docs, and
campaigns can span many execution issues, which makes the final artifact
hard to find and reason about after the execution thread moves on.
### Proposed solution
Add an experimental Cases object that is company-scoped, linked to
issues, queryable through the API, inspectable in the board UI, and
writable by agent workflows through documented conventions.
### Alternatives considered
Continue encoding content artifacts directly in issues or documents
only. That keeps the data model smaller, but it does not give operators
a stable artifact-centric view or a clean way to link related execution
history.
### Roadmap alignment
Checked `ROADMAP.md`; this PR does not duplicate an existing planned
core roadmap item.
## What Changed
- Added the `cases` data model, migration, schema exports, and
experimental `enableCases` instance setting.
- Added company-scoped Cases API routes for list/detail/update, issue
links, revisions, children, activity events, annotations, attachments,
and idempotent agent-oriented upserts.
- Scoped case and issue lookup helpers before access checks so
inaccessible cross-company identifiers resolve as not found rather than
leaking existence.
- Fixed case PATCH timestamp handling so non-status updates cannot
overwrite `completedAt` from a stale pre-transaction row snapshot.
- Moved Cases list type/status/project filters into the server request
before the server-side limit is applied, including multi-select filters
and no-project filtering.
- Added backend route coverage for creation, updates, idempotency, issue
linking, attribution, company-boundary enforcement, OpenAPI
registration, list filtering, timestamp patch behavior, and inaccessible
lookup regressions.
- Added the experimental Cases UI surface: sidebar entry, gated routes,
list filters/grouping, detail overview, activity, revisions, children,
attachments, and issue-page case rail.
- Added case reference rendering and company-prefixed case href
generation so case links resolve directly inside the active company
route.
- Added Paperclip skill documentation for agent workflows that create or
update Cases.
- Wired release-content skills to emit Cases for dogfooding.
- Rebased onto current `master` and renumbered the Cases migrations to
`0143`/`0144` after the latest upstream migration sequence.
## Verification
- Current PR head: `ecc13be0d`.
- Rebased on current `master` (`606aa4f266`) and pushed to the existing
PR branch.
- `git diff --check origin/master...HEAD` — passed before the first
update push; subsequent committed diffs were also checked with `git diff
--check` before commit.
- Guardrails checked: no `pnpm-lock.yaml` changes, no
`.github/workflows` changes, and changed-file count is below the
Greptile 100-file limit.
- `pnpm --filter @paperclipai/server exec vitest run
src/__tests__/cases-routes.test.ts
src/__tests__/instance-settings-service.test.ts
src/__tests__/openapi-routes.test.ts` — passed, 3 files / 26 tests
before review-fix commits.
- `pnpm --filter @paperclipai/server exec vitest run
src/__tests__/cases-routes.test.ts` — passed after each server-side
Greptile fix, latest 1 file / 15 tests.
- `pnpm --filter @paperclipai/server typecheck` — passed after the
timestamp and lookup fixes.
- `pnpm --filter @paperclipai/ui exec vitest run
src/pages/Cases.test.tsx src/pages/CaseDetail.test.tsx
src/pages/CompanySkills.test.tsx src/App.cases-routing.test.tsx` —
passed, 4 files / 30 tests before review-fix commits.
- `pnpm --filter @paperclipai/ui exec vitest run
src/pages/Cases.test.tsx` — passed after the list-filter fix, 1 file /
12 tests.
- `pnpm --filter @paperclipai/ui typecheck` — passed after the
list-filter fix.
- `pnpm check:token-gates` — passed after UI changes.
- Remote PR checks on head `ecc13be0d` are green: Paperclip CI, build,
typecheck, test matrix, e2e, Canary Dry Run, policy, commit review,
Superagent Security Scan, Socket, Snyk, and Greptile passed; Storybook
visual regression is skipped and security-review is neutral.
- Greptile Review: 5/5 confidence, zero unresolved Greptile threads.
## Risks
- Medium feature risk because this introduces a new experimental domain
object across database, server, shared contracts, skills, and UI.
- The feature is gated behind `enableCases`, which limits default
operator exposure while the model is exercised.
- Case links now prefer company-prefixed hrefs; the unprefixed redirect
remains for externally entered URLs.
- Cases list filtering now sends multi-select filters to the server
before limiting; the UI still applies the same local filters as a second
pass for ancestor/context rows.
- Migrations were renumbered on top of current master; the SQL uses
guarded `IF NOT EXISTS` / `ADD COLUMN IF NOT EXISTS` patterns where
relevant for safer replay.
> 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 GPT-5 Codex in the Paperclip local coding environment was used
for this PR curation, rebase verification, review-fix implementation,
push, and PR description update. The runtime exposes tool use and shell
execution; context-window size is not exposed by this Paperclip adapter.
Several implementation commits also include AI co-author trailers
recorded in git history.
## 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 the GitHub PR list for similar PRs and confirmed this
is not a duplicate
- [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)
- [ ] 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: Claude Opus 4.8 <noreply@anthropic.com>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - Agent setup depends on instruction files that are readable and
editable from the board UI
> - The instructions tab already receives server-side metadata
describing whether each file is Markdown
> - The UI was deciding Markdown editor usage primarily from the file
extension, which makes extensionless Markdown instruction files feel
like raw code
> - This pull request makes the instructions editor trust server
Markdown metadata for existing files and keep extension fallback only
for new unsaved files
> - The benefit is that AGENTS-style prose instructions render and edit
like prose while explicitly non-Markdown files still use the raw
textarea
## Linked Issues or Issue Description
- Refs #8201
- Refs #5652
- Refs #3427
- Refs #2068
- Related PRs: #2468, #2620
## What Changed
- Use server `markdown` metadata from instruction file details/summaries
to choose the prose Markdown editor for existing instruction files.
- Keep extension-based Markdown detection only for pending new files
before server metadata exists.
- Remove the monospace content styling from the Markdown editor path so
prose instructions read like normal text.
- Add focused tests for extensionless Markdown files, new `.md` files,
and `.md` files explicitly marked non-Markdown by the server.
## Verification
- `pnpm check:token-gates`
- `pnpm exec vitest run ui/src/pages/AgentDetail.instructions.test.tsx`
- `pnpm --filter @paperclipai/ui typecheck`
## Risks
- Low risk. The editor selection now depends on server metadata for
existing files, so incorrect server metadata would choose the wrong
editor. The fallback still preserves extension-based behavior for newly
created unsaved files.
> 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 GPT-5 via Codex coding agent, tool-enabled terminal workflow.
Context window details were not exposed by the runtime.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have 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>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - Company search is the primary way operators find issues, comments,
documents, artifacts, agents, and projects across a busy company
> - Search previously supported only a bare text query: no way to narrow
by status/assignee/project/label/date, no sort control, no typed
operators, and weak relevance/snippets meant hunting through noise
> - As companies accumulate tens of thousands of items, unfiltered
single-sort search stops scaling for day-to-day operator workflows
> - This pull request adds a full filtering model (filter bar, chips,
mobile sheet, URL state), sort modes, typed query operators (`status:`,
`assignee:`, `type:`, …) with command-palette parity,
relevance/snippet/deep-link improvements, zero-results recovery, and the
supporting shared validators, backend service work, and DB indexes
> - The benefit is that operators can go from a vague query to the exact
item in a couple of keystrokes, on desktop and mobile, with shareable
filtered-search URLs
## Linked Issues or Issue Description
No existing public GitHub issue; describing the underlying feature
request inline (per feature_request template):
- **Problem:** Company search accepted only a plain text query. Users
could not filter results by status, assignee, project, label, or
recency; could not change result ordering; and got no guidance when
filters emptied the result set.
- **Desired solution:** Structured search filters (UI controls + typed
query operators + URL parameters), selectable sort modes, better
relevance and snippets with exact deep links, and parity between the
search page and the command palette.
- **Alternatives considered:** Client-side filtering of unfiltered
results (does not scale past the fetch limit); a separate "advanced
search" page (splits the surface and duplicates state handling).
Related (not duplicate) PRs found while searching: #4848 (issue search
query planning), #8235 (search rate limiting).
## What Changed
- **Shared contract:** new search filter/sort/count/zero-results types
and validators in `packages/shared` (`validators/search.ts`, types
index).
- **Backend:** `server/src/services/company-search.ts` supports issue
filters, sort modes, per-filter option counts, snippets, artifact
visibility, and zero-results loosen suggestions; single-statement match
replaces per-scope scans and predicates are trigram-index compatible
(~3.7s → ~350ms on a live 14.8k-hit corpus).
- **DB:** migration `0142_company_search_sort_indexes.sql` adds the
supporting indexes.
- **Search page (`ui/src/pages/Search.tsx`):** filter bar, removable
chips, mobile filter sheet with result-count preview, sort menu, URL
round-tripping, zero-results recovery UI.
- **Query operators (`ui/src/lib/search-query-parser.ts`):** typed
operators parsed into filters, operator autocomplete, filter pills.
- **Command palette:** operator-aware parsing and full-search handoff.
- **Stale-operator fix (latest commit):** typed operator filters are no
longer folded into persistent URL-filter state, so deleting a token
(e.g. removing `status:blocked` from the input) actually removes the
filter from subsequent requests; filter-control edits materialize
control state and strip typed tokens so a removed chip cannot resurrect
from the input.
## Verification
- `cd ui && npx vitest run src/pages/Search.test.tsx` — 19 tests
including two new red→green regressions for the stale-operator paths
(both fail on the previous commit, pass now).
- `cd ui && npx vitest run src/components/CommandPalette.test.tsx` and
`cd server && npx vitest run
src/services/company-search-service.test.ts` — operator parity and
backend filter/sort/count coverage.
- `cd ui && npx tsc --noEmit` — clean.
- Manual: open `/search`, type `auth status:blocked`, confirm the status
filter applies; delete `status:blocked`, confirm results are unfiltered
again; drive the same filters from the filter bar/chips/mobile sheet and
confirm the URL round-trips (reload/back/forward preserves state).
- Full end-to-end QA pass (9/9 acceptance checks) against the wireframes
on desktop (1280px) and mobile (390px) with a live API and browser
automation.
## Risks
- Additive migration (indexes only, no data rewrites) — safe to roll
forward; index creation cost is paid once at migrate time.
- Search request shape gains optional parameters only; old clients keep
working.
- Behavioral shift: filter-control edits now strip typed operator tokens
from the query text (their values persist as filter state) — deliberate,
so removed filters stay removed.
- Ranking changes alter result ordering for existing queries; covered by
service tests and the QA pass.
> 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 Fable 5 (`claude-fable-5`, Anthropic, extended thinking + tool
use) — stale-operator-filter fix, regression tests, PR preparation.
- GPT-5 Codex (`codex_local` adapter) and Claude Opus 4.6
(`claude-opus-4-6`) — earlier implementation phases (backend contract,
filter UI, operators, ranking) under agent orchestration.
## 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)
- [ ] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
— pre-existing branch name retained to avoid closing/reopening the PR
- [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
user-facing docs affected)
- [x] I have considered and documented any risks above
- [ ] All Paperclip CI gates are green (pending re-run on latest commit)
- [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
(pending re-review of the stale-filter fix)
- [x] I will address all Greptile and reviewer comments before
requesting merge
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - Execution workspaces are the bridge between Paperclip's control
plane and a local agent's checked-out repository state.
> - When a workspace is restored after a failed or interrupted run, the
recorded branch can disagree with the branch currently checked out on
disk.
> - A clean branch mismatch can be reconciled safely, but a dirty
mismatch needs a lossless path that does not discard uncommitted agent
work.
> - This pull request adds a quarantine-and-restore path that saves
dirty work to a rescue branch, restores the recorded branch, and exposes
the repair from the board UI and run page.
> - The benefit is that operators can recover wedged execution
workspaces without losing work or moving another live branch
unexpectedly.
## Linked Issues or Issue Description
No public GitHub issue exists for this workspace-recovery failure, so
this PR includes the bug report inline.
**What happened**
A git worktree-backed execution workspace could become wedged when
Paperclip expected one branch but found a different checked-out branch
with dirty tracked or untracked files. The existing safe repair path
refused the restore, leaving the source task blocked with no lossless
one-click recovery path.
**Expected behavior**
Paperclip should preserve dirty work before restoring the recorded
workspace branch. If another live workspace claims the checked-out
branch, or an attached runtime service is active, the repair should
refuse with clear operator-facing evidence instead of risking work loss
or file contention.
**Steps to reproduce**
Create a git worktree execution workspace whose persisted branch name
differs from the checked-out branch, add dirty tracked or untracked
files in that worktree, then trigger workspace validation or use the
branch reconcile endpoint. Before this change, the dirty mismatch
remained blocked because Paperclip had no quarantine restore mode.
**Paperclip version or commit**
Observed on the pre-fix workspace-recovery implementation. Verified on
this PR head after rebasing onto current `master`.
**Deployment mode**
Local trusted development/worktree deployments using git worktree
execution workspaces and optional workspace runtime services.
## What Changed
- Added dirty-worktree quarantine repair that creates a rescue branch,
commits dirty tracked and untracked files there, restores the recorded
branch, writes audit comments/activity, and preserves the live foreign
branch ref.
- Added `quarantine_restore` branch reconcile API support,
recovery-action resolution, source-task wake behavior, execution-review
preservation, claimant refusal, runtime-service refusal, and coverage
for the non-transactional git ordering.
- Added board UI controls for the repair action in the recovery card
plus a compact failed-run workspace recovery surface that uses the same
reconcile handlers.
- Hardened Greptile follow-up cases by best-effort restoring the
recorded branch after a mid-sequence rescue commit failure and by
refusing quarantine restore while attached runtime services are active.
## Verification
- `pnpm exec vitest run
server/src/__tests__/execution-workspaces-service.test.ts -t
"quarantine_restore"`
- `pnpm exec vitest run server/src/__tests__/workspace-runtime.test.ts
-t "workspace dirty quarantine branch repair"`
- `pnpm exec vitest run
server/src/__tests__/heartbeat-workspace-finalize-branch.test.ts -t
"repairs clean unrecorded branch drift|adopts unrecorded forward branch
drift"`
- `pnpm exec vitest run
server/src/__tests__/workspace-runtime-routes-authz.test.ts`
- `pnpm --filter @paperclipai/server typecheck`
- Earlier PR verification covered the route, service, heartbeat, UI
component, and run-page recovery surfaces; Cutter posted public preview
screenshots for the repair popover and run-page panel at
https://github.com/paperclipai/paperclip/pull/9297#issuecomment-4926934211.
- GitHub PR checks are green on
`dcac76b05f4cf6e1ee16544c2831d83c7857e475`.
- Greptile is 5/5 with zero annotations and no unresolved review threads
on `dcac76b05f4cf6e1ee16544c2831d83c7857e475`.
## Risks
- Moderate risk because the change intentionally runs git commands
against local worktrees; the implementation refuses dirty repair when
another claimant or active runtime service is detected and records
rescue refs for auditability.
- Compatibility / release-note callout for self-hosted operators:
existing instances that left `enableWorkspaceBranchReconcileForward`
unset now get automatic forward branch reconciliation during heartbeat
workspace recovery. Operators who want the previous advisory-only
behavior can set `experimental.enableWorkspaceBranchReconcileForward` to
`false`; dirty quarantine repair can likewise be disabled with
`experimental.enableWorkspaceDirtyQuarantineRepair: false`.
- If the git rescue succeeds but a later database write fails, the
worktree may already be restored while the recovery action remains open;
this ordering is documented in code because git side effects cannot
participate in the database transaction.
- UI risk is limited to the workspace recovery surfaces and covered by
component tests plus the existing Cutter visual preview.
## Model Used
OpenAI Codex coding agent based on GPT-5, with repository tool use,
shell execution, and local test execution. Earlier preserved commits on
this branch also show Claude Code / Claude Opus 4.8 assistance in their
commit metadata.
## 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] Branch naming exception documented: this PR preserves the existing
worktree branch requested for publication while keeping the PR title and
body public-facing
- [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: Claude Opus 4.8 <noreply@anthropic.com>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - The board UI keeps issue, agent, activity, and run state fresh
through polling across several pages and sidebar surfaces
> - When multiple components or browser tabs poll the same company data
at the same time, the API can receive bursts of duplicate issue-list
requests
> - Those duplicate requests increase database and server load without
returning meaningfully different data
> - This pull request adds server-side compression/coalescing plus
client-side visibility-aware and cross-tab shared polling
> - The benefit is lower request volume during normal board usage while
preserving fresh UI data for active users
## Linked Issues or Issue Description
No exact public GitHub issue was found for this request.
Problem:
- The board can issue redundant polling requests for the same issue-list
data from multiple UI surfaces and tabs.
- In busy operator sessions, those bursts can trigger request-storm
behavior and unnecessary issue-list load.
- Expected behavior is to reuse identical in-flight work server-side and
reduce hidden-tab or duplicate-tab polling client-side while preserving
normal refresh behavior.
Related public context found during duplicate search:
- #8206 covers a different board UI 404-storm scope.
- #5165 covers separate issue-list behavior around page-size truncation.
## What Changed
- Added API compression middleware foundation and a company/created-at
index for heartbeat run access.
- Added server-side issue-list request storm detection and identical
in-flight request coalescing.
- Added UI fetch metadata, visibility-aware polling, and request
deduplication for issue/activity/client calls.
- Added cross-tab shared polling primitives and wired them into the
sidebar, inbox, dashboard, issue, project, routine, and agent surfaces.
- Resolved the latest `master` migration collision by keeping upstream
`0140_built_in_managed_resources.sql` and renumbering this branch's
heartbeat-run index migration to
`0141_heartbeat_runs_company_created_at_index.sql`; the SQL uses `CREATE
INDEX IF NOT EXISTS` for idempotency.
- Stabilized server heartbeat cleanup tests exposed by the PR check
matrix.
- Fixed the Greptile compression follow-up by weakening strong ETags on
encoded JSON responses and bypassing compression for streamed/download
responses.
## Verification
- `pnpm exec vitest run ui/src/components/IssuesList.test.tsx
ui/src/pages/Inbox.test.tsx` — passed after resolving the latest
`master` conflict in `IssuesList.tsx` and updating the 200-result cap
expectations.
- `pnpm check:token-gates` — passed after the UI conflict resolution.
- `jq -e '.entries | length as $n | (map(.idx) | unique | length == $n)
and (map(.tag) | unique | length == $n)'
packages/db/src/migrations/meta/_journal.json` — passed after
renumbering the migration to `0141`.
- `pnpm exec vitest run server/src/__tests__/api-compression.test.ts` -
passed after the compression follow-up.
- `pnpm exec vitest run
server/src/__tests__/issue-list-assignee-filter-routes.test.ts` - passed
after the compression follow-up.
- `pnpm --filter @paperclipai/server typecheck` - passed after the
compression follow-up.
- Greptile Review for head `8dbddac41ec273fda404100b4981ddb912fad57b` -
passed after the latest conflict/migration fix; all Greptile review
threads are resolved.
- `pnpm exec vitest run server/src/__tests__/api-compression.test.ts
server/src/__tests__/issue-list-assignee-filter-routes.test.ts
ui/src/api/client.test.ts ui/src/api/issues.test.ts
ui/src/lib/polling.test.ts ui/src/lib/cross-tab-poll.test.ts
ui/src/pages/Inbox.test.tsx ui/src/components/SidebarProjects.test.tsx
ui/src/components/SidebarAccountMenu.test.tsx
ui/src/lib/issueDetailCache.test.ts` — passed.
- `pnpm exec vitest run ui/src/api/client.test.ts` — passed.
- `pnpm exec vitest run ui/src/pages/Inbox.test.tsx
ui/src/components/SidebarProjects.test.tsx
ui/src/components/SidebarAccountMenu.test.tsx
ui/src/lib/issueDetailCache.test.ts` — passed.
- `pnpm exec vitest run
server/src/__tests__/heartbeat-worktree-suppression.test.ts` — passed.
- `pnpm exec vitest run
server/src/__tests__/low-trust-red-team-routes.test.ts` — passed.
- `pnpm exec vitest run
server/src/__tests__/heartbeat-workspace-finalize-branch.test.ts` —
passed.
- `pnpm --filter @paperclipai/ui typecheck` — passed.
- `pnpm --filter @paperclipai/server typecheck` — passed.
- GitHub PR checks for head `8dbddac41ec273fda404100b4981ddb912fad57b`:
all GitHub Actions/status checks passed; Greptile, Superagent, Socket,
Snyk, build, typecheck/release registry, general tests, serialized
server suites, e2e, canary dry run, policy, and commitperclip review are
green; Storybook visual regression and security-review were
skipped/neutral by policy.
- Confirmed this branch does not include `pnpm-lock.yaml` or
`.github/workflows` changes.
## Risks
- Medium risk: issue-list coalescing changes request timing and cache
semantics for a hot API path.
- Medium risk: cross-tab polling uses browser coordination primitives,
so older or unusual browser environments need fallback behavior to stay
correct.
- Low migration risk: the new index migration is ordered after current
`master` and uses `CREATE INDEX IF NOT EXISTS`.
> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.
## Model Used
OpenAI Codex coding agent, GPT-5-based model, tool-enabled with
shell/git execution. Exact hosted deployment identifier and
context-window size were not surfaced in the agent runtime.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - Operators use the issue list, blocked-work notices, pipeline body
documents, and issue documents to understand what agents are doing
> - Some of those surfaces had low-information details: live-work
blockers were visually easy to miss, revision history showed generic
actor labels, and selected active subtasks carried an extra accent
border
> - These details matter because Paperclip's default UI should make
agent state and work history legible without requiring users to inspect
raw logs
> - This pull request groups a small set of related operator UI polish
changes for those work-state and document-history surfaces
> - The benefit is clearer review context for blocked work, better
attribution in document revision history, and cleaner active-subtask row
styling
## Linked Issues or Issue Description
No public GitHub issue found during dedup search.
### Bug Report
**Pre-submission checklist**
- Searched existing open PRs/issues for `document revision authors` and
`blocked notice live work`; no duplicates found.
- Reproduced against the current `master` base for this PR.
- Confirmed this is core board UI behavior, not adapter/provider/local
configuration.
**What happened?**
Blocked live-work notices, document revision history, pipeline body
document revision history, and active subtask rows exposed technically
correct but low-signal UI details. Revision menus could show generic
`Board`/`Agent` labels instead of the specific author, and selected
active subtasks had an extra accent border.
**Expected behavior**
Operators should see clear blocked-live-work copy, recognizable revision
authors using available agent/user profile data, and visually consistent
selected task rows.
**Steps to reproduce**
1. Open an issue with a live-work blocker and inspect the blocked
notice.
2. Open an issue document revision menu with agent/user-authored
revisions.
3. Open a pipeline item body document revision menu with
agent/user-authored revisions.
4. Inspect a selected active subtask row in the issue list.
**Paperclip version or commit**
`8b6a06ee2` (`origin/master` at branch creation).
**Deployment mode**
Local dev / self-hosted board UI.
**Installation method**
Built from source.
**Agent adapter(s) involved**
Not adapter-specific (core board UI).
**Database mode**
Not database-related.
**Access context**
Board operator UI.
**Relevant logs or output**
Not applicable.
**Relevant config**
Not applicable.
**Additional context**
This PR is a small polish/fix contribution. `ROADMAP.md` allows tightly
scoped bugs and polish without roadmap-level coordination.
**Privacy checklist**
Reviewed the PR body for private instance references and omitted
internal ticket links.
## What Changed
- Polished the live-work blocked notice copy and tests so operators get
a clearer explanation of what is blocking progress.
- Show issue document revision authors with agent/user names and avatar
treatment instead of generic actor labels.
- Show pipeline body document revision authors with the same available
agent/user profile data.
- Removed the extra left accent border from selected active subtask
rows.
- Stabilized the document revision test helper for React runtimes where
`act` is not exported as a function.
## Verification
- `git diff --check origin/master..HEAD`
- `git diff --name-only origin/master..HEAD -- .github/workflows
pnpm-lock.yaml` produced no output.
- `pnpm check:token-gates` passed: all color literal, arbitrary bracket
value, and raw font-size gates clean.
- `pnpm exec vitest run ui/src/components/IssueBlockedNotice.test.tsx
ui/src/components/IssueDocumentsSection.test.tsx
ui/src/components/IssueRow.test.tsx` passed: 3 test files, 32 tests.
- `pnpm --filter @paperclipai/ui typecheck` passed.
- Remote PR checks passed on head `3e18fe3f`: 22 complete, 0 pending, 0
failing.
- Greptile completed at 5/5 with no blocking issues after addressing the
pipeline revision author feedback.
## Risks
Low risk. The change is limited to board UI rendering and component
tests. The main risk is a subtle visual regression in issue/document
surfaces that are not covered by screenshots; the affected component
tests cover the intended copy, attribution, and row-style 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 coding agent, GPT-5 model family, tool-enabled CLI session.
Exact runtime context window is not exposed by this execution
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>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - Operators need first-party agent capabilities for repeatable company
work, not just manually created one-off agents.
> - Built-in agents need to behave like normal company-scoped agents
while preserving approval gates, permissions, budgets, and audit trails.
> - Reflection and coaching work also needs bundled instructions, skill
content, and a routine so the feature can be installed and reset
predictably.
> - The API, database, UI, portability, and tests all need to agree on
the built-in lifecycle from not provisioned through setup, approval,
ready, paused, and reset.
> - This pull request adds built-in agent provisioning and the
Reflection Coach bundle end-to-end.
> - The benefit is a safer first-party path for Paperclip-managed agents
without bypassing the same governance model used for operator-created
agents.
## Linked Issues or Issue Description
No public GitHub issue was found for this exact built-in agent and
Reflection Coach bundle work.
Problem/motivation:
- Paperclip did not have a first-party built-in agent lifecycle for
product-owned agents.
- Bundled agent resources such as default instructions, skills, and
routines needed managed ownership and reset semantics.
- Approval-gated companies needed built-in setup to preserve requested
adapter, budget, manager, and permission state through board approval.
- The board UI needed clear built-in badges, setup affordances,
readiness state, and bundle status without exposing secrets.
Proposed solution:
- Add a company-scoped built-in agent registry,
provisioning/reset/reconcile/status APIs, and Reflection Coach bundled
resources.
- Track bundled managed resources in the database with idempotent
migration behavior.
- Reuse existing agent approval, authorization, budget, and activity-log
paths instead of creating a bypass.
- Add UI setup, badges, gates, bundle panels, and route coverage for
built-in agents.
Duplicate search:
- Searched GitHub PRs for `built-in agents Reflection Coach
repo:paperclipai/paperclip`; only this PR was returned.
- Searched GitHub issues for the same query; no public issues were
returned.
## What Changed
- Added built-in agent definitions, lifecycle state derivation,
provisioning, reset, reconcile, status, and routine-control routes.
- Added the `built_in_managed_resources` migration and schema exports
for bundled instructions, skill, and routine ownership.
- Added the Reflection Coach built-in bundle with default instructions,
skill catalog content, routine template, default permissions, and
managed-resource drift handling.
- Added approval-aware provisioning behavior that preserves requested
adapter config, budgets, manager assignment, and built-in permissions
through hire approval.
- Added authorization and mutation gates for built-in agent and skill
changes, including consented Reflection Coach change paths.
- Added UI surfaces for built-in agent setup, roster/detail badges,
readiness gates, bundle status, routine controls, and route filtering.
- Added company import/export and validator coverage for built-in
managed resources and low-trust/red-team presets.
- Addressed Greptile follow-ups for pending approval reconciliation,
consent-gate error propagation, config-read authorization fallback,
approval-path manager preservation, and non-model adapter provisioning.
## Verification
Local verification:
- `git diff --check public/master..HEAD` passed.
- `pnpm check:token-gates` passed with all gates clean.
- `pnpm exec vitest run
ui/src/components/ConfigureBuiltInAgentModal.test.tsx` passed: 1 file, 4
tests.
- `pnpm exec vitest run ui/src/components/EntityRow.test.tsx
ui/src/pages/Agents.test.tsx ui/src/components/BuiltInAgentGate.test.tsx
ui/src/components/ConfigureBuiltInAgentModal.test.tsx
ui/src/components/BuiltInBundlePanel.test.tsx
ui/src/pages/InstanceExperimentalSettings.test.tsx
ui/src/pages/Routines.test.tsx` passed: 7 files, 64 tests.
- `pnpm --filter @paperclipai/server exec vitest run
src/__tests__/built-in-agents.test.ts
src/__tests__/authorization-service.test.ts
src/__tests__/company-skills-routes.test.ts` passed: 3 files, 91 tests.
- `pnpm --filter @paperclipai/db check:migrations` passed.
- `pnpm -r typecheck` passed after the rebase; `pnpm --filter ui
typecheck` passed after the final UI review fix.
Remote verification on latest head
`1c61f693a4ec881d739022b0e75a8ca8bf8c2cd8`:
- Merge state: `CLEAN`.
- Greptile: `5/5`, zero unresolved Greptile threads.
- PR check rollup: all checks successful, neutral, or skipped as
expected.
- Passing gates include Build, Typecheck + Release Registry, all server
shards, all workspace shards, all serialized server suites, e2e, Canary
Dry Run, policy, review, verify, Socket, Superagent, and Snyk.
## Risks
- This adds a new managed-resource table and migration; the migration
uses idempotent create/add/index guards and passed migration safety
checks.
- Built-in agent provisioning touches approval and authorization paths;
tests cover pending approval preservation, stale retry rejection,
consent gates, and config-read fallback behavior.
- Reflection Coach creates managed instructions, skill, and routine
resources; drift/reset behavior is covered by service tests and redacted
API responses.
- Non-model adapter setup now provisions a `needs_setup` built-in row
before command/endpoint fields are complete; this matches the server
lifecycle and is covered by the setup modal regression test.
## Model Used
OpenAI Codex coding agent based on GPT-5. Exact hosted model ID,
context-window size, and reasoning-mode labels are not exposed in this
runtime; tool use, shell execution, GitHub CLI/API access, and local
code editing 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>
## Thinking Path
> - Paperclip's UI is governed by the design system merged in #9134 and
the component convergence in #9240 — one Card, one Badge, one nav row,
one `IssueRow`, a single multiplicative radius ladder.
> - With those primitives in place, the remaining rough edges were
interaction and alignment details on the surfaces people use every day:
the inbox, the task list, the sidebar, and the routine/task detail
pages.
> - Each item here was reported from live use and fixed against a
running instance, then verified by measurement (pixel alignment, frame
timing) rather than by eye alone.
> - The result is that the inbox and task lists now behave as one system
(same hover, keyboard nav, tree-guide, and archive language), list
nesting reads correctly, list hover is smooth, and the routine detail
page scrolls and aligns like the rest of the app.
## Linked Issues or Issue Description
No public GitHub issue exists for this work; describing per the feature
template.
- **Problem**: after the design-system foundation (#9134) and component
convergence (#9240), the inbox/task lists still had interaction and
alignment gaps — hover lag on long lists, keyboard navigation that only
partly matched between the two lists, workspace/parent nesting whose
guides and chevrons didn't line up, an inbox that sat offset from the
task list, and a routine detail page with an odd double-scroll and a
bespoke sub-nav.
- **Proposed behavior**: the inbox and task lists share one interaction
contract (hover, keyboard nav, collapse, archive), list nesting aligns
to the status column with clean chevrons, list hover is CSS-only (no
per-hover re-render), and the routine detail page uses a fixed
header/sub-nav with a single scrolling content region and a sub-nav that
matches the primary nav.
## What Changed
- **List hover performance**: hover is now painted purely by CSS
`:hover` and records the hovered row in a ref, instead of writing
list-selection state on every `mouseenter` (which re-rendered 100–300
non-memoized rows per hover). Keyboard nav reads the ref so it still
continues from the hovered row; the keyboard band clears on the first
real mouse move so hover and keyboard selection never show two bands at
once. The row's `transition-colors` fade was removed so the highlight
snaps (no comet-tail). Measured on a 100-row scrub: worst frame **333 ms
→ 33 ms**, long frames (>50 ms) **12 → 0**, avg **41 → 60 fps**.
- **Inbox ↔ task-list parity**: keyboard navigation works on every inbox
tab (archive/read keys stay scoped to the archivable tab); group headers
and parent tasks collapse/expand with the arrow keys in both lists; the
task list gains the same j/k / arrows / Enter selection model as the
inbox; hover selection bands match; the `g` then `i` go-to-inbox chord
works app-wide.
- **List nesting & alignment**: the workspace group-header chevron lines
up exactly with the task chevrons below it (both lists); the
parent→child connector line drops from under the parent's **status
icon** rather than its chevron and breaks with a 14px gap around a
nested row's own chevron; and inbox rows line up with the task list
(read rows no longer reserve a mark-read column).
- **Inbox archive affordance**: moved from a bare `x` left of the status
icon to an `Archive` icon + label button on the right (before the
timestamp), revealed on row hover; the left slot now carries only the
unread dot; swipe-to-archive is unchanged.
- **Sidebar**: when no agent has a live run, the AGENTS section shows 3
recent agents (was 5) plus "See all agents"; the working-agents view is
unchanged.
- **Routine detail page**: the layout is bounded to the main scroll area
so the header (Run now + automation toggle) and the sub-nav stay fixed
and only the section content scrolls (was a page-level scroll competing
with a `sticky` sub-nav). The sub-nav items adopt the primary nav's
rhythm — row padding/height, inset rounded pill, type scale, and 16px
icons — and its background matches the main nav.
- **Task detail**: dropped the redundant `🔵` prefix the breadcrumb added
for live/in-progress tasks; the status glyph already conveys that state.
## Verification
- `pnpm check:token-gates` → 3/3 CLEAN
- `pnpm typecheck` → green (all packages)
- `cd ui && npx vitest run` → 2255/2255 (assertions updated in lockstep
where behavior changed)
- `pnpm --filter @paperclipai/ui build` → exit 0
- Storybook visual regression: run locally throughout (the
baseline-manifest archive is still unpublished, so CI cannot run this
suite — pre-existing condition from #9134). Every visible delta was
reviewed against a live instance and, where it was intentional (nesting
alignment, routine sub-nav restyle, unread-row shift), the affected
snapshots were re-baselined locally.
- Manual/measured: inbox + task lists (grouped and nested, light +
dark), hover-scrub frame timing, keyboard navigation, and the routine
detail scroll/alignment were exercised on a running instance.
## Risks
- Behavior-and-alignment changes concentrated in `IssueRow` /
`IssuesList` / `Inbox` (the shared task-row surfaces). The riskiest area
— the hover/keyboard-selection model — is covered by unit tests (updated
in lockstep) and was measured and driven live.
- The routine detail scroll change restructures that page's layout
container; verified the page's own scroll stays fixed while only the
section content scrolls.
- The visual suite cannot yet run in CI (unpublished baseline archive —
pre-existing); snapshot coverage is local-only until that lands.
## Model Used
- Claude Fable 5 (`claude-fable-5`, Anthropic) via Claude Code — agentic
coding with tool use (file editing, test execution, Playwright
measurement/screenshot verification); extended thinking 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
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - The Agents index (`ui/src/pages/Agents.tsx`) is the main roster
view, with both a list layout and an org-tree layout
> - On mobile viewports the roster could render rows with no visible
agent names because shrink-resistant trailing controls competed with the
fixed-width title cell
> - A roster where names are unreadable is unusable on phones, and the
org-tree view had a related narrow-width overflow risk for deeply
indented names
> - This pull request lets the list title flex below `xl`, hides
nonessential row controls on mobile, keeps Join/Leave reachable, and
truncates org-tree names safely
> - The result is a readable agents index on mobile while preserving
desktop meta-column alignment and truncation
## Linked Issues or Issue Description
No existing public GitHub issue; describing the bug per the bug report
template:
**What happened?**
On a mobile-width viewport, the agents index showed rows where agent
names could become unreadable or disappear because trailing controls
consumed the available row width. In the org view, deeply indented names
could overflow the row.
**Expected behavior**
Agent names remain visible on every viewport, Join/Leave stays
reachable, and desktop rows continue to align and truncate as before.
**Steps to reproduce**
Open Paperclip in a browser at a narrow viewport, navigate to the Agents
index, and observe rows with long names or left-membership controls.
**Paperclip version or commit**
Reproducible on `master` prior to this fix.
**Deployment mode**
Local dev instance. The bug is viewport-width dependent, not
deployment-mode dependent.
## What Changed
- `EntityRow` now lets callers control title text, subtitle text, and
the meta spacer classes while keeping the default truncation behavior
unchanged.
- Agents list rows now use `flex-1 xl:flex-none xl:w-56`, so names get
mobile width while desktop meta columns keep their aligned fixed title
column.
- Long list-view names/subtitles wrap below `xl` but return to
truncation at `xl` and above.
- Join/Leave stays visible on mobile; run/status/star row controls stay
hidden on mobile to avoid squeezing names.
- Org-tree rows use `min-w-0 truncate`, so deep indentation shortens
names with an ellipsis instead of overflowing.
- Regression tests cover mobile name visibility, left-membership
dimming, responsive title/meta behavior, and mobile Join/Leave
reachability.
## Verification
- `pnpm --filter @paperclipai/ui exec vitest run
src/components/EntityRow.test.tsx src/pages/Agents.test.tsx` — 2 files,
18 tests passed.
- `pnpm check:token-gates` — all gates clean.
- `git diff --cached | rg -n "(AKIA|ASIA|SECRET|TOKEN|PASSWORD|PRIVATE
KEY|BEGIN RSA|BEGIN
OPENSSH|api[_-]?key|bearer|paperclip_api_key|DATABASE_URL|postgres://|sk-[A-Za-z0-9]|xox[baprs]-)"`
— no matches before push.
## Risks
- Low risk: UI-only change scoped to the agents index. The main behavior
change is that nonessential row controls remain hidden on mobile while
Join/Leave remains available there and all controls remain available on
larger screens/detail pages.
## Model Used
- OpenAI GPT-5 via Codex (`codex_local` adapter), agentic code editing
and local/GitHub 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
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - The Skills Manager gives operators a reusable skill layer, but
iteration still required manual edits, ad hoc prompts, and indirect run
inspection.
> - Skill authors need a focused workflow for editing skill files,
saving representative test inputs, and running those inputs through an
agent without exposing harness tasks as normal company work.
> - The backend therefore needs durable test inputs, reusable run
templates, hidden harness issues, scoped run execution, retention
metadata, and read-containment rules around hidden work.
> - The frontend needs a three-pane Studio that keeps skill files, saved
inputs/templates, and run output/history visible together while
preserving the existing design system and token rules.
> - This pull request ships that Skill Studio surface end to end:
database migrations, shared contracts, server APIs/services, hidden
harness execution behavior, UI routes/components, and focused tests.
> - The benefit is faster and safer skill iteration, with inspectable
outputs and fewer ways for internal harness work to leak into normal
task lists, costs, or adjacent read APIs.
## Linked Issues or Issue Description
No public GitHub issue exists for this feature. Feature request summary:
- Problem: Skill authors need to edit and test company skills in one
place instead of switching between the skill detail page, task creation,
run output, and manual prompt history.
- Proposed solution: Add a Skill Studio workbench with saved inputs,
reusable templates, hidden sandboxed test runs, live run status, output
inspection, run history, rerun/delete controls, and frontmatter-aware
editing.
- Expected users: Paperclip operators and agent-company maintainers who
create, fork, import, and tune skills.
- Related public PRs: Supersedes #9205, which was replaced so the public
PR branch name follows contributor policy.
- Duplicate search: searched public GitHub issues and PRs for "Skill
Studio"; no other active public issue or PR directly covers this
feature.
## What Changed
- Added database migrations for Skill Studio test inputs, test runs,
test run retention, and reusable run templates.
- Added shared Skill Studio types, validators, route helpers,
frontmatter utilities, and status handling.
- Added server services and routes for saved inputs, test runs,
templates, reruns, terminal-run deletion, hidden harness issue
execution, and run-detail hydration.
- Strengthened hidden-issue read containment across issue-adjacent
routes and cost rollups used by skill test harness work.
- Added the Skill Studio UI with skill file editing, frontmatter
editing, saved inputs, templates, run creation/cancel/rerun/delete
flows, output rendering, history, route support, and responsive pane
behavior.
- Added focused backend, shared, and UI tests for the new APIs, routing
logic, editor/run behavior, hidden-issue containment, and migration
safety.
- Rebased onto current `master`, removed the generated lockfile diff
from the PR, and verified no workflow files are changed.
## Verification
- [x] `pnpm --filter @paperclipai/db check:migrations`
- [x] `pnpm check:token-gates`
- [x] `pnpm exec vitest run
server/src/__tests__/company-skills-service.test.ts
server/src/__tests__/company-skills-routes.test.ts
server/src/__tests__/company-skill-test-runs-service.test.ts
ui/src/lib/skill-studio.test.ts ui/src/pages/SkillStudio.test.tsx` — 5
files, 132 tests passed
- [x] Greptile review on the latest PR head
- [x] GitHub PR checks on the latest PR head
## Risks
- Medium risk because this is a broad feature touching database schema,
server orchestration, issue visibility, and a large UI surface.
- Hidden harness issue containment is security-sensitive; this PR
includes regression coverage for adjacent read paths and cost rollups.
- The new migrations are additive and use idempotent guards where
applicable, but deployed databases that previously tested draft
migration numbers should still be checked carefully.
- The UI depends on a new resizable panels package in `ui/package.json`;
the lockfile is intentionally left to repository automation.
> 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 coding agent with shell, git, and GitHub CLI tool
use. Earlier feature commits include assistance from other Paperclip
coding agents; this PR preparation, rebase, cleanup commit, and PR body
were completed by OpenAI Codex in a Paperclip worktree.
## 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: Claude Opus 4.8 <noreply@anthropic.com>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - Operators rely on the issue detail thread to understand whether a
task is blocked, live, or waiting on another task.
> - A blocked issue can have a healthy blocker chain where downstream
work is actively running and the parent will resume automatically.
> - Showing that case with the same amber blocked notice as a stalled or
attention-needed blocker makes the state look more severe than it is.
> - The UI already receives blocker-attention state, blocker summaries,
and company live-run ids, so this can be clarified without a new API
shape.
> - This pull request adds a blue "Waiting on live work" notice for
covered blocker chains while preserving the existing amber notice for
the other blocked states.
> - The benefit is that operators can distinguish healthy queued work
from blocked work that needs intervention.
## Linked Issues or Issue Description
Refs #3820
Refs #8271
Related PR: #3877
Supersedes #9295
## What Changed
- Added a blue `IssueBlockedNotice` variant when
`blockerAttention.state` is `covered` and the blocker chain has live
work.
- Rendered blocker-chain progress as done, running, and queued steps,
including a "Now running" row for live terminal blockers.
- Preserved the existing amber blocked notice for stalled,
attention-needed, ordinary blocked, and successful-run handoff states.
- Plumbed the existing `liveIssueIds`, `blockedBy`, and
`blockerAttention` data from issue detail into the chat-thread blocked
notice.
- Added regression coverage around the covered live-work state, the
no-confirmed-live fallback, numeric step ordering, and amber fallback
states.
- Hardened a low-trust server route test cleanup helper so CI deletes
heartbeat run events before deleting heartbeat runs.
## Verification
- `pnpm check:token-gates`
- `pnpm --filter @paperclipai/ui typecheck`
- `pnpm exec vitest run ui/src/components/IssueBlockedNotice.test.tsx`
- GitHub PR workflow is green on head
`52ab6d9076ce233c183bf7133fa666e8597b6765`.
- Greptile check is green on head
`52ab6d9076ce233c183bf7133fa666e8597b6765` with zero unresolved review
threads.
## Risks
Low runtime risk: the product change is frontend-only and uses data
already returned to the issue detail page. The main risk is visual
regression in the blocked notice; the change keeps non-covered states on
the existing amber path and adds focused regression coverage. The
server-side change is test-only cleanup for an existing CI shard
failure.
> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.
## Model Used
OpenAI Codex using GPT-5, tool-use enabled in a repository workspace.
The runtime did not expose a more specific model build id or
context-window size.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - The work timeline helps operators understand what agents did, when
they did it, and who kicked off each run
> - Timeline statistics should describe the window the operator is
viewing, not hidden work outside the viewport
> - Kickoff chips should point to the run that was actually closest to
the triggering edge, not stale later runs on the same issue
> - This pull request scopes timeline summary stats to the visible
window and deduplicates kickoff attribution to the nearest matching run
> - The benefit is that operators get a more accurate, less misleading
timeline while scanning agent work
## Linked Issues or Issue Description
No matching public GitHub issue was found. Public duplicate search found
no open PR for "work timeline visible window kickoff".
### What happened?
Work timeline summary stats could count hidden spans outside the
selected visible window, and kickoff attribution could appear on a stale
matching run instead of the closest run associated with the edge.
### Expected behavior
Visible timeline stats should reflect only the current visible range,
and each kickoff edge should attach to the nearest matching run.
### Steps to reproduce
1. View a work timeline with agent spans that begin before or end after
the selected visible window.
2. Compare the summary stats against only the spans visible in the
viewport.
3. View repeated runs for the same actor and issue that share a kickoff
edge.
4. Check which run gets the kickoff chip.
### Paperclip version or commit
Current `origin/master` before this PR.
### Deployment mode
Board UI.
## What Changed
- Filters timeline summary stats to the visible time window.
- Passes visible-window metadata into the work timeline chart and page
state.
- Assigns each kickoff edge to only the closest matching run, with
deterministic tie-breaking.
- Adds focused regression coverage for visible stats and stale kickoff
attribution.
## Verification
- `pnpm exec vitest run
ui/src/components/timeline/WorkTimelineChart.test.tsx
ui/src/lib/timeline/layout.test.ts ui/src/pages/Timeline.test.tsx` -- 38
tests passed.
- `pnpm check:token-gates` -- all gates clean.
- `git diff --check origin/master...HEAD` -- passed.
## Visual Evidence
Public review artifacts for the user-visible timeline behaviours. The
SVGs use sanitized run labels and no internal Paperclip issue
identifiers.


Artifact gist:
https://gist.github.com/cryppadotta/dc2645d54c4bc56ffc0323224b3ea4bf
## Risks
- Low risk. The change affects timeline rendering and summary
calculations only; the main risk is a subtle difference in which run
gets a kickoff chip when multiple runs are very close together.
> 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. See `CONTRIBUTING.md`.
## Model Used
OpenAI Codex coding agent based on GPT-5, tool-enabled shell workflow.
Exact hosted model variant and context window were not exposed by the
runtime.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have 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>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - Adapter packages are the bridge between the control plane and local
agent harnesses such as Claude Code, Codex, and Gemini CLI.
> - ACP support was concentrated in a separate `acpx_local` adapter,
which made ACP feel like a separate agent choice instead of an execution
capability of the harness adapters.
> - Claude, Codex, and Gemini now have ACP-capable harnesses, so the
native adapter should own ACP selection, fallback, config, transcript
parsing, and environment diagnostics.
> - The standalone ACPX adapter still needs a compatibility path for
existing rows, but it should not be offered as an active adapter for new
agents.
> - This pull request moves the shared ACP runtime into
`@paperclipai/acpx-engine`, wires Claude/Codex/Gemini local adapters to
prefer ACP when prerequisites are available, and retires `acpx_local` to
a tombstone.
> - The benefit is one adapter per harness, richer ACP transcripts by
default where possible, and a migration path for existing Claude/Codex
ACPX agents.
## Linked Issues or Issue Description
Closes#5932 — the broken default `acpx_local` Claude path is replaced
by native `claude_local` ACP support, existing Claude/Codex ACPX rows
migrate to native adapters, and new agents no longer choose the
standalone ACPX adapter.
Refs #4893 — original merged ACPX local adapter runtime that this PR
replaces with native per-harness ACP engines.
Refs #6590 — prior ACPX-Claude seamlessness work folded into the new
native Claude ACP path.
Refs #197 — related open generic ACP/Kiro adapter work; this PR does not
close it because Kiro/custom generic ACP remains a separate adapter
decision.
Refs #7018 — related Kimi-specific `acpx_local` shell failure; this PR
retires the built-in standalone adapter but does not add a native Kimi
adapter.
Refs #8864 — related ACPX prompt/API guidance PR; this PR moves runtime
guidance into the shared/native ACP engine path instead of the old
standalone adapter.
Refs #8881 — related `acpx_local` POSIX shell failure from the old
`acpx` pin; this PR updates ACP dependencies but does not claim
custom/OMP ACP support as a first-class native adapter.
Refs #8964 — related open `acpx_local` stderr cleanup PR; this PR makes
the old runtime path obsolete for new agents but keeps it as a
non-closing reference.
Problem description:
- The standalone `acpx_local` adapter duplicates Claude/Codex agent
choices that already have first-class local adapters.
- ACP should be an execution engine capability of each harness adapter
when the underlying harness supports ACP.
- Existing `acpx_local` agents should either migrate to native harness
adapters or fail with an explicit retirement message instead of silently
falling back to the process adapter.
## What Changed
- Added `@paperclipai/acpx-engine` as the shared ACP execution,
session-codec, CLI formatter, and UI parser package.
- Wired `claude_local`, `codex_local`, and `gemini_local` to auto-select
ACP by default when prerequisites pass, with `engine=cli` opt-out and
`engine=acp` strict mode.
- Added ACP config schema/UI fields, environment checks, session-codec
preservation, transcript parsing, and adapter capability metadata for
the native adapters.
- Retired `acpx_local` to a server tombstone, removed its
UI/package/runtime image surface, and added a migration for existing
Claude/Codex ACPX agents.
- Updated package manifests, lockfile, release tooling, docs, Kubernetes
sandbox defaults, and tests.
## Verification
- `corepack pnpm --filter @paperclipai/acpx-engine typecheck`
- `corepack pnpm --filter @paperclipai/adapter-claude-local typecheck`
- `corepack pnpm --filter @paperclipai/adapter-codex-local typecheck`
- `corepack pnpm --filter @paperclipai/adapter-gemini-local typecheck`
- `corepack pnpm --filter @paperclipai/acpx-engine exec vitest run`
- `corepack pnpm --filter @paperclipai/adapter-claude-local exec vitest
run src/server/acp.test.ts src/server/execute.acp-fallback.test.ts
src/ui/build-config.test.ts`
- `corepack pnpm --filter @paperclipai/adapter-codex-local exec vitest
run src/server/acp.test.ts src/ui/build-config.test.ts`
- `corepack pnpm --filter @paperclipai/adapter-gemini-local exec vitest
run src/server/acp.test.ts src/ui/build-config.test.ts
src/ui/parse-stdout.test.ts`
- `corepack pnpm --filter @paperclipai/plugin-sdk ensure-build-deps &&
corepack pnpm --filter @paperclipai/server exec tsc --noEmit`
- `corepack pnpm --filter @paperclipai/server exec vitest run
src/__tests__/adapter-routes.test.ts
src/__tests__/adapter-session-codecs.test.ts
src/__tests__/adapter-models.test.ts`
- `corepack pnpm --filter @paperclipai/ui typecheck`
- `corepack pnpm --filter @paperclipai/ui exec vitest run
src/adapters/metadata.test.ts
src/adapters/adapter-display-registry.test.ts
src/components/AgentConfigForm.test.ts
src/components/AgentConfigForm.render.test.tsx
src/components/transcript/RunTranscriptView.test.tsx`
- `node --test scripts/bootstrap-npm-package.test.mjs
scripts/release-package-map.test.mjs
scripts/verify-release-registry-state.test.mjs`
Note: the server typecheck script calls `pnpm` internally; this dev
shell exposes pnpm through Corepack only, so I ran the two script steps
manually with `corepack pnpm`.
## Risks
- Migration changes existing `acpx_local` Claude/Codex agents to native
adapter types and clears old ACPX task sessions/runtime state.
- Custom ACP commands remain on the retired tombstone and will need a
separate future adapter/plugin path.
- ACP auto-selection depends on local Node and ACP server command
prerequisites; remote and unsupported environments fall back to CLI
unless `engine=acp` is explicit.
- `@paperclipai/acpx-engine` is a new public package and needs npm
trusted-publishing bootstrap before release automation can publish it.
> 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 GPT-5 via Codex coding agent. Exact hosted model build and
context-window size are not exposed in this runtime. Tool use included
shell execution, repository editing, GitHub CLI operations, and local
test/typecheck 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>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - Its UI is governed by a design system (`DESIGN.md` + the token layer
in `ui/src/index.css`, merged in #9134) whose first principle is "one
way to say each thing" — one Card, one Badge, one nav row
> - After the token extraction landed, ~35 files still hand-rolled card
containers, ~45 files hand-rolled pill spans, the sidebar agents section
duplicated the nav-row chrome, and the inbox and tasks lists rendered
the same task rows two subtly different ways
> - Each divergence is a place where a future design change (radius,
hover language, status vocabulary) silently misses surfaces, defeating
the "edit tokens + run checks" model the design system exists for
> - This pull request converges those surfaces onto the shared
primitives, codifies the radius scale as the modern multiplicative
shadcn ladder, and unifies the row/hover/tree-guide language across the
inbox and tasks lists — every visible delta was human-reviewed
screen-by-screen against a live instance across nine feedback rounds
> - The benefit is that the app's look is now steerable from single
knobs (one `--radius` anchor, one Card, one Badge, one row component),
and the visual regression suite covers the result (514 snapshots
including a new AgentDetail page story)
## Linked Issues or Issue Description
No public GitHub issue exists for this work; describing per the feature
template:
- **Problem**: after the design-token foundation (#9134),
component-level drift remained — hand-rolled cards/pills, duplicated
sidebar row chrome, and two different renderings of task rows (inbox vs
tasks list) meant design changes had to be applied per-surface and
frequently missed spots (e.g. status glyphs rendered 16px in the inbox
but 20px in the tasks list because a slot override silently beat the
component default).
- **Proposed behavior**: all card-shaped containers render via `Card`,
all label pills via `Badge`, sidebar rows via `SidebarNavItem`, and both
task-list surfaces via one `IssueRow` configuration; the radius scale is
a single multiplicative ladder anchored at `--radius: 0.5rem`.
- **Alternatives considered**: converting interactive
`<button>`/`<Link>` cards to `Card` divs (rejected — breaks semantics;
documented inline with `design-allow` comments instead); keeping the
legacy additive radius ladder (rejected in favor of the standard shadcn
multiplicative mapping).
## What Changed
- `Card` adoption across ~35 files (settings pages, auth/board flows,
dashboards, list containers, KPI tiles); non-adoptable sites
(interactive cards, `<li>` rows, class-string props, chart tooltip)
carry documented `design-allow(card-pattern)` comments
- `Card` gains an `interactive` prop — one quiet hover affordance for
clickable cards (cursor, border darken, shadow lift, focus ring),
applied to skills tiles, artifact cards, and the company selector; cards
carry no resting shadow
- `Badge` adoption for 113 hand-rolled pill spans across ~45 files;
`PropertyChip` wraps `Badge` internally; `StatusBadge`, external-object
chips, and match chips stay bespoke by documented decision (WCAG-tuned
status mechanics)
- Radius ladder becomes the multiplicative shadcn mapping
(`sm/md/lg/xl/2xl/3xl/4xl = 0.6/0.8/1.0/1.4/1.8/2.2/2.6 × --radius`,
anchor `0.5rem`); every card surface unifies on `rounded-lg`; the
orphaned 8px literal token is deleted
- Sidebar: agent rows render via `SidebarNavItem` (new additive props:
`iconNode`, `active`, `trailing`, `liveAccessory`); live dots use
`--status-agent-running`; one row rhythm and inset rounded pill
highlight; right-aligned trailing badges; every labeled section is
collapsible
- Inbox + tasks lists unified: md status glyphs, `accent/50` rounded row
hovers, vertical tree guides under parent rows (opaque underlay so
dark-mode translucent borders don't stack), no horizontal dividers under
expanded parents; the swipe-to-archive reveal layer shows only
mid-swipe; board toggle uses the `SquareKanban` glyph
- Kanban: every column carries a status-hued tint; lanes default
expanded (including empty); compact mode collapses empty lanes to
labeled rails (fixes a clipped, label-less empty-column state)
- Storybook: new AgentDetail page story (realistic fixtures, light+dark)
joins the visual suite; suite captures with `reducedMotion: 'reduce'`
and the ux-lab reasoning ticker honors `prefers-reduced-motion`; a stale
lexical alias in `storybook/main.ts` is fixed (build was broken since
the lexical 0.46 bump)
- Keyboard navigation, from live review of the unified lists: inbox
navigation keys work on every tab (archive keys stay scoped to the
archivable tab); keyboard-driven scrolling no longer hands the selection
to whatever row lands under the stationary cursor (hover selects only
after real pointer movement); the tasks list view gains the same j/k /
arrows / Enter selection model as the inbox; and the `g` then `i`
go-to-inbox chord works app-wide instead of only on the issue detail
page
- Token gates restored to 3/3 CLEAN (tokenized a post-#9134 regression
in the recovery card); decisions recorded in
`doc/design/DECISION-SHEET.md` and `doc/design/COMPONENT-INVENTORY.md`
(investigation verdicts: FileTree vs WorkspaceFileBrowser and the four
entity pickers stay separate — evidence included)
## Verification
- `pnpm check:token-gates` → 3/3 CLEAN
- `pnpm typecheck` → green (all packages)
- `cd ui && npx vitest run` → 2106/2106 (assertions updated in lockstep
where they documented superseded decisions; new tests for the global
go-to-inbox chord)
- `pnpm --filter @paperclipai/ui build` → exit 0
- `pnpm build-storybook` → succeeds (also fixes the lexical-alias break
on master)
- Visual regression: 514-snapshot Playwright suite green against the
updated baseline (zero diffs from the keyboard-navigation round — those
changes are purely behavioral). Note: baselines live outside git per the
suite design and the baseline-manifest archive is not yet published, so
CI cannot run this suite — it was run locally throughout; every visible
delta was reviewed screen-by-screen in a live instance across nine
review rounds. Review evidence (before/after triplets) intentionally
kept out of the repo for size; available on request.
- Manual: exercised dashboard, tasks (list + board), inbox, agents,
skills, costs, settings, and artifact surfaces in light and dark themes
## Risks
- Wide but shallow visual surface: most changes are class-string
substitutions with behavior preserved (props, handlers, roles, test
ids). The riskiest areas — dnd-kit card refs (React 19 ref-as-prop),
inbox swipe-to-archive, and sidebar overlays — are covered by existing
unit tests (all green) and were manually exercised.
- Intentional visual deltas (rounded cards, tinted kanban columns, md
status glyphs, unified hovers) are design decisions recorded in
`doc/design/DECISION-SHEET.md`; each maps to a re-baselined snapshot set
locally.
- The visual suite cannot yet run in CI (unpublished baseline archive —
pre-existing condition from #9134); until that lands, snapshot coverage
is local-only.
## Model Used
- Claude Fable 5 (`claude-fable-5`, Anthropic) via Claude Code — agentic
coding with tool use (file editing, test execution, Playwright
screenshot verification); extended thinking 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
- [ ] All Paperclip CI gates are green (will confirm once CI runs)
- [ ] 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
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Adds company skill fork precheck metadata, fork result/reassignment contracts, selected-agent reassignment during fork creation, and targeted server/shared test coverage.
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - The work timeline helps operators understand when agent and user
activity actually happened across a project.
> - The timeline view needs clearer interaction context so activity is
easier to inspect and reason about.
> - The existing story coverage did not fully exercise the denser
activity states needed to review this UI safely.
> - This pull request expands the work timeline data shape, service
behavior, UI rendering, tests, and Storybook story so the activity
timeline is easier to verify.
> - The benefit is a more inspectable timeline for project activity,
backed by targeted server and UI coverage.
## Linked Issues or Issue Description
No public GitHub issue found, so this PR describes the feature inline
following the feature request template.
**Subsystem affected**
Cross-cutting: `server/`, `packages/shared`, and `ui/`.
**Problem or motivation**
Project operators need a clearer timeline view that shows when work
activity happened, how much agent time is represented inside the
selected window, and enough realistic activity states for safe visual
review. Sparse mock data and unbounded summary calculations make it
harder to trust the timeline when inspecting historical or capped
windows.
**Proposed solution**
Enrich the work timeline activity data returned by the service, render
clearer top-level timeline summary stats, clamp duration calculations to
the returned window, prorate token totals for partially visible spans,
and add Storybook/test coverage with realistic timeline activity data.
**Alternatives considered**
Keeping the existing sparse timeline story was considered, but it would
leave dense activity layouts and selected-window summary behavior
under-reviewed. Counting full span usage for partially visible spans was
also considered, but it makes historical windows report activity outside
the displayed range.
**Roadmap alignment**
Searched `ROADMAP.md` for timeline/activity references and found no
conflicting planned core work.
**Additional context**
This PR does not include migrations and does not commit generated design
screenshots or images.
## What Changed
- Extended shared work timeline activity types and server timeline
service behavior.
- Updated the timeline page and work timeline chart for richer activity
rendering.
- Clamped timeline runtime summary calculations to the returned window
and prorated summary token usage for clipped spans.
- Added and updated targeted server/UI tests for timeline activity
behavior.
- Added Storybook timeline mock coverage and Storybook preview setup
needed by the story.
## Verification
- `git rebase origin/master` completed cleanly after fetching
`paperclipai/paperclip:master`.
- `git diff --check origin/master...HEAD`
- `pnpm exec vitest run
server/src/__tests__/work-timeline-service.test.ts
ui/src/components/timeline/WorkTimelineChart.test.tsx
ui/src/pages/Timeline.test.tsx` — latest run: 3 files passed, 28 tests
passed.
- Greptile review completed at 5/5 with no unresolved Greptile threads
after fixes.
- GitHub checks completed green on the latest head SHA; Storybook visual
regression was skipped by the workflow.
- `pnpm check:token-gates` currently fails locally on existing
`origin/master` violations in `ui/src/components/ActivityCharts.tsx` and
`ui/src/components/IssueRecoveryActionCard.tsx`; this PR does not modify
those files.
## Risks
Low to moderate risk. The change affects the work timeline service
response shape and timeline UI rendering, so regressions would likely
show up as missing/incorrect timeline activity display. Targeted service
and UI tests cover the changed behavior. No migrations are included.
## Model Used
OpenAI Codex running GPT-5 as a tool-enabled coding agent with local
shell and GitHub CLI access. Exact runtime model ID/context-window size
was not exposed by the 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>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - The board sidebar is a high-frequency navigation surface for
companies, projects, and work queues.
> - Starred projects render as child rows under Projects in the expanded
sidebar.
> - The collapsed rail should align every nav icon in the same rail
column.
> - The starred-project child indent was still applied in the collapsed
rail, which pushed the project glyph out of alignment.
> - This pull request keeps the expanded hierarchy indent while removing
it only for the collapsed rail.
> - The benefit is a cleaner collapsed sidebar without changing expanded
sidebar hierarchy.
## Linked Issues or Issue Description
No public GitHub issue exists.
## What happened?
In the collapsed sidebar rail, starred project rows kept the expanded
child indentation. That pushed the project glyph out of alignment with
the rest of the collapsed sidebar icons.
## Expected behavior
Collapsed starred project icons should align with the other sidebar rail
icons while the expanded sidebar should keep the child-row indentation
under Projects.
## Steps to reproduce
1. Open Paperclip with at least one starred project.
2. Collapse the sidebar into rail mode.
3. Compare the starred project glyph position with the other collapsed
sidebar glyphs.
## Paperclip version or commit
Reproduced on the PR base before this branch; fixed on commit
e57d14343d with CI cleanup on commit
12aafe8959.
## Deployment mode
Local dev (`pnpm dev`).
## What Changed
- Applies the starred-project left padding only when the sidebar is not
in rail mode.
- Adds a regression test covering expanded and collapsed starred-project
rendering.
- Makes the heartbeat worktree suppression test cleanup tolerate late
heartbeat run events before deleting heartbeat runs.
## Verification
- Passed: `pnpm exec vitest run
ui/src/components/SidebarStarredProjects.test.tsx`
- Passed: `pnpm exec vitest run
server/src/__tests__/heartbeat-worktree-suppression.test.ts`
- Passed: PR #9215 latest-head GitHub checks on commit
`12aafe8959351826038baf0c1e401fb44913c67c`
- Passed: Greptile 5/5 with no inline comments or unresolved review
threads on commit `12aafe8959351826038baf0c1e401fb44913c67c`
- Known existing baseline failure: `pnpm check:token-gates` reports
violations in `ui/src/components/ActivityCharts.tsx` and
`ui/src/components/IssueRecoveryActionCard.tsx`, which this PR does not
touch.
## Risks
Low risk. The UI change adjusts one conditional class on starred project
links and preserves the expanded sidebar layout. The server test change
is cleanup-only and does not alter production behavior.
## Model Used
OpenAI GPT-5 Codex coding agent with local command execution and
repository editing 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>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - Agents run in heartbeat runs orchestrated by the server; run
lifecycle, retry scheduling, and the dashboard's run-activity metrics
are the subsystems involved
> - A spike in "failed" tasks traced to three causes: server restarts
killing in-flight runs and mislabeling them as failures, deterministic
workspace-validation loops when a worktree's branch diverged, and
provider quota/usage-limit errors being classified as generic transient
failures (putting agents into error state and polluting metrics)
> - Killed-then-recovered runs and quota waits are not product failures,
so both the runtime behavior and the reporting needed to distinguish
them
> - This pull request drains runs gracefully on shutdown with idempotent
restart retries, self-heals workspace branch mismatches, adds a
quota-aware failure class with reset-time retry, separates recovered
restart kills from true failures on the dashboard, and documents restart
hygiene for operators
> - The benefit is fewer spurious failures, automatic recovery instead
of manual repair, and dashboard metrics that reflect real failure rates
## Linked Issues or Issue Description
No public GitHub issue exists; describing the bug inline per the
bug-report template:
**What happened?**
In-flight heartbeat runs are marked `failed` when the server restarts,
even though a retry later succeeds. Worktrees whose checked-out branch
diverges from the issue branch fail workspace validation on every
subsequent run with no recovery path. Provider quota/usage-limit
responses are treated as generic transient upstream errors, putting
agents into an error state and retrying before the quota window resets.
The dashboard counts all of these as true failures, inflating failure
metrics.
**Expected behavior**
Graceful shutdown should interrupt (not fail) running runs and chain
exactly one recovery retry. Workspace validation should repair
recoverable branch mismatches automatically. Quota errors should get
their own error class with the retry scheduled at the provider reset
time and the agent left idle. The dashboard should report recovered
restart kills separately from true failures.
**Steps to reproduce**
1. Start a heartbeat run, then restart the server (SIGTERM) while it is
in flight — the run lands as `failed` with a process-loss error code
even when its retry succeeds
2. Check out an issue whose worktree branch has diverged (e.g. after a
force-moved branch) — every subsequent run fails
`workspace_validation_failed` deterministically
3. Drive an agent into a provider usage-limit window — the run fails as
a generic transient upstream error and the agent enters an error state
instead of idling until the reset time
**Paperclip version or commit**
master (base c07e650cd)
**Deployment mode**
Self-hosted dev plane (Linux, node server + embedded Postgres)
## What Changed
- Graceful shutdown (SIGTERM hook) now marks in-flight runs
`interrupted` instead of `failed` and enqueues an idempotent
process-loss retry (pre-insert existence check on `retryOfRunId`
prevents duplicates; bursts chain exactly one retry per interrupted run)
- Run-liveness classification routes `interrupted` to `needs_followup`
rather than `failed`
- Workspace validation self-heals branch mismatch / missing-branch
states instead of failing deterministically on every run
- New `provider_quota` error class: session/usage-limit responses
schedule the retry at the provider reset time and leave the agent idle
(not errored); fixes a case where a quota-terminated run with subtype
`success` was misclassified as failed; HTTP 529 remains transient
- Dashboard run-activity query separates recovered restart kills from
true failures via a recursive CTE over `retry_of_run_id` (ancestors of a
succeeded retry count as recovered), adds a per-day failed-by-error-code
breakdown, and binds the window start as a timestamptz string
- Activity charts UI: amber "Recovered" segment with legend and per-day
error-code tooltip; success-rate chart counts recovered runs as
successes
- New ops runbook: `docs/deploy/dev-plane-restart-hygiene.md`
## Verification
- Greptile follow-up fixes on `992705edd`: `pnpm exec vitest run
server/src/__tests__/server-startup-feedback-export.test.ts
server/src/__tests__/heartbeat-workspace-branch-containment.test.ts
server/src/__tests__/heartbeat-workspace-session.test.ts
server/src/__tests__/heartbeat-process-recovery.test.ts
server/src/__tests__/heartbeat-workspace-finalize-branch.test.ts` (209
tests); `pnpm --filter @paperclipai/server typecheck`; `git diff
--check`
- Post-rebase CI fixes: `pnpm exec vitest run
server/src/__tests__/heartbeat-retry-scheduling.test.ts`; `pnpm exec
vitest run server/src/__tests__/heartbeat-retry-scheduling.test.ts
server/src/__tests__/workspace-runtime.test.ts
server/src/__tests__/heartbeat-workspace-branch-containment.test.ts
server/src/__tests__/heartbeat-workspace-finalize-branch.test.ts
server/src/__tests__/heartbeat-workspace-session.test.ts` (227 tests);
`pnpm --filter @paperclipai/server typecheck`
- `npm test` server suites covering the changes:
`heartbeat-process-recovery`, `heartbeat-stop-metadata`,
`heartbeat-retry-scheduling` (95 tests), quota parse +
execute/retry-scheduling suites (100 tests), workspace self-heal suites
(200 tests), dashboard run-activity tests (3 tests) — all green,
typecheck exit 0
- Dashboard CTE cross-checked against a real development database: two
restart-burst days moved from 17 to 8 and 19 to 6 true failures once
recovered kills were separated, matching manual retry-chain inspection
- Screenshot verification of the real ActivityCharts component
(recovered segment + tooltip) during QA
## Risks
- Behavioral shift: runs killed by a restart no longer surface as
`failed`; anyone consuming raw run statuses will see `interrupted` (new
status value) — dashboards/queries in this repo were updated accordingly
- Retry chaining on repeated restarts is bounded (one chained retry per
interruption) but a pathological restart loop still delays work rather
than failing it; the runbook covers operator hygiene for that case
- Dashboard query adds a recursive CTE; cost is bounded by the
day-window row count and was verified against production-sized data
- No schema migrations; low migration risk
## Model Used
- Claude (Anthropic) — claude-fable-5 via Claude Code / Paperclip agent
harness, extended thinking with tool use; implementation commits also
produced with Codex CLI (GPT-5 class) agents under the same 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)
- [ ] 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>
Adds the task-page recovery affordance for workspace branch divergence: reconcile-forward when ancestry is safe, and a break-glass override flow that requires explicit confirmation and a reason.
Includes client wiring for the existing reconcile-branch endpoint, issue-detail refresh after successful reconciliation, runtime-management gating for break-glass, and tests for action visibility, payloads, permission gating, and workspace-target selection.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - The board sidebar is the primary navigation surface for operators
scanning companies, projects, tasks, agents, and related control-plane
tools.
> - Goals still has a route and product surface, but keeping the
top-level sidebar link always visible makes it part of the default
navigation whether or not that surface is ready for every operator.
> - Instance experimental settings already provide a controlled place to
expose optional UI surfaces while they are being evaluated.
> - This pull request adds a dedicated experimental setting for
restoring the Goals sidebar link.
> - The benefit is a quieter default sidebar with an explicit escape
hatch for operators who still need the Goals entry point.
## Linked Issues or Issue Description
No public GitHub issue exists for this internal task, so the feature
request is described inline.
**Subsystem affected**
Cross-cutting: `ui/`, `server/`, and `packages/shared`.
**Problem or motivation**
The Goals route remains available, but the top-level Goals sidebar entry
makes that surface part of the default operator navigation. While the
goals surface is still being evaluated, operators need a quieter default
sidebar without losing an escape hatch for teams that still rely on the
link.
**Proposed solution**
Add a boolean instance experimental setting, `enableGoalsSidebarLink`,
default it to `false`, and render the Goals sidebar link only when the
setting is enabled. Expose the toggle in Instance Experimental Settings
so operators can restore the link without changing routes or rebuilding
the app.
**Alternatives considered**
- Remove the Goals route entirely: rejected because this task only asks
to hide the sidebar entry point and preserve access for teams evaluating
goals.
- Keep the sidebar link always visible: rejected because it does not
provide the requested quieter default navigation.
- Hard-code a local UI flag: rejected because instance experimental
settings already provide the expected operator-controlled pattern.
**Roadmap alignment**
Checked `ROADMAP.md`; no overlapping goals/sidebar/experimental roadmap
entry was found.
**Additional context**
The `/goals` route is preserved. This PR only gates the sidebar
navigation item.
## What Changed
- Added `enableGoalsSidebarLink` to the shared instance experimental
settings type and validator, defaulting to `false`.
- Normalized the new setting in the server instance settings service.
- Hid the Goals sidebar nav item unless the new setting is enabled.
- Added a Goals Sidebar Link toggle to the Instance Experimental
Settings page.
- Updated shared, server, sidebar, and settings page tests for the new
setting.
## Verification
- `pnpm exec vitest run packages/shared/src/validators/instance.test.ts
server/src/__tests__/instance-settings-service.test.ts
server/src/__tests__/instance-settings-routes.test.ts
ui/src/components/Sidebar.test.tsx
ui/src/pages/InstanceExperimentalSettings.test.tsx`
- `git diff --check origin/master...HEAD`
- `git merge-tree --write-tree HEAD origin/master`
- Searched for duplicate/related PRs by title and
`enableGoalsSidebarLink`; none found.
- Checked `ROADMAP.md` for overlapping goals/sidebar/experimental
entries; none found.
## Risks
Low risk. The main behavior shift is that operators who depended on the
sidebar Goals link need to enable the new experimental toggle. The
`/goals` route itself is not removed.
> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.
## Model Used
OpenAI Codex, GPT-5-based Paperclip CodexCoder session with repository
tool access and command execution. Exact API model identifier and
context window were not exposed by the Paperclip 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
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - Its UI is the operator's daily surface: task lists, boards, budgets,
agent status — all built on shadcn components and Tailwind
> - Visual values (colors, spacing, type sizes, radii) were hardcoded at
~1,600 call sites: the same "small gray label" was 9/10/11px depending
on the file, charts disagreed with chips about status colors, two
toggle-switch implementations coexisted in two greens, and there was no
visual regression coverage
> - This made the UI drift-prone and made any restyle a
hundreds-of-files project, which discourages design iteration
> - This pull request extracts visual values into a single token layer
in `ui/src/index.css`, adds a Storybook visual regression suite backed
by external immutable baseline archives, and then applies a deliberate
retune reviewed change-by-change on screenshot diffs
> - The benefit is that Paperclip's look becomes a config surface:
retheming is a token edit reviewed as a snapshot diff, drift is blocked
by a token gate, and future UI PRs can prove exactly what changed
visually without committing hundreds of PNGs
## Linked Issues or Issue Description
No existing public issue covers this work (searched "design tokens",
"visual regression", "design system" across issues and PRs). Related in
spirit: Refs #8982 (theming a hardcoded panel — a one-off instance of
the same problem class this PR addresses systematically).
**Problem (feature-request form):** UI visual values are hardcoded per
call site with no source of truth and no regression coverage;
consistency depends on reviewer memory, and restyling requires mass file
edits.
**Proposed solution (this PR):** a single token layer + enforcement gate
+ externally stored visual snapshot suite, then an intentional restyle
on top of that foundation.
## What Changed
- **Token extraction (zero visual change, machine-verified during
development):** committed codemods (`scripts/codemod-*.mjs`) moved
~1,600 hardcoded color/type/spacing/radius/shadow/misc values into named
tokens in a non-inline `:root` block of `ui/src/index.css`.
- **Visual regression suite:** `pnpm test:storybook-visual` covers 255
stories × light/dark = 510 Playwright screenshots at `maxDiffPixels: 0`,
plus new primitive-coverage stories and deterministic-render fixes.
- **External visual baselines:** committed PNG snapshots were removed.
`tests/storybook-visual/baseline-manifest.json` pins an immutable
archive URL/hash/size/count, and `scripts/storybook-visual-baseline.mjs`
handles `download`, `verify`, `pack`, and trusted maintainer `upload`
flows.
- **Opt-in visual CI artifacts:** added a `Storybook Visual` workflow
that runs on manual dispatch or PRs labeled `storybook-visual`,
downloads/verifies the baseline, runs Playwright, and uploads Playwright
report/test-result artifacts for review. Normal PR runs do not mutate
baseline objects.
- **Token gate:** `pnpm check:token-gates` — zero hex literals, zero
arbitrary bracket values, zero raw font-sizes in `ui/src/components/**`
and `ui/src/pages/**`, with a documented inline allowlist for legitimate
opt-outs.
- **Theme retune (intentional, snapshot-reviewed):** new base theme
values; radius ladder derived from a single `--radius` knob; micro-type
cluster collapsed to a named ladder (`--text-nano/micro/compact` +
Tailwind `text-xs`/`text-sm`); letter-spacing collapsed to named steps.
- **One status-color vocabulary:** charts, quota/budget bar fills,
RUNNING/live chips, and liveness indicators all use the canonical
`--status-*` hues. Light-mode legibility fixes for red alert surfaces
that used dark-tuned text classes.
- **One switch:** `ToggleSwitch` restyled to the registry capsule form,
second hand-rolled implementation removed, and all call sites unified.
- **Docs:** `DESIGN.md` is the design contract; `doc/design/` holds
audit reports, decision logs, and updated guidance for external baseline
review/update workflows.
- Dead code removed (`agentStatusBadge` duplicate map), byte-identical
contrast constants consolidated, semantic renames
(`--project-seed`/`--project-none`, `--liveness-blue`).
## Verification
- `pnpm check:token-gates` — 3/3 gates CLEAN during the design-system
run
- `pnpm typecheck` && `pnpm --filter @paperclipai/ui build` — green
during the design-system run
- `node --test scripts/__tests__/storybook-visual-baseline.test.mjs` —
pass after external-baseline rework
- `pnpm exec tsc --noEmit --pretty false --module NodeNext
--moduleResolution NodeNext --target ES2022 --types
node,@playwright/test tests/storybook-visual/playwright.config.ts
tests/storybook-visual/storybook-visual.spec.ts` — pass after
external-baseline rework
- `git diff --check origin/pr/9134..HEAD` — pass after external-baseline
rework
- `find tests/storybook-visual -type f -name '*.png' -print | wc -l` —
`0`
- `node scripts/storybook-visual-baseline.mjs verify` — intentionally
fails closed until the first trusted maintainer publishes the baseline
archive and updates `baseline-manifest.json`
## Risks
- **Large but shallow:** the PR still touches many UI files due to
mechanical token extraction and retune work, but committed PNG snapshot
churn has been removed from the branch.
- **Baseline publication required before the visual suite can pass in
clean clones:** the manifest currently has placeholder archive metadata.
A trusted maintainer must publish the first immutable archive, then
update `baseline-manifest.json`.
- **Rendering platform variance:** the external baseline should be
captured in the documented Linux/Chromium environment. Future CI runs
verify against the pinned archive and fail closed on checksum/count
mismatch.
- **Visual CI is opt-in while stabilizing:** add the `storybook-visual`
label or dispatch the workflow manually to produce downloadable
Playwright report/test-result artifacts.
- **Scheduled follow-ups, deliberately out of scope:** Tailwind palette
classes map to semantic tokens in a dedicated pass; card/pill component
consolidation; ESLint ratchet. Tracked in
`doc/design/DECISION-SHEET.md`.
## Model Used
Claude Fable 5 (Anthropic, `claude-fable-5`, Mythos-class tier) with
extended thinking, running in Claude Code with tool use; mechanical
phases delegated to Claude Sonnet subagents. Follow-up external-baseline
rework assisted by OpenAI Codex (`gpt-5` coding agent with repository,
terminal, and GitHub tool use). All bulk rewrites executed via
deterministic, idempotent scripts committed in `scripts/`; intentional
visual changes were human-reviewed on screenshot contact sheets.
## 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 targeted local verification and documented the
intentional baseline-publication failure above
- [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 *(pending new CI run after this
rework)*
- [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
*(pending review)*
- [x] I will address all Greptile and reviewer comments before
requesting merge
🤖 Generated with [Claude Code](https://claude.com/claude-code) and
OpenAI Codex
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Dotta <bippadotta@protonmail.com>
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - The issue and routine editor surfaces share document annotation
behavior for commentable text.
> - The annotation layer currently observes document selections inside
its container and converts those selections into pending comment
anchors.
> - Routine editor fields can live inside that same annotated document
container while still needing normal native text selection behavior.
> - When a user selects text inside an editable routine field, the
annotation layer should leave that selection alone instead of preparing
a comment.
> - This pull request teaches the annotation layer to ignore selections
touching editable controls and covers that case with focused component
tests.
> - The benefit is that routine editing remains editable text UX, while
document annotation selection continues to work for normal read-only
document text.
## Linked Issues or Issue Description
No public GitHub issue exists for this bug. Inline bug report follows.
### Pre-submission checklist
- [x] I have searched existing open and closed issues and this is not a
duplicate.
- [x] I am on current `master` for this PR branch.
- [x] I have confirmed the error originates in Paperclip itself, not in
an agent adapter, API provider, or local configuration.
### What happened?
Selecting text inside an editable routine field could be interpreted as
a document annotation selection. That meant the annotation layer could
prepare a pending comment anchor or show annotation affordances while
the user was just selecting text to edit routine content.
### Expected behavior
Editable controls should keep native text selection behavior. Selecting
text inside `input`, `textarea`, `select`, or contenteditable routine
editor regions should not build a document annotation anchor or show the
document annotation toolbar.
### Steps to reproduce
1. Render document annotation behavior around a document/editor
container that also contains an editable routine text region.
2. Select text inside the editable region.
3. Observe that the document annotation layer treats the selection as
commentable text instead of ignoring it.
### Paperclip version or commit
Reproduced and fixed against `master` at `5356b7d73`, with PR head
`4f53f83f1`.
### Deployment mode
Local dev / component test environment.
### Installation method
Built from source.
### Agent adapter(s) involved
Not adapter-specific; core UI behavior.
### Database mode
Not database-related.
### Access context
Board / human operator UI behavior.
### Relevant logs or output
No runtime logs. Regression coverage is in
`ui/src/components/DocumentAnnotationLayer.test.tsx`.
### Relevant config
Not applicable.
### Additional context
Root cause: `DocumentAnnotationLayer` filtered selections by container
membership, but did not exclude editable controls or all valid
contenteditable hosts before building a pending annotation anchor.
### Privacy checklist
- [x] I have reviewed all pasted output for PII and redacted where
necessary.
## What Changed
- Added an editable-selection guard in `DocumentAnnotationLayer` that
ignores selections touching `input`, `textarea`, `select`, or
contenteditable elements.
- Covered both `contenteditable="true"` and bare/empty `contenteditable`
hosts so routine editor selections do not build annotation anchors.
- Added focused regression tests proving editable selections do not call
the annotation anchor helpers or show the annotation toolbar.
## Verification
- `pnpm vitest run ui/src/components/DocumentAnnotationLayer.test.tsx`
- `git diff --check`
- GitHub PR checks are green for head `4f53f83f1`, including policy,
review, typecheck/release registry, build, general tests, serialized
suites, e2e, canary dry run, security checks, and Greptile.
## Risks
Low risk. The change only narrows annotation capture when a selection
touches an editable element inside the annotation container. Normal
read-only document annotation selections still use the existing
anchor-building path.
> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.
## Model Used
OpenAI Codex coding agent based on GPT-5, with shell, Git, Vitest, and
GitHub connector/CLI tool use. The exact hosted runtime model identifier
is not exposed in this session.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [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>
## Thinking Path
> - Paperclip orchestrates AI agents for zero-human companies
> - The issue chat thread renders comments from agents, users, and the
system itself
> - When Paperclip's internal recovery system posts a comment (e.g.,
stranded issue recovery), it creates a comment with `author_type:
"system"`, no agent ID, and no user ID
> - The UI function `authorNameForComment` falls back to `"You"` when
both author IDs are null
> - This PR returns `"Paperclip"` instead for system notices
> - The benefit: system comments are clearly attributed to Paperclip,
not the logged-in viewer
## What Changed
- `ui/src/lib/issue-chat-messages.ts`: In `authorNameForComment`, the
null-userId fallback now checks `options?.isSystemNotice` and returns
`"Paperclip"` instead of `"You"` for system-authored comments. One-line
change.
## Verification
- 82/82 existing tests pass (issue-chat-messages 20,
IssueChatThreadSystemNotice 11, IssueChatThread 51)
- Manual repro: view any issue with a system-authored comment (stranded
issue recovery moves issue to blocked). Before: author "You". After:
author "Paperclip".
## Risks
Low risk. One-line change. Only affects system-authored comments with no
user/agent ID. The `isSystemNotice` flag is already computed upstream
(`comment.authorType === "system"`) and passed into this function.
## Model Used
zai/glm-5.1 (200K context). Investigation and code tracing assisted by
the model.
## Checklist
- [x] Thinking path included
- [x] Model specified
- [x] Checked ROADMAP.md — bug fix, not a feature
- [x] Tests pass locally (82/82)
- [x] Risks documented
- [x] Will address all reviewer comments
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - Environment settings let board users configure sandbox execution
targets for agent runs
> - Sandbox custom-image setup is company-scoped because the backend
resolves provider secrets and access through company context
> - The browser UI already knows the selected company, but the
custom-image setup calls were not passing it to routes whose OpenAPI
contract includes `companyId`
> - In multi-company authenticated deployments, the backend cannot
safely infer company context and returns a `companyId query parameter is
required` error
> - This pull request passes the selected company id through the
custom-image overview, setup, rollback, and disable UI paths
> - The benefit is that custom-image setup works consistently in
multi-company instances while preserving backend company-boundary checks
## Linked Issues or Issue Description
- No public issue is filed for this regression.
- Related prior work: #8911
- Bug summary: after the browser SSH terminal custom-image setup flow
shipped, opening custom-image setup in an authenticated multi-company
instance could show `companyId query parameter is required for
environment customImage setup` instead of starting the setup session.
- Expected behavior: the environment settings UI should send the
selected company context to company-scoped custom-image endpoints.
- Reproduction shape: run Paperclip in authenticated/private mode with
more than one company, open a sandbox environment's edit dialog, and use
the custom-image setup controls.
## What Changed
- Added company-id query construction for custom-image overview, setup,
rollback, and disable calls in the UI environment API wrapper.
- Passed the selected company id into the custom-image panel used by the
environment edit dialog.
- Updated the environment page tests so the regression fails if company
context is dropped again.
## Verification
- `pnpm exec vitest run ui/src/pages/CompanyEnvironments.test.tsx`
- `pnpm --filter @paperclipai/ui typecheck`
- `git diff --check`
- Also applied the same patch to a local dev checkout serving port 3100
and confirmed `/api/health` still returns `ok` after the dev watcher
reload.
## Risks
- Low risk: this only adds the selected company id to UI calls for
endpoints that already declare or require company context.
- If the selected company id is stale or invalid, the existing
server-side company access checks still reject the request.
## Model Used
- OpenAI Codex CLI using GPT-5, with tool-enabled repository inspection,
editing, shell command execution, and local test execution. 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>
Bumps
[@radix-ui/react-slot](https://github.com/radix-ui/primitives/tree/HEAD/packages/react/slot)
from 1.2.4 to 1.3.0.
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/radix-ui/primitives/blob/main/packages/react/slot/CHANGELOG.md">@radix-ui/react-slot's
changelog</a>.</em></p>
<blockquote>
<h2>1.3.0</h2>
<h3>Added generic type arguments for <code>SlotProps</code> and
<code>createSlot</code></h3>
<p><code>SlotProps</code> and <code>createSlot</code> now accept generic
type arguments to specify the type of element a slot should render, as
well as its props.</p>
<pre lang="tsx"><code>const Slot = createSlot<HTMLButtonElement,
MyCustomButtonProps>('Slot');
</code></pre>
<h2>1.2.5</h2>
<ul>
<li>Fixed infinite re-render loop in React 19 caused by
<code>Slot</code> creating a new ref callback on every render</li>
<li>Added support for nested <code>Slottable</code> via a render prop,
so a slotted element can be wrapped while still merging Slot props and
refs onto it</li>
<li>Added repository.directory to all package.json files</li>
<li>Improved error messages for invalid slot children</li>
<li>Updated dependencies:
<code>@radix-ui/react-compose-refs@1.1.3</code></li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li>See full diff in <a
href="https://github.com/radix-ui/primitives/commits/HEAD/packages/react/slot">compare
view</a></li>
</ul>
</details>
<details>
<summary>Maintainer changes</summary>
<p>This version was pushed to npm by <a
href="https://www.npmjs.com/~GitHub%20Actions">GitHub Actions</a>, a new
releaser for <code>@radix-ui/react-slot</code> since your current
version.</p>
</details>
<br />
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
<details>
<summary>Dependabot commands and options</summary>
<br />
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
</details>
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - The work timeline UI visualizes agent and human activity across
kickoff chips and the Gantt chart.
> - The timeline actor chips were falling back to generic initials or
blank circular avatars instead of using the richer identity data already
available elsewhere in the app.
> - Agent rows should match the sidebar identity treatment, and human
entries should use the user's configured avatar image when one exists.
> - This pull request teaches the timeline avatar renderer to prefer
configured agent icons and human avatar URLs, while preserving initials
as a fallback.
> - The benefit is a more recognizable work timeline that matches the
rest of the Paperclip UI.
## Linked Issues or Issue Description
No public GitHub issue exists for this UI bug. The underlying issue:
work timeline actor avatars did not consistently use the available actor
identity assets. Agents appeared as generic white-circle initials
instead of their configured sidebar icons, and human kickoff entries
appeared as initials even when the user had an avatar image.
Related prior timeline work: #8875 and #8880. No open duplicate PR was
found for this avatar correction.
## What Changed
- Updated the work timeline actor avatar renderer to show configured
agent icons for agent rows and chips.
- Updated human kickoff avatar rendering to prefer the user avatar image
and fall back to initials only when no image is available.
- Added regression coverage for agent sidebar-style icons and human
avatar images in `WorkTimelineChart`.
## Verification
- `pnpm exec vitest run
ui/src/components/timeline/WorkTimelineChart.test.tsx`
- `pnpm --filter @paperclipai/ui typecheck`
- `git diff --check origin/master...HEAD`
## Risks
Low risk. The change is scoped to work timeline avatar presentation and
preserves the existing initials fallback when configured icons or avatar
images are unavailable.
> 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-class coding agent in local tool-use mode with
shell, git, test execution, and GitHub connector access.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have 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
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - The board inbox is a high-frequency operator surface, especially on
mobile where row gestures are the primary way to dismiss handled work.
> - The existing swipe archive control shared one pending-state gate
across the inbox, so one archive request could make other inbox archive
controls feel stuck.
> - The touch handler also kept click suppression and archive timers
coupled too loosely, which made cancelled or partial iOS touch sequences
behave unpredictably.
> - This pull request keeps archive state per issue, makes swipe/cancel
cleanup explicit, and removes archived issues from every relevant inbox
query cache before navigation/refetch.
> - The benefit is that iOS users can archive from inbox/detail views
without the inbox getting into a weird pending or stale-cache state.
## Linked Issues or Issue Description
Bug report:
- Summary: On iOS/mobile, inbox swipe/archive interactions can leave the
inbox feeling stuck or stale after archiving a task.
- Expected: Archiving one inbox item should remove that item
optimistically, keep other archive controls usable, and recover cleanly
from partial or cancelled touch gestures.
- Actual: A pending archive globally disabled other archive controls,
touch cancellation could share the touch-end path, and issue-detail
archive navigation could happen before related inbox query caches were
cleaned up.
- Root cause: Archive pending state and inbox cache updates were handled
locally in multiple places instead of through reusable per-company cache
helpers, and the swipe component did not independently track live
offset, archive timers, and click-suppression timers.
## What Changed
- Split `SwipeToArchive` archive timeout handling from temporary click
suppression and added explicit touch-cancel/reset cleanup.
- Track swipe offset in a ref so commit threshold checks use the latest
touch position rather than potentially stale React state.
- Added shared inbox archive cache helpers for cancelling, snapshotting,
optimistic removal, rollback, and invalidation across inbox query
variants.
- Updated inbox archive mutations to disable only the row being archived
instead of every row while any archive request is pending.
- Updated issue-detail archive-from-inbox to remove the archived issue
from cached inbox variants before navigating back.
- Added focused tests for partial drags, cancelled touches, unmount
cleanup, per-row pending archive behavior, and detail-page cache removal
before navigation.
## Verification
- `pnpm exec vitest run ui/src/components/SwipeToArchive.test.tsx
ui/src/pages/Inbox.test.tsx ui/src/pages/IssueDetail.test.tsx`
- 3 test files passed
- 53 tests passed
## Risks
Low-to-medium risk. This changes mobile archive gesture state and
optimistic inbox cache handling, so the main risk is cache invalidation
missing an inbox query variant. The helper uses prefix-based React Query
APIs for the known inbox variants and still invalidates those variants
plus sidebar badges on settle.
> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.
## Model Used
OpenAI Codex, GPT-5 based coding agent, with repository tool use and
local command 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
Related PR search performed with `gh search prs "inbox archive iOS
swipe" --repo paperclipai/paperclip` and `gh search prs "SwipeToArchive"
--repo paperclipai/paperclip`; existing related work includes #1857 and
#1860, but no duplicate of this regression fix was found.
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - Agents execute on isolated workspaces (git worktrees), each pinned
to a specific branch and commit at checkout time
> - When an agent's live checkout diverges from the recorded workspace
branch — either through a branch rename, a stale worktree, or a
concurrent git operation — Paperclip detects the mismatch and surfaces a
recovery card to the operator
> - But the existing recovery card showed a generic error with no
diagnostic context: it didn't display *which* branch was expected vs.
which was checked out, the commit SHAs involved, or whether the branches
share ancestry
> - Without that information operators cannot diagnose the root cause,
and the only recovery path was fully manual re-issue
> - This pull request extends `IssueRecoveryActionCard` for
`workspace_validation` / `git_worktree_branch_incoherence` recovery
kinds to render a divergence-diagnosis panel (expected branch, live
branch, short SHAs, ancestry-verdict badge, plain-language reason) and
adds a confirm-gated "Re-issue on isolated workspace" action that
creates a new task with `executionWorkspacePreference:
isolated_workspace` so the re-issued run cannot trip the same
branch-mismatch gate
> - The benefit is that operators can immediately see *why* a workspace
was declined and recover with a single click instead of having to
manually reconstruct the task
## Linked Issues or Issue Description
Refs #4757 (heartbeat re-wake doesn't reconcile working-tree HEAD
against ticket's expected branch — this PR surfaces the resulting
divergence to the operator and provides a one-click isolated re-issue
path)
Refs #8460 (workspace_validation_failed local-only project workspaces —
this PR extends the recovery card UI for this case)
**Subsystem affected:** ui/ — React + Vite board UI
**Problem or motivation:** When Paperclip records a workspace branch for
an agent run and the live checkout disagrees (diverged HEAD, renamed
branch, stale worktree), the issue recovery card surfaces a generic
`workspace_validation` error. The operator sees "run declined" but has
no visibility into the expected vs. actual branch, the relevant commit
SHAs, or whether the branches even share ancestry. There is no one-click
path to re-issue the task on a clean isolated workspace — the operator
must manually reconstruct the task from scratch.
**Proposed solution:** Extend `IssueRecoveryActionCard` to:
1. Render a divergence-diagnosis panel from the `recoveryEvidence`
field: expected branch, live branch, short SHAs for both, an
ancestry-verdict badge (`forward-only` / `diverged` / `ancestry
unknown`), and the server's `plainLanguageReason`.
2. Add Action 3 "Re-issue on isolated workspace" — a confirm-gated
button that calls `issuesApi.create` with `executionWorkspacePreference:
isolated_workspace` and `workspaceStrategy.baseRef` set to the live
branch (SHA fallback when detached). The current workspace is never
mutated.
3. Wire `onReissueIsolated` / `reissuePending` through `IssueChatThread`
→ `IssueDetail` so the operator sees an immediate success toast and is
navigated to the new task.
**Alternatives considered:** Showing divergence details only in a
tooltip (rejected — too easy to miss). Providing a "force-reset the
workspace" action (rejected — destructive, no audit trail, doesn't fix
stale-branch root cause). Isolated re-issue via isolated workspace was
the clearest safe path.
## What Changed
- `IssueRecoveryActionCard.tsx` — Added `DiagnosisPanel` sub-component
rendered for `workspace_validation` / `git_worktree_branch_incoherence`
recovery kinds: displays expected vs. live branch, short SHAs,
ancestry-verdict badge, and plain-language reason. Added Action 3
confirm-popover with `onReissueIsolated` callback and `reissuePending`
loading state. Kept existing Action 1 and Action 2 unchanged.
- `IssueChatThread.tsx` — Threaded `onReissueIsolated` and
`reissuePending` props down to `IssueRecoveryActionCard`.
- `IssueDetail.tsx` — Implemented `handleReissueIsolated`: calls
`issuesApi.create` with `executionWorkspacePreference:
isolated_workspace` + `workspaceStrategy.baseRef` derived from live
branch / SHA; shows a success toast and navigates to the new task on
completion.
- `IssueRecoveryActionCard.test.tsx` — Added 19 unit tests covering
diagnosis-panel rendering, verdict label rendering, base-ref derivation
(branch-first then detached-HEAD SHA fallback), and action gating.
## Verification
```bash
# Unit tests — 19/19 pass
pnpm vitest run ui/src/components/IssueRecoveryActionCard.test.tsx
# Typecheck — 0 new errors
pnpm typecheck
```
Manual browser validation deferred to QA — see Risks.
## Risks
- **Re-issue creates a new task** — the original task remains unchanged.
This is intentional (safe default), but operators should be aware both
tasks exist after re-issue.
- **Base-ref derivation falls back to the live HEAD SHA when detached.**
SHA-based worktrees are valid for isolated re-issue but may surprise
operators expecting a branch name.
- **Browser-level end-to-end validation not included here.** Toast,
navigation, and full create-flow are covered by integration QA in a
follow-up pass.
- **Low overall risk** — no new endpoints, no data mutations on existing
records, no PII or telemetry changes. Composes the existing
`issuesApi.create` endpoint; all new behavior is additive.
## Model Used
Claude Sonnet 4.6 (`claude-sonnet-4-6`) — Anthropic. 200k context
window, tool use, code execution. Extended thinking not used.
## 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
- [ ] 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
- [ ] 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>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - Agents run in git worktrees tied to a workspace branch; when the
actual branch diverges from the expected one (e.g. a parent feature
branch was renamed), Paperclip currently has no structured field to
report *why* the branch is incoherent or whether it can be
auto-reconciled
> - The workspace-incoherence fingerprint already captures SHA
mismatches, but there is no evidence field distinguishing "actual branch
is a descendant of expected" (safe to fast-forward) from "branches have
diverged" (needs human review) or "SHAs are unavailable" (unknown)
> - Operators and future recovery flows need a typed verdict to make
decisions without re-running git commands themselves
> - This pull request adds `ancestryVerdict` and `plainLanguageReason`
evidence fields computed via `git merge-base --is-ancestor`, and
scaffolds the off-by-default `enableWorkspaceBranchReconcileForward`
instance setting with no runtime behavior yet
> - The benefit is that future recovery logic can branch on a typed
verdict rather than parsing prose, while the fingerprint v1 payload
stays stable
## Linked Issues or Issue Description
No public GitHub issue pre-exists for this diagnostic addition.
**Problem or motivation**
When Paperclip detects that an agent's actual workspace branch differs
from the recorded expected branch, the current fingerprint carries only
raw SHAs. There is no typed field indicating whether the actual branch
is a descendant of the expected one (safe reconcile path) vs. a true
divergence (requires human intervention) vs. an indeterminate state
(missing SHAs or git errors). Downstream recovery logic cannot branch
safely without re-running git.
**Proposed solution**
Add `ancestryVerdict` and `plainLanguageReason` to the workspace
incoherence evidence type; compute via `git merge-base --is-ancestor`;
scaffold a feature-flag for future forward-reconcile behavior
(`enableWorkspaceBranchReconcileForward`, off by default, not yet read
by any runtime path).
**Alternatives considered**
Encoding the verdict in the existing fingerprint string was rejected
because the fingerprint is a stable identity hash, not a mutable
evidence bag. Changing it would break monitors keyed on the string.
**Roadmap alignment**
Supports future workspace auto-reconcile work; ROADMAP.md has no
conflicting entry for this diagnostic layer.
## What Changed
- `packages/shared/src/types/heartbeat.ts` adds `ancestryVerdict` and
`plainLanguageReason` fields to `WorkspaceIncoherenceEvidence`
- `packages/shared/src/types/instance.ts` adds
`enableWorkspaceBranchReconcileForward` boolean (off by default)
- `packages/shared/src/validators/instance.ts` exports the new flag from
the settings validator
- `server/src/services/workspace-runtime.ts` computes `ancestryVerdict`
via `git merge-base --is-ancestor`; falls back to `unknown` on missing
SHAs or command errors; excludes verdict fields from fingerprint v1
computation
- `server/src/services/instance-settings.ts` wires the new setting
through to the settings service
- Tests updated in `workspace-runtime.test.ts`,
`instance-settings-service.test.ts`, `instance-settings-routes.test.ts`,
and `instance.test.ts` (104 tests total)
## Verification
```bash
pnpm exec vitest run \
server/src/__tests__/workspace-runtime.test.ts \
server/src/__tests__/instance-settings-service.test.ts \
server/src/__tests__/instance-settings-routes.test.ts \
packages/shared/src/validators/instance.test.ts
# 104 tests pass
pnpm --filter @paperclipai/shared typecheck
pnpm --filter @paperclipai/server typecheck
# both exit 0
```
Manual: trigger a workspace incoherence event and confirm the evidence
object carries `ancestryVerdict` and `plainLanguageReason`; confirm the
fingerprint string stays `workspace_incoherence:v1:sha256:...`.
## Risks
**Low risk.** Purely additive. Fingerprint v1 payload is unchanged. The
new flag has no runtime effect in this PR. `git merge-base
--is-ancestor` exits non-zero for both "not an ancestor" and "command
error"; both are handled and collapsed to typed values with a prose
reason.
## Model Used
Provider: Anthropic, model: Claude Sonnet 4.6 (`claude-sonnet-4-6`),
200k context, tool use 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
- [ ] 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
- [ ] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
## Problem
On the Agents page, there are two small icon buttons for switching
between list view and org chart view. But these buttons had:
- No title attribute, so hovering show nothing
- No aria-label, so screen readers just say "button"
- No aria-pressed, so no way to know which view is active
- No role="group" on the container
I was confused the first time I see these buttons because one is a list
icon and the other is a git-branch icon, and without hovering or
clicking I had no idea what they do. Specially the git-branch icon - is
it for branches? Repos? No, it's for org chart view.
## What I changed
- Added `title` to each button: "List view" and "Org chart view"
- Added `aria-label` matching the title for screen readers
- Added `aria-pressed` to indicate which view is currently selected
- Added `role="group"` with `aria-label="View mode"` on the container
div
## How to test
1. Go to Agents page (must have more than 1 agent for toggle to appear)
2. Hover over the list icon - should see "List view" tooltip
3. Hover over the branch icon - should see "Org chart view" tooltip
4. Use screen reader - should announce "List view, pressed" or "Org
chart view, not pressed"
1 file, 7 lines added.
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - The board UI is where operators create issues, inspect issue-thread
decisions, write markdown, and move between issue workflow surfaces.
> - A broad recovered branch mixed these operator workflow polish fixes
with unrelated backend, pipeline, plugin, eval, and work-product
changes.
> - Reviewers need the operator UI workflow fixes in a small PR that can
be understood without pulling in the rest of that recovery branch.
> - This pull request extracts only the issue workflow UI slice:
interaction cards, markdown editing fallback behavior, create-issue work
mode shortcuts, file-viewer URL handling, and issue navigation scroll
behavior.
> - The benefit is a lower-risk review path for operator workflow fixes
while keeping unrelated execution-workspace and server work out of this
PR.
## Linked Issues or Issue Description
- Refs #8866, the closed broad source PR that this focused slice was
extracted from.
- Related: #8228 is an open UI polish PR, but its file list does not
overlap this branch.
- Related: #4090 is older merged operator workflow polish context.
No public GitHub issue exists for this exact extracted slice. The
problem is that several small operator issue-workflow fixes were bundled
inside a broad recovery branch, making them hard to review and ship
independently.
## What Changed
- Removed interaction continuation wake-policy labels from issue-thread
interaction card headers and updated the card test/story copy
accordingly.
- Added a markdown editor error boundary so rich editor render crashes
fall back to a raw textarea instead of breaking the compose surface.
- Made the create-issue work-mode shortcut accept ctrl-period and iOS
hardware-keyboard command-period-as-Escape behavior without dismissing
the dialog.
- Fixed file-viewer URL navigation to compare against the live browser
search string when router state is stale.
- Restored scroll reset when navigating from an issue detail route back
to the issue index, while preserving browser-history restoration
behavior.
## Screenshots
Before removing the continuation wake-policy badge from interaction card
headers:

After removing the wake-policy badge:

## Verification
- `pnpm exec vitest run
ui/src/components/IssueThreadInteractionCard.test.tsx
ui/src/components/MarkdownEditor.test.tsx
ui/src/components/NewIssueDialog.test.tsx
ui/src/context/FileViewerContext.test.ts
ui/src/lib/navigation-scroll.test.ts` — 5 files, 102 tests passed.
- `pnpm --filter @paperclipai/ui typecheck` — passed.
- `git diff --check` — passed.
## Risks
Low risk. The PR is limited to UI workflow components/helpers and their
tests. Main behavioral risks are keyboard shortcut edge cases across
browsers and fallback editor rendering, both covered by focused tests.
## Model Used
OpenAI Codex, GPT-5-based coding agent (`gpt-5` family; exact served
variant and context window are not exposed in this runtime), with
terminal, Git, GitHub, and test execution 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>
## Thinking Path
> - Paperclip is the open source control plane people use to manage AI
agents for work.
> - The relevant subsystem is the agent heartbeat policy surface: the
Paperclip skill, default onboarding AGENTS.md, new-agent runtime
defaults, and promptfoo eval coverage for agent behavior.
> - A broad recovery PR collected several unrelated local-mainline
changes, which made review too large and mixed policy/eval updates with
server execution and UI work.
> - This PR extracts only the heartbeat policy and prompt-eval slice so
reviewers can assess the behavior contract independently.
> - The eval additions cover scoped wake handling, idle no-op behavior,
dependency-blocked comment triage, final disposition, budget hard stops,
and Phase 5 memory/control-surface policy expectations.
> - The benefit is a narrower review surface plus deterministic
follow-up guidance for server/shared tests that should back these
prompt-level checks.
## Linked Issues or Issue Description
Refs #8866
No public issue was filed for this split. This is a focused extraction
from the closed broad recovery PR so heartbeat policy and eval coverage
can be reviewed separately from execution behavior, work-product feature
work, plugin hardening, pipeline health, and unrelated UI polish.
## What Changed
- Added promptfoo release-gate cases for scoped wake payload handling,
idle exits, dependency-blocked comment triage, final disposition, and
budget hard-stop behavior.
- Added Phase 5 memory/control-surface prompt eval cases for provider
binding precedence, provenance/audit fields, hook cost/trust handling,
and auditable board command surfaces.
- Documented how these prompt evals map to deterministic server/shared
follow-up coverage.
- Updated agent policy guidance so operator-facing engineering outputs
such as PRs, branches, commits, previews, and runtime services get
matching work products.
- Defaulted new agent runtime config to skip timer heartbeats when there
is no actionable work, with focused test coverage.
## Verification
- `cd evals/promptfoo && npx promptfoo@latest validate -c
promptfooconfig.yaml` passes.
- `/srv/paperclip/home/paperclipai/paperclip/node_modules/.bin/vitest
run ui/src/lib/new-agent-runtime-config.test.ts` passes in an isolated
worktree after `pnpm install --ignore-scripts --frozen-lockfile` created
workspace links.
- A live promptfoo eval was not run because `OPENROUTER_API_KEY`,
`OPENAI_API_KEY`, and `ANTHROPIC_API_KEY` were unset in the workspace.
## Risks
Low-to-medium risk. The runtime default reduces timer-driven empty
heartbeats for newly created agents, so the main behavioral risk is
missing an edge case where timer wakes were expected despite no
actionable work. The promptfoo additions are deterministic assertion
coverage and documentation-only until a live eval is run with provider
credentials.
> 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 GPT-5-based Codex coding agent in the Paperclip local Codex
adapter environment; exact hosted model ID and context window were not
exposed to the agent runtime. Tool use included shell, git, promptfoo
validation, Vitest, and the GitHub connector/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
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - The board UI is the main daily navigation surface for agents,
projects, and their related resources.
> - Operators need a lightweight way to keep frequently used agents and
projects close without changing company-wide ordering or ownership.
> - Resource memberships already model per-user relationships to
projects and agents, so they are the right place to store user-specific
starred state.
> - This pull request extends that membership contract with a starred
timestamp and exposes star controls in list/detail views.
> - The sidebar then uses those starred memberships to show compact,
user-specific shortcuts.
> - The benefit is faster navigation without introducing a separate
favorites system or leaking preferences across users.
## Linked Issues or Issue Description
No public GitHub issue exists.
Feature request:
## Problem or motivation
Users cannot pin frequently used agents or projects into the main
sidebar. Returning to important resources requires scanning full
project/agent lists or navigating through detail pages, which adds
friction to repeated daily workflows.
## Proposed solution
Store a per-user `starred_at` timestamp on agent and project
memberships, expose API actions to set or clear that state, add star
toggle controls to list/detail pages, and render starred projects and
agents as compact sidebar shortcuts.
## Alternatives considered
A separate favorites table would work, but it would duplicate membership
scoping and require another resource relationship model. Keeping starred
state on memberships preserves existing company/user boundaries and
avoids a second source of truth.
## Roadmap alignment
Checked `ROADMAP.md`; no overlapping planned core work for starred
resource/sidebar navigation was found.
## Additional context
The affected subsystems are `packages/db`, `packages/shared`, `server/`,
and `ui/`. The migration is idempotent with `IF NOT EXISTS` guards so
environments that saw an earlier local migration name can still apply
the final ordered migration safely.
## What Changed
- Added idempotent migration `0133_resource_membership_stars` for
`starred_at` columns and lookup indexes on agent/project memberships.
- Extended shared resource membership types and validators with starred
metadata and actions.
- Updated server resource membership services/routes to read and mutate
starred resource state.
- Added reusable star toggle UI and resource membership hook support for
starred state.
- Added starred projects and agents sidebar rendering, plus star
controls on list and detail pages.
- Added focused shared, server, and UI coverage for starred membership
behavior and sidebar rendering.
## Verification
- Rebased and force-with-lease pushed current PR head
`a086fc965391c9e50a51b5b83b5b44a797b2a6f4` onto current
`paperclipai/paperclip:master`; `gh pr view` reports `MERGEABLE` with no
merge conflicts. GitHub checks are green for this fresh head.
- `pnpm exec vitest run packages/shared/src/resource-memberships.test.ts
server/src/__tests__/resource-memberships-routes.test.ts
server/src/__tests__/workspace-runtime.test.ts
ui/src/components/Sidebar.test.tsx
ui/src/components/SidebarAgents.test.tsx
ui/src/components/SidebarStarredProjects.test.tsx
ui/src/components/StarToggle.test.tsx
ui/src/pages/InstanceExperimentalSettings.test.tsx` passed after the
rebase: 8 files, 143 tests.
- Greptile re-review is 5/5; the remaining screenshot thread was
resolved as non-blocking because this task explicitly requested no
screenshots/images in the PR.
- `pnpm exec vitest run
ui/src/components/SidebarStarredProjects.test.tsx` passed after the
mobile pending-spinner fix.
- `pnpm exec vitest run packages/shared/src/resource-memberships.test.ts
server/src/__tests__/resource-memberships-routes.test.ts
ui/src/components/Sidebar.test.tsx
ui/src/components/SidebarAgents.test.tsx
ui/src/components/SidebarStarredProjects.test.tsx
ui/src/components/StarToggle.test.tsx
ui/src/pages/InstanceExperimentalSettings.test.tsx` passed: 7 files, 68
tests.
- `pnpm --filter @paperclipai/db typecheck && pnpm --filter
@paperclipai/shared typecheck && pnpm --filter @paperclipai/server
typecheck && pnpm --filter @paperclipai/ui typecheck` passed
db/shared/server, then failed in pre-existing UI code outside this PR:
`src/pages/CompanyEnvironments.tsx` missing `@xterm/*` type declarations
and `previous` possibly null.
- Checked that the PR diff does not include `pnpm-lock.yaml` or
`.github/workflows` changes.
- Checked `ROADMAP.md` and found no overlapping planned core work for
starred resource/sidebar navigation.
- Searched existing GitHub PRs for duplicate starred-resource/sidebar
work and found none.
## Risks
- Migration touches membership tables. The SQL uses `IF NOT EXISTS` for
columns and indexes so environments that saw an earlier local migration
name can still apply this safely.
- Sidebar ordering and visibility changes could affect users who rely on
the previous flat sidebar layout.
- Starred state is per-user membership metadata; code paths must
continue preserving company/user scoping around memberships.
> 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 GPT-5 Codex, tool-enabled coding agent with shell/GitHub access.
Context window not disclosed by the runtime.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - The browser UI depends on the Inter font family for its intended
visual baseline
> - The app previously referenced remote Google Fonts stylesheets at
runtime
> - That meant self-hosted, offline, or privacy-sensitive deployments
could lose the intended typography or depend on an external request
> - This pull request bundles the required Inter variable font files
with the UI and serves them from the app
> - A normal UI test now covers the static assets and CSS wiring without
adding a package script or build step
> - The benefit is a more reliable, self-contained UI that does not rely
on third-party webfont hosting
## Linked Issues or Issue Description
No public GitHub issue was found for this exact gap.
**Subsystem affected**
ui/ — React + Vite board UI
**Problem or motivation**
Paperclip's UI should ship the webfont assets it references so
production and self-hosted deployments render consistently without
reaching out to Google Fonts at runtime.
**Proposed solution**
Bundle the Inter variable font files under the UI public assets, load
them with local `@font-face` declarations, document the bundled assets,
and cover the source assets/CSS wiring with a normal UI Vitest test.
**Alternatives considered**
Keeping the remote stylesheet dependency is simpler, but leaves
deployments dependent on external font hosting. Using system fonts only
would avoid the asset footprint, but changes the intended UI typography.
**Roadmap alignment**
This is a focused UI reliability/polish fix, not a roadmap-level core
feature.
**Additional context**
Searched public GitHub issues and PRs for `webfonts
repo:paperclipai/paperclip`; no duplicates or closely related open items
were found.
## What Changed
- Added bundled Inter variable font assets and their notice under
`ui/public/fonts/`.
- Replaced remote Google Fonts imports with local `@font-face`
declarations using relative public-asset URLs that remain subpath-safe
from built CSS.
- Documented the local font asset expectation in development and UI spec
docs.
- Removed the follow-up font asset checker scripts and package/build
wiring after review feedback clarified they are not required for
building the UI.
- Added `ui/src/lib/ui-font-assets.test.ts` to verify the shipped WOFF2
files, notice text, and CSS font references through the normal UI test
suite.
## Verification
- `pnpm --filter @paperclipai/ui exec vitest run
src/lib/ui-font-assets.test.ts --config vitest.config.ts`
- Passed.
- Confirms the bundled font files exist, are WOFF2 files, have notice
coverage, and are referenced by `ui/src/index.css`.
- `pnpm --filter @paperclipai/ui build`
- Passed.
- Confirmed the UI still builds after removing the checker from
`ui/package.json`.
- Confirmed `ui/dist/fonts/` contains `InterVariable.woff2`,
`InterVariable-Italic.woff2`, and `NOTICE.md` after the build.
- The UI build emitted existing warnings about `::highlight(...)`, a
dynamic/static import overlap for `MarkdownEditor.tsx`, unresolved
relative public font URLs left for runtime resolution, and large chunks,
but completed successfully.
## Risks
Low risk. This adds static font assets and swaps the font source from a
remote stylesheet to same-origin files. The main tradeoff is a larger
repository/UI asset footprint from the bundled `.woff2` files.
> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.
## Model Used
OpenAI Codex, GPT-5-based coding agent, tool-enabled terminal/GitHub
workflow with reasoning support.
## 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>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - The board UI is where operators inspect company activity, agent
work, and issue progress.
> - Existing board views show individual issue and run details, but they
do not give operators a compact time-based picture of work across
agents.
> - A work timeline helps operators scan when agents worked, how
handoffs happened, and where overlapping work occurred.
> - This pull request adds a company-scoped Work Timeline page backed by
the existing API surface and renders the timeline as a custom SVG
Gantt-style view.
> - The benefit is faster operator understanding of multi-agent
execution without opening each issue thread individually.
## Linked Issues or Issue Description
No public GitHub issue exists for this change.
Subsystem affected:
- ui/ — React + Vite board UI
Problem or motivation:
- Operators can inspect individual issues and runs, but there is no
compact time-based view of company work across agents.
- This makes it harder to scan overlaps, handoffs, retries, and activity
windows without opening many issue threads.
Proposed solution:
- Add a company-scoped Work Timeline page in the board UI.
- Render agent and system run spans as a custom SVG Gantt-style chart
with packed overlap lanes.
- Show issue color identity, kickoff attribution, hover-revealed
delegation connectors, retry styling, zoom controls, a sticky actor
gutter, and a minimap brush.
- Keep human activity lightweight by showing human kickoff chips without
plotting standalone human event rows.
Alternatives considered:
- Add the same information to existing issue-list or run-list views.
That would preserve simpler UI, but it would not show temporal overlap
or handoff paths clearly.
- Build this as a plugin-only surface. That keeps core smaller, but the
board already has the company-scoped route, navigation, and API client
patterns needed for this operator workflow.
Roadmap alignment:
- `ROADMAP.md` does not list an existing duplicate work-timeline
milestone. This supports the broader operator visibility direction
around artifacts, enforced outcomes, and higher-autonomy execution.
Additional context:
- Storybook includes `Pages/Work Timeline` stories for hour/day zoom and
a human-activity sample so reviewers can inspect the component without a
live backend.
## What Changed
- Added the Work Timeline page, route, sidebar entry, API client, query
key, and company-prefixed route helper coverage.
- Added a pure timeline layout transform for row packing, issue colors,
kickoff attribution, connector calculation, tick selection, and duration
formatting.
- Added the custom SVG timeline chart with sticky actor gutter,
hover-revealed connectors, zoom controls, minimap brushing,
visible-range feedback, and issue navigation.
- Added Storybook coverage plus sample fixtures for the work timeline.
- Added and corrected focused UI tests covering layout, chart behavior,
routing, sidebar behavior, and collapsed-sidebar expectations.
- Addressed Greptile feedback for kickoff fallback ordering, minimap
range math, document drag listener cleanup, and stable default `now`
handling.
## Verification
- `pnpm --filter @paperclipai/ui exec vitest run
src/lib/timeline/layout.test.ts
src/components/timeline/WorkTimelineChart.test.tsx
src/pages/Timeline.test.tsx src/lib/company-routes.test.ts
src/components/Sidebar.test.tsx
src/components/RequestCollapsedSidebar.test.tsx`
- `pnpm --filter @paperclipai/ui typecheck`
- `git merge-tree $(git merge-base HEAD origin/master) HEAD
origin/master | rg -n "<<<<<<<|changed in both|CONFLICT"` returned no
conflicts.
- Attempted Storybook screenshot capture with Playwright; Storybook ran
locally, but Chromium could not launch in this container because native
browser libraries such as `libatk-1.0.so.0` are unavailable and `npx
playwright install-deps chromium` requires interactive sudo.
## Risks
- Medium UI risk: this adds a substantial visual surface with custom SVG
interaction logic, so browser-level review is still useful for
responsive behavior and usability.
- Low backend risk: this PR only adds a UI client/page around the
existing timeline API contract and does not change database schema or
server routes.
> 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 GPT-5 via Codex coding agent, with repository file access, shell
command execution, GitHub connector access, and focused test execution.
Exact context-window details are not exposed in this runtime.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have 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>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - When you swap an agent's adapter (e.g. from one LLM provider to
another), the server merges the incoming PATCH body with stored config —
keys listed in \`ADAPTER_AGNOSTIC_KEYS\` are preserved regardless of
which adapter is active
> - That constant was defined independently in two places:
\`server/src/agents.ts\` (used by the adapter-swap route) and
\`ui/src/lib/agent-config-patch.ts\` (used by the UI patch builder)
> - PR #8975 fixed the bug where \`paperclipSkillSync.desiredSkills\`
was dropped on adapter swap by adding it to the server-side constant,
but the UI-side copy was not updated in the same PR — creating ongoing
drift risk
> - This pull request hoists \`ADAPTER_AGNOSTIC_KEYS\` into
\`packages/shared\` so both consumers import the same constant
> - The benefit is a single source of truth: any future key addition is
made in one place and both the server route and the UI patch builder
pick it up automatically, with a drift guard to catch any accidental
re-duplication
## Linked Issues or Issue Description
Refs #8975 — follow-up deduplication: #8975 fixed the runtime bug but
left the constant duplicated across server and UI. This PR closes that
gap.
## What Changed
- Added \`ADAPTER_AGNOSTIC_KEYS\` constant and \`AdapterAgnosticKey\`
type to \`packages/shared/src/adapter-agnostic-keys.ts\`
- Updated \`server/src/agents.ts\` to import the shared constant,
removing the local copy
- Updated \`ui/src/lib/agent-config-patch.ts\` to import the shared
constant, removing the local copy
- Added \`packages/shared/src/adapter-agnostic-keys.test.ts\`: drift
guard asserting the expected key set and both consumer import sites
## Verification
\`\`\`bash
pnpm exec vitest run packages/shared/src/adapter-agnostic-keys.test.ts
ui/src/lib/agent-config-patch.test.ts
server/src/__tests__/agent-instructions-routes.test.ts
pnpm --filter @paperclipai/shared typecheck
pnpm --filter @paperclipai/server typecheck
pnpm --filter @paperclipai/ui typecheck
\`\`\`
All 15 tests pass across the three files; all three packages typecheck
clean.
## Risks
Low risk — behavior-preserving refactor. The key set is unchanged; only
the import source changes. The drift guard will fail loudly if someone
accidentally re-introduces a local copy or modifies one without updating
the other.
> 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
- Provider: Anthropic
- Model: Claude Sonnet 4.6 (\`claude-sonnet-4-6\`)
- Context: standard context window, tool use enabled
- Reasoning: standard mode (no extended thinking)
## 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)
- [ ] 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
- [ ] 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
- [ ] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - Agent runs often need provider credentials, API tokens, and other
environment-bound secrets.
> - Company-level secrets work for shared credentials, but they do not
model values that should differ by human operator.
> - Without a user-scoped model, a run can dispatch without knowing
whether the responsible human has supplied the needed value.
> - Paperclip also needs run attribution to make those user-scoped
runtime checks deterministic and auditable.
> - This pull request adds user-specific secret definitions, per-user
values, environment bindings, responsible-user attribution, and runtime
resolution gates.
> - The benefit is that teams can define the secret once, let each user
provide their own value, and block runs before dispatch when required
user secrets or active definitions are unavailable.
## Linked Issues or Issue Description
Refs #224
Refs #6057
This PR implements user-specific secret support as a core
secret-management capability rather than a one-off adapter setting. It
is related to existing public work on company secrets UI and runtime
secret refs, but is distinct because the value is owned by the
responsible user and resolved at run dispatch time.
Related PR search before opening found existing secrets work such as
#1550, #8256, #8614, #8634, and #8647; none of those add the full
user-secret definition/value/runtime gate covered here.
## What Changed
- Added user-secret definitions and per-user "My secrets" values,
keeping stored values out of access metadata.
- Added `user_secret_ref` environment bindings and UI affordances to
pick them alongside existing secret refs.
- Added responsible-user runtime resolution so user-secret refs resolve
against the human responsible for the run.
- Added pre-dispatch missing-secret gates so runs fail before adapter
dispatch when required user values are absent or definitions are
inactive.
- Added low-trust allowlist hardening for user-secret runtime access.
- Added issue, routine, run, and agent API key responsible-user
attribution and fail-closed dispatch behavior when attribution cannot be
resolved.
- Added denial-copy mapping so responsible-user authorization failures
surface as actionable run outcomes instead of opaque setup failures.
- Added OpenAPI documentation for the user-secret routes.
- Rebases cleanly on current `master`; migrations were renumbered
incrementally as `0128_user_specific_secrets`,
`0129_agent_api_key_responsible_user`, and
`0130_run_responsible_user_invariant` after upstream `0126`/`0127`
migrations.
- Removed previously committed local design screenshots so the PR
contains code/docs/tests only.
## Verification
- PASS: PR head `2527febd106bcf3ca264ca0da7fca491084192d6` is based on
`paperclipai/paperclip:master`.
- PASS: `git diff --check`
- PASS: `git diff --name-only public/master...HEAD | rg
'^(pnpm-lock\\.yaml|\\.github/workflows/|screenshots/)' || true`
produced no files.
- PASS: migration journal audit confirmed unique indexes through `130`
with tail entries `0126_issue_comment_derived_attribution`,
`0127_environment_custom_images_instance_scoped`,
`0128_user_specific_secrets`, `0129_agent_api_key_responsible_user`, and
`0130_run_responsible_user_invariant`.
- PASS: `pnpm --filter @paperclipai/ui typecheck`
- PASS: `pnpm --filter @paperclipai/server typecheck`
- PASS: `pnpm --filter @paperclipai/server exec vitest run
src/__tests__/heartbeat-responsible-user-invariant.test.ts`
- PASS: `pnpm --filter @paperclipai/server exec vitest run
src/__tests__/heartbeat-active-run-output-watchdog.test.ts
src/__tests__/heartbeat-stale-queue-invalidation.test.ts
src/__tests__/heartbeat-workspace-finalize-branch.test.ts
src/__tests__/issue-monitor-scheduler.test.ts`
- PASS: `pnpm --filter @paperclipai/server exec vitest run
src/__tests__/heartbeat-comment-wake-batching.test.ts
src/__tests__/heartbeat-retry-scheduling.test.ts
src/__tests__/heartbeat-accepted-plan-workspace-refresh.test.ts
src/__tests__/heartbeat-plugin-environment.test.ts`
- PASS: `pnpm --filter @paperclipai/server exec vitest run
src/__tests__/low-trust-red-team-routes.test.ts`
- PASS: `pnpm --filter @paperclipai/server exec vitest run
src/__tests__/secrets-service.test.ts` (55 tests)
- PASS: `pnpm vitest run server/src/__tests__/secrets-routes.test.ts
server/src/__tests__/secrets-service.test.ts` (89 tests after final
Greptile cleanup fixes)
- PASS: `pnpm --filter @paperclipai/server exec vitest run
src/__tests__/heartbeat-issue-liveness-escalation.test.ts` (17 tests
after the final rebase CI fix)
- PASS: focused server Vitest batches covering heartbeat recovery,
project env, plugin env, routines, low-trust, pipelines, monitors,
watchdog, and stale queue paths.
- PASS: GitHub checks are green on
`2527febd106bcf3ca264ca0da7fca491084192d6`, including Typecheck +
Release Registry, Build, General tests, serialized server suites, e2e,
Canary Dry Run, verify, security checks, and Greptile Review.
- PASS: Greptile Review completed successfully on
`2527febd106bcf3ca264ca0da7fca491084192d6` with Confidence Score 5/5,
and GraphQL review-thread audit returned zero unresolved non-outdated
threads.
## Risks
- Runtime behavior now depends on a run having a correct responsible
user; missing or incorrect responsibility assignment can block runs
before adapter dispatch.
- `user_secret_ref` bindings intentionally expose metadata without
values, but UI/API callers may need to handle the new binding kind
explicitly.
- External secret providers and IAM policies are not automatically
provisioned by this PR; operators still need to configure provider-side
access for non-local vaults.
- The PR is broad across db/shared/server/UI/runtime paths, so release
validation should include both API and UI secret workflows before merge.
- The migration renumbering is intentionally incremental after upstream
migrations; the branch migrations use guarded
column/table/index/constraint creation so users who tested the older
draft numbering should not hit duplicate DDL for the existing objects.
> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.
## Model Used
OpenAI Codex, GPT-5-based coding agent (`gpt-5`), Codex local adapter
with shell/tool use and code execution. Context window and internal
reasoning mode are not exposed by the runtime.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - Each agent runs on an adapter (`claude_local`, `codex_local`, …) and
can be assigned company skills that are synced into its runtime
> - An agent's desired-skill selection is persisted inside its single
`adapterConfig` JSON blob under `paperclipSkillSync`, even though the
selection is a company-level, adapter-agnostic choice
> - When a user changes an agent's adapter type, both the server PATCH
handler and the UI patch builder rebuild `adapterConfig` and carry over
only a hardcoded allow-list of adapter-agnostic keys (`env`, `cwd`,
instructions bundle, …)
> - `paperclipSkillSync` was missing from both allow-lists, so switching
adapters (e.g. claude_local → codex_local) silently wiped every assigned
skill
> - This pull request adds `paperclipSkillSync` to the adapter-agnostic
preservation list on both layers and covers it with regression tests
> - The benefit is that switching an agent's adapter no longer destroys
its skill configuration — skills are preserved exactly like
env/cwd/instructions already are
## Linked Issues or Issue Description
Fixes#8974
## What Changed
- **Server (authoritative fix)** — `server/src/routes/agents.ts`: added
`"paperclipSkillSync"` to the `ADAPTER_AGNOSTIC_KEYS` list in the
`changingAdapterType` branch of `PATCH /agents/:id`. On an adapter-type
change the handler now restores the skill-sync selection from the
existing persisted config when the incoming config omits it — the same
mechanism already used for `env`, `cwd`, and the instructions bundle.
This protects every API/CLI client, not just the UI.
- **UI (defense in depth)** — `ui/src/lib/agent-config-patch.ts`: added
`"paperclipSkillSync"` to the client-side `ADAPTER_AGNOSTIC_KEYS` in
`buildAgentUpdatePatch`, so the optimistic patch the client builds on an
adapter switch stops stripping the key before it reaches the server.
- **Tests** — added regression tests on both layers:
- `server/src/__tests__/agent-instructions-routes.test.ts`: `PATCH`ing
`adapterType` (claude_local → codex_local) with `replaceAdapterConfig:
true` keeps `adapterConfig.paperclipSkillSync`.
- `ui/src/lib/agent-config-patch.test.ts`: `buildAgentUpdatePatch`
preserves `paperclipSkillSync` when the overlay changes the adapter
type.
## Verification
```
# server (run from repo root)
cd server && ../node_modules/.bin/vitest run \
src/__tests__/agent-instructions-routes.test.ts \
src/__tests__/agent-skills-routes.test.ts \
src/__tests__/agent-adapter-validation-routes.test.ts \
src/__tests__/agent-permissions-routes.test.ts
# 83 passed
../node_modules/.bin/tsc --noEmit -p tsconfig.json # clean
# ui
cd ui && ./node_modules/.bin/vitest run src/lib/agent-config-patch.test.ts # 7 passed
pnpm --filter @paperclipai/ui typecheck # clean
```
Both new tests fail without the corresponding source change (verified
red → green).
Manual: create an agent on `claude_local`, assign skills, switch it to
`codex_local`, and confirm `GET /api/agents/:id/skills` still returns
the desired skills.
## Risks
Low risk.
- The change only *adds* one key to an existing preservation allow-list;
it does not alter how any other key is handled. Behavior for agents
without a `paperclipSkillSync` block is unchanged (the key is simply
absent and nothing is copied).
- `paperclipSkillSync` is adapter-agnostic (company skill keys, not
adapter-specific), so carrying it across an adapter switch is always
safe — a target adapter that does not support skill sync just ignores
it, and switching back restores the selection.
- Same-adapter config edits already merged and preserved the key; this
only closes the adapter-type-change gap, matching the existing
env/cwd/instructions behavior.
- Follow-up (not in this PR to keep it minimal): the server and client
`ADAPTER_AGNOSTIC_KEYS` lists are maintained separately and already
diverge (`instructionsFilePath` is client-only); a shared constant could
prevent future drift.
## Model Used
Claude Opus 4.8 (`claude-opus-4-8`, 1M-token context), extended thinking
enabled, with tool use (file edit, shell, GitHub CLI) via Claude Code. A
read-only sub-agent was used to trace the root cause across the server
and UI layers.
## 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 (bug fix, not a feature)
- [x] I have searched GitHub for duplicate or related PRs and linked
them above (none found)
- [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
(`fix/preserve-skills-on-adapter-type-switch`) 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 (N/A —
internal config-preservation fix, no user-facing docs or API contract
change)
- [x] I have considered and documented any risks above
- [ ] All Paperclip CI gates are green (pending CI on this PR)
- [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
(pending review)
- [x] I will address all Greptile and reviewer comments before
requesting merge
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - The agent detail page is where operators inspect heartbeat runs and
watch live execution output.
> - Heartbeat runs can publish progress events while longer operations
are happening.
> - The live log viewer already appended streamed log and structured run
events, but it ignored progress events for the same run.
> - That made useful progress text invisible in the run log until
another event type arrived or the operator inspected other surfaces.
> - This pull request renders run progress events as system log lines in
the live agent run viewer.
> - The benefit is clearer live feedback during long-running heartbeat
operations without changing the backend event contract.
## Linked Issues or Issue Description
No public GitHub issue exists, so this PR describes the issue inline
following the bug report template.
### What happened
The agent detail run log subscribed to company live events and handled
`heartbeat.run.log` plus structured `heartbeat.run.event` payloads, but
it ignored `heartbeat.run.progress` events for the active run.
### Expected behavior
When a heartbeat run emits a progress message, the active run log should
show that message immediately as operator-visible system output.
### Steps to reproduce
1. Open an agent detail page for a live heartbeat run.
2. Trigger a run operation that emits `heartbeat.run.progress` events
with a `message` and optional `phase`.
3. Watch the live log viewer.
Before this change, the progress event was ignored by the log viewer.
After this change, it appears as a system log line, prefixed by
`[phase]` when a phase is present.
### Paperclip version / deployment mode
Current `master`; local development and normal board UI deployments.
### Related work search
Searched public GitHub issues and PRs in `paperclipai/paperclip` for
`heartbeat.run.progress AgentDetail` and `run progress log viewer`; no
duplicate issue or PR was found.
## What Changed
- Added live handling for `heartbeat.run.progress` events in
`AgentDetail`'s run `LogViewer`.
- Render progress messages as `system` log lines for the matching run.
- Include the optional progress phase in the displayed line as `[phase]
message`.
- Prefer the event's `updatedAt` timestamp when provided, falling back
to the live event timestamp.
- Added a replay key for progress log lines so WebSocket reconnect
replay does not duplicate the same rendered progress line.
- Added focused formatter/key tests covering phased progress, unphased
progress, empty messages, and replay-key output.
### Visual output example
The rendered log text is covered by the new formatter test:
```text
[workspace] Syncing issue history
Preparing workspace
```
No layout or styling changes are included; this PR only makes existing
log-line UI receive one more live event type.
## Verification
- `pnpm install --frozen-lockfile` — completed; emitted non-fatal
bin-link warnings for the unbuilt plugin SDK dev CLI.
- `pnpm --filter @paperclipai/ui typecheck` — passed.
- `pnpm --filter @paperclipai/ui exec vitest run
src/pages/AgentDetail.progress.test.ts
src/context/LiveUpdatesProvider.test.ts` — passed, 2 files / 26 tests.
- `git diff --check` — passed.
- Local sensitive-content scan over the PR diff using patterns for API
keys, tokens, secrets, passwords, auth headers, private keys,
localhost/private paths, internal ticket ids, agent links, and tailnet
markers — no findings.
## Risks
Low risk. This is a UI-only live-event handling change for an existing
event type. The replay guard is intentionally scoped to progress lines
and uses the rendered timestamp, stream, and chunk as the key, so
repeated progress events with distinct timestamps or messages still
appear.
> 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 coding agent with repository tool use, shell
execution, GitHub CLI access, and local test execution. Context window
size was not exposed in this runtime.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have 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>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - Environment sandboxes already support custom image creation and
refresh through a temporary SSH setup session.
> - The existing workflow makes operators copy an SSH command into an
external terminal before they can install packages or make image
changes.
> - That extra context switch is slower, easier to get wrong, and less
integrated with the setup session Paperclip already tracks.
> - This pull request adds an embedded browser SSH terminal for custom
image setup, so operators can start working in the target sandbox
directly from the environment configuration flow.
> - The implementation uses short-lived websocket attachment tokens,
session-lifetime SSH host-key pinning, and server-managed terminal
cleanup so the feature fits the existing setup-session boundary.
> - The benefit is a smoother custom image creation and refresh
experience without asking users to leave Paperclip for routine sandbox
setup work.
## Linked Issues or Issue Description
No public GitHub issue exists.
### Subsystem affected
Cross-cutting: `server/` custom image setup APIs and websocket handling,
`ui/` environment configuration UI, and shared custom image contracts.
### Problem or motivation
Custom image creation and refresh require an operator to open a separate
SSH client, paste the command shown by Paperclip, perform setup work,
then return to the browser to finish the image flow. This is functional
but awkward for a setup process that already starts and tracks a
temporary sandbox session.
### Proposed solution
Embed an SSH terminal in the custom image setup UI. When a setup session
exposes an SSH payload, Paperclip should open a browser terminal backed
by a server-side websocket session, let the operator run setup commands
in-place, and then close the terminal when setup is finished, cancelled,
expired, or disconnected.
### Alternatives considered
- Keep the existing copy/paste SSH command workflow. This remains a
fallback, but it does not streamline the common path.
- Put SSH credentials directly into websocket URLs. This was avoided so
terminal authentication can happen in an explicit first websocket auth
frame rather than in logged URLs.
- Trust the SSH host blindly for every reconnect. This PR instead pins
the observed host-key fingerprint for the setup-session lifetime.
### Roadmap alignment
This fits the roadmap theme of making agent workspaces usable in more
remote and sandboxed environments while preserving Paperclip's
control-plane model.
### Additional context
Public GitHub search did not find a duplicate issue or PR for `custom
image terminal ssh` in `paperclipai/paperclip`.
## What Changed
- Added server-side terminal session tracking for custom image setup
sessions, including connect-token issuance, websocket attachment,
expiry, resize, input, and shutdown handling.
- Added an embedded browser terminal to the custom image creation and
refresh flow when a setup session provides SSH connection details.
- Moved terminal token authentication out of the websocket URL and into
the first websocket JSON auth frame.
- Added SSH host-key SHA-256 pinning for each terminal session and
documented the provider convention for username-embedded SSH
credentials.
- Updated the custom image environment API and UI so the setup terminal
can open, reconnect, show status, authenticate, resize, and remain
active for the setup-session lifetime once attached.
- Kept custom image setup routes company-scoped and closed active
terminal sessions on setup finish/cancel.
- Added focused unit/integration/UI coverage for token expiry,
setup-session expiry, websocket close paths, host-key pinning, and
terminal session lifecycle behavior.
- Removed the generated lockfile delta from the PR; CI owns temporary
lockfile regeneration for manifest-changing PRs.
## Verification
- `pnpm exec vitest run
server/src/__tests__/server-startup-feedback-export.test.ts
server/src/__tests__/environment-custom-image-terminal-ws.test.ts
server/src/services/environment-custom-image-terminal-sessions.test.ts
server/src/__tests__/environment-custom-image-routes.test.ts
packages/shared/src/environment-custom-images.test.ts
ui/src/pages/CompanyEnvironments.test.tsx`
- 6 test files passed
- 58 tests passed
- `pnpm --filter @paperclipai/server typecheck`
- `pnpm --filter @paperclipai/ui typecheck`
- `pnpm --filter @paperclipai/server build`
- `pnpm --filter @paperclipai/ui build`
- `pnpm run typecheck:build-gaps`
- `git diff --check`
- Local sensitive-content scan over the PR diff using patterns for API
keys, private keys, private hostnames, local paths, token fields, and
credential-like strings.
- Findings were limited to removed URL-token code and synthetic test
placeholders such as `ssh-token-secret` and
`terminal-token-terminal-token-123456`.
- No real credentials, private hostnames, local filesystem paths, or
instance-local links were found.
- Remote PR checks were green after the implementation commit, including
Build, Typecheck + Release Registry, General tests, serialized server
suites, e2e, verify, Socket, Snyk, Superagent, and Greptile 5/5.
- Post-merge PR hardening on July 3, 2026: merged `origin/master` at
`47448721e` into the branch, resolved the `CompanyEnvironments.tsx`
import conflict, reran focused tests, server/UI typechecks, server/UI
builds, `pnpm run typecheck:build-gaps`, and `git diff --check`, scanned
the final diff for sensitive content, pushed `4b43558cc`, and confirmed
all remote checks plus Greptile 5/5 were green.
- PR metadata correction on July 3, 2026: changed the title/body framing
from bug-fix language to feature-request language. No source files
changed for this metadata-only update.
## Risks
- Moderate surface area because this adds websocket routing,
setup-session runtime state, package dependencies, and a new custom
image UI path.
- New websocket attachments still require valid short-lived tokens;
established terminal sessions remain bounded by setup-session expiry,
explicit finish/cancel, client close, or server shutdown.
- The terminal-session store is in-memory, so active terminal websocket
tokens and host-key pins do not survive server restarts.
- SSH host-key verification uses session-lifetime TOFU pinning because
the current provider payload does not expose a trusted host-key
fingerprint.
- The external SSH command remains important as a fallback if a browser,
proxy, or network environment cannot sustain the websocket terminal.
> 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 coding agent with shell/tool execution. Context
window size was not exposed in this runtime.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have 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>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - The issue details sidebar is a high-traffic operator surface for
scanning task state, ownership, policy, relationships, and external
references.
> - The previous properties pane mixed too much behavior in one large
component and several rows could overflow or become hard to scan with
long labels and relation lists.
> - The UI needed a narrower, more reusable structure so compact
property rows, relation pills, external URL rows, and picker triggers
behave consistently.
> - This pull request splits the properties pane into focused helper
modules and tightens the pane's layout, truncation, popover, and
overflow behavior.
> - The benefit is a denser, more stable properties pane that stays
usable when issues have long titles, many relationships, external URLs,
or execution policy IDs.
## Linked Issues or Issue Description
No public issue exists for this work. I searched GitHub issues and PRs
for `IssueProperties properties pane`; there was no duplicate issue and
the matching PRs were unrelated test stabilization or other work.
### Problem or motivation
The board issue details properties pane is a frequent operator workflow
surface, but it was implemented as one large component and several rows
could become hard to scan with long labels, many relationships, external
URLs, or execution policy IDs.
### Proposed solution
Split the properties pane into focused modules, tighten compact row
layout and truncation behavior, add bounded previews with explicit
expansion controls for long relation and URL lists, and make execution
policy ID generation work on insecure origins as well as normal browser
origins.
### Alternatives considered
A smaller patch inside the existing monolithic component would fix
individual overflow symptoms, but it would keep related primitives,
picker behavior, relation controls, and external URL rendering tangled
in one file. The split keeps the behavior easier to test and review
without changing public APIs.
### Roadmap alignment
This is an incremental UI quality improvement to the existing board task
details surface. I checked `ROADMAP.md` and did not find overlapping
planned core feature work.
## What Changed
- Split the issue properties pane into focused modules for helpers,
primitives, relation controls, property pickers, and external object
rows.
- Cleaned up row spacing, truncation, picker trigger alignment, scroll
behavior, status color coverage, and long-value titles.
- Added bounded previews and expand/collapse controls for blocking,
sub-task, related-task, and external URL lists.
- Fixed execution policy ID generation so insecure origins fall back to
a stable base URL instead of throwing.
- Updated Storybook issue-management stories and expanded unit coverage
for the new properties pane behavior.
## Verification
- `git diff --check origin/master..HEAD`
- `pnpm --filter @paperclipai/ui exec vitest run
src/components/IssueProperties.test.tsx` — 38 tests passed
## Risks
Low to moderate risk. The changes are UI-only, but they touch a
frequently used task details surface. The main risk is a subtle layout
regression in an untested viewport or issue shape; the branch adds
targeted coverage for long relation and external URL lists.
> 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 GPT-5 Codex coding agent with repository tool use, shell
execution, GitHub CLI access, and local test execution. Context window
size was not exposed in this runtime.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have 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: Claude Opus 4.8 <noreply@anthropic.com>
## Thinking Path
> - Paperclip is the open source control plane people use to manage AI
agents for work.
> - Environment variables and secrets sit in the configuration surfaces
that let agents, projects, routines, and company environments run with
the right runtime inputs.
> - The previous editor was a single legacy component with cramped row
behavior, weak secret conversion affordances, and duplicated handling
across several call sites.
> - Operators need a clearer editor that handles text values, secret
references, draft rows, and sensitive-value warnings consistently
wherever environment variables are configured.
> - This pull request replaces the legacy editor with a reusable
environment variables editor and migrates the existing configuration
surfaces to it.
> - The benefit is a more reliable editing workflow with targeted test
coverage around row state, dotenv parsing, secret selection, and
affected page integrations.
## Linked Issues or Issue Description
No public GitHub issue exists, so this PR describes the issue inline
following the feature request template.
### Subsystem affected
ui/ — React + Vite board UI
### Problem or motivation
Environment variables are edited in several Paperclip configuration
surfaces, including agent config, project properties, stage secrets,
routine sections, company environments, and company settings. The legacy
editor made common operator work difficult: rows could feel cramped,
secret conversion was inconsistent, draft rows and imported dotenv data
were easy to mishandle, and sensitive-value warnings did not have a
consistent place in the workflow.
### Proposed solution
Introduce a reusable environment variables editor component that
consistently supports text values, secret references, draft rows, dotenv
import parsing, sensitive-value hints, secret picking, secret creation,
and conversion to stored secrets. Migrate the existing
environment-variable call sites to the shared editor so behavior and
tests live in one component family.
### Alternatives considered
Keeping the existing `EnvVarEditor` and patching individual call sites
would preserve duplication and leave each surface responsible for its
own row and secret handling. This PR instead centralizes the behavior so
future fixes cover all migrated surfaces.
### Roadmap alignment
Checked `ROADMAP.md`; this does not duplicate a named roadmap item. It
supports the existing local-first and deployment-oriented product
direction by improving the UI where operators configure runtime
environment values.
### Additional context
The PR includes targeted tests for the editor model, dotenv parsing,
sensitive-value detection, component behavior, affected page
integrations, and Greptile review regressions around external saves and
bulk import immutability.
## What Changed
- Replaced the legacy `EnvVarEditor` with a reusable
`environment-variables-editor` component family.
- Added editor model helpers for draft rows, dotenv parsing,
sensitive-value detection, secret picking, secret creation, and
conversion to secret references.
- Migrated agent config, project properties, stage secrets, routine
editable sections, company environments, company settings, design guide
examples, and Storybook stories to the new editor.
- Added targeted tests for the editor model, parsing, sensitive-value
detection, component behavior, and affected company environment/settings
integrations.
- Fixed the company settings test harness to use the repo’s
`flushSync`-based React test helper pattern under the current React
build.
- Addressed Greptile feedback by flushing pending editor drafts before
enclosing form submits or external save-button clicks, cloning
bulk-import rows before mutation, and deferring the overflow
store-as-secret popover open path.
## Verification
- `pnpm exec vitest run
ui/src/components/environment-variables-editor/EnvironmentVariablesEditor.test.tsx
ui/src/pages/CompanyEnvironments.test.tsx
ui/src/components/AgentConfigForm.render.test.tsx` — passed, 3 files /
40 tests.
- Earlier focused Vitest coverage for model, dotenv parsing, sensitive
detection, company environments, and company settings passed, 6 files /
78 tests.
- `pnpm --filter @paperclipai/ui typecheck` — passed.
- GitHub PR checks on head `0aa49c6f8afed1f62d9e26da07fc466fbf299850` —
passed; CI checks green, security review neutral, Greptile check
success.
- Greptile review — 5/5 confidence on head
`0aa49c6f8afed1f62d9e26da07fc466fbf299850`; 0 unresolved review threads.
## Risks
- Medium UI risk: several environment-variable entry surfaces now share
the new editor, so regressions could affect multiple configuration
workflows at once.
- Secret conversion, draft-row behavior, external save flushing, and
bulk import behavior are covered by targeted tests, but reviewer
attention should still focus on manual editing flows, focus retention,
and save/cancel affordances.
- No database or API contract 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-based coding agent with tool use and local command
execution; medium reasoning mode. Exact context-window metadata is not
exposed in this runtime.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have 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: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - A core part of that experience is watching active agent runs without
dropping into raw logs first
> - Local and sandbox-backed adapters already record useful run output,
progress, and tool activity
> - But active issue threads could sit visually stale while the agent
was syncing workspaces, tailing sandbox output, or emitting incremental
tool-call updates
> - Operators need timely, human-readable progress while preserving the
raw transcript underneath
> - This pull request streams sandbox run-log progress into runtime
status, keeps visible issue threads refreshed, and folds repeated ACPX
tool updates into stable transcript cards
> - The benefit is that long-running agent work becomes easier to
supervise without changing the task/comment control-plane model
## Linked Issues or Issue Description
No public GitHub issue exists for this exact change.
Problem/motivation:
- During long-running sandboxed agent work, the issue UI can appear idle
even though the agent is actively syncing, running tools, or producing
incremental output.
- Operators need realtime feedback at the issue-thread layer, not only
after opening raw logs or waiting for the final heartbeat result.
- Related public context: #1808 previously added live-run status dots to
Projects; #4362 touches heartbeat wakeup behavior but is not a duplicate
of this runtime/UI feedback change.
## What Changed
- Added sandbox run-log streaming support and defaulted sandbox-capable
local adapters into the richer live-feedback path.
- Surfaced environment/sandbox sync progress through heartbeat runtime
status with bounded, redacted snippets.
- Added live issue-thread cache patching so visible active runs update
as progress events arrive.
- Folded repeated ACPX `tool_call` updates into one transcript card
instead of stacking duplicate cards.
- Updated adapter docs and added focused regression coverage for sandbox
log streaming, runtime status, ACPX parsing, live updates, transcript
rendering, and issue chat messages.
## Verification
- `pnpm install --frozen-lockfile`
- `pnpm exec vitest run ui/src/context/LiveUpdatesProvider.test.ts`
- `pnpm exec vitest run
server/src/services/heartbeat-run-runtime-status.test.ts
server/src/__tests__/heartbeat-runtime-state.test.ts
ui/src/context/LiveUpdatesProvider.test.ts`
- `pnpm exec vitest run
packages/adapter-utils/src/execution-target-sandbox.test.ts
packages/adapter-utils/src/sandbox-managed-runtime.test.ts
server/src/services/heartbeat-run-runtime-status.test.ts
server/src/__tests__/agent-live-run-routes.test.ts
server/src/__tests__/heartbeat-runtime-state.test.ts
packages/adapters/acpx-local/src/ui/parse-stdout.test.ts
ui/src/context/LiveUpdatesProvider.test.ts
ui/src/components/transcript/RunTranscriptView.test.tsx
ui/src/lib/issue-chat-messages.test.ts
ui/src/components/IssueChatThread.test.tsx`
- GitHub PR workflow on head `8397953e7b41ccd42e5d9457ee7e4dfb996e4ec5`:
`verify`, build, typecheck/release-registry, e2e, general shards,
serialized server shards, and canary dry run passed.
- Greptile Review on head `8397953e7b41ccd42e5d9457ee7e4dfb996e4ec5`:
Confidence Score 5/5, no unresolved review threads.
## Risks
- Live issue-thread cache patching could miss an edge case for a route
shape not covered by tests.
- Surfacing active-run snippets needs continued care around redaction;
this PR keeps snippets bounded and adds redaction-focused coverage.
- More frequent active-run UI refreshes could expose performance issues
on very large issue threads, though updates are scoped to visible
run/query caches.
## Model Used
OpenAI GPT-5 via Codex, operating as a tool-enabled coding agent with
shell, git, and repository-editing capabilities. Context window size is
not exposed in this runtime.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have 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>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - Adapter names are part of the board-facing agent setup and
management experience.
> - The product now treats adapters as harnesses, while execution
environments are modeled separately.
> - Several built-in adapter labels still carried legacy local wording
from the older harness-by-environment model.
> - That wording makes the UI noisier and implies a distinction users no
longer need to reason about.
> - This pull request normalizes adapter display labels while keeping
persisted adapter type identifiers unchanged.
> - The benefit is clearer adapter selection and management copy without
a database migration.
## Linked Issues or Issue Description
No public GitHub issue was found for this exact cleanup.
Related public PRs:
- Supersedes #8910, an earlier branch for the same cleanup that did not
include the later docs/gateway/Cursor alignment.
- Refs #8819, which is related display-registry work for external
multi-segment adapter labels, but not a duplicate of this built-in label
cleanup.
Feature request details:
- Subsystem affected: Cross-cutting (`ui/`, `packages/adapters`, and
docs).
- Problem or motivation: user-facing adapter names include legacy local
qualifiers even though adapters map to harnesses and environments are
first-class elsewhere.
- Proposed solution: remove the legacy local wording from built-in
display labels, keep machine-readable adapter type ids unchanged, and
keep gateway disambiguation where it is useful.
- Alternatives considered: changing persisted adapter type ids was ruled
out because it would create migration and compatibility risk; one-off UI
replacements were ruled out because the display registry is already the
correct central label boundary.
- Roadmap alignment: this is small adapter UX polish, not a new
roadmap-level core feature.
## What Changed
- Updated the adapter display registry so known adapter labels are final
and no built-in local adapter renders a legacy local suffix.
- Preserved clean derived labels for unknown plugin local types while
keeping gateway disambiguation for unknown gateway types.
- Updated `AdapterManager` to prefer registry labels when the server
reports raw adapter type ids for built-ins.
- Removed legacy local wording from built-in adapter metadata labels in
UI and adapter packages.
- Aligned Cursor adapter metadata with the central display registry
label.
- Updated adapter docs and Storybook fixtures to match the new display
names.
- Added focused registry coverage for built-in labels and unknown plugin
suffix behavior.
## Verification
- `pnpm check:tokens`
- `git diff --check origin/master...fix/adapter-display-labels`
- Patch-addition scan for added secrets, private paths, and internal
links: no matches.
- GitHub duplicate search for open adapter-label/local-suffix issues and
PRs; #8910 was identified as the older superseded public PR.
- `pnpm exec vitest run
ui/src/adapters/adapter-display-registry.test.ts`
- `pnpm --filter @paperclipai/ui typecheck`
- Stale-label scan found no remaining user-facing display-label
suffixes; remaining local wording is operational/test terminology such
as adapter ids, docs about running locally, and test descriptions.
## Risks
Low risk. The change is display-label and documentation focused, and
adapter type ids remain unchanged. The main risk is ambiguous gateway
naming, mitigated by keeping explicit gateway labels where variants need
disambiguation.
## Model Used
OpenAI GPT-5 via Codex, tool-enabled coding agent in a local repository
workspace. Context window size is not exposed by 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>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - The app shell exposes user-visible product surfaces through routes
and the left navigation
> - The Work Timeline frontend landed on `master` in related PR #8880
before the product surface was ready for general users
> - The backend aggregation endpoint and shared DTOs can remain
available for continued iteration without exposing the page in the main
app
> - This pull request removes the visible `/timeline` route, sidebar
entry, page implementation, chart component, and Storybook fixtures
> - The benefit is that users no longer see an unfinished Work Timeline
page on `master`, while future work can continue from a preserved
branch/workspace
## Linked Issues or Issue Description
No public GitHub issue exists for this rollback request.
Bug-style description:
- **What happened:** A Work Timeline page was exposed in the app
navigation before the surface was ready for users.
- **Expected behavior:** Unready product work should not be visible from
the default app shell on `master`.
- **Steps to reproduce:** Open the app on current `master`; the sidebar
includes a `Timeline` item that routes to `/timeline`.
- **Paperclip version/commit:**
`60f7fb422394c94618b8eef27ae032702004f544`.
- **Deployment mode:** Local app / standard Paperclip app shell.
- **Related public PR:** #8880.
## What Changed
- Removed the `/timeline` route and `Timeline` page import from the app
route table.
- Removed the `Timeline` sidebar item and unused `GanttChartSquare` icon
import.
- Deleted the frontend Work Timeline API wrapper, chart component,
layout helper, page, Storybook story, and sample fixtures.
- Removed the now-unused `queryKeys.workTimeline` entry.
- Left the backend timeline endpoint and shared DTOs intact so the data
contract can continue to be developed off the preserved work.
## Verification
- `pnpm --filter @paperclipai/ui typecheck`
- Searched the frontend for stale `workTimeline`, `WorkTimeline`,
`/timeline` route/sidebar, and `GanttChartSquare` references after
deletion.
## Risks
- Low runtime risk: this removes an app route and navigation entry for
an unfinished surface.
- Deep links to `/timeline` will now fall through to the app's existing
not-found behavior.
- The backend endpoint remains available; if the intent was to remove
the API too, that should be handled in a separate, explicit PR.
## Model Used
OpenAI Codex coding agent, GPT-5-based model, with shell/tool use for
repository inspection, code editing, git, and GitHub CLI operations.
## 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
- [ ] I will address all Greptile and reviewer comments before
requesting merge
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - Operators need to *see* how work actually flowed across their agents
over time — who was invoked, what they worked on, and how work was
delegated between them
> - The dashboard shows point-in-time state but nothing reconstructs the
temporal, cross-actor picture of heartbeat runs and delegations
> - A read-only company work-timeline endpoint was landed first (server
aggregation over runs/issues/activity); it had no frontend
> - This pull request adds the Gantt-style **Work Timeline** page that
renders that endpoint, plus the small additive server contract change it
needs (shared DTOs + a task title on each span)
> - The benefit is a single dense view — actor rows, concurrency lanes,
delegation connectors, zoom and a mini-map — that makes agent activity
legible without an N+1 fetch storm from the client
## Linked Issues or Issue Description
No public GitHub issue. Problem, in-PR:
- **Gap:** the company work-timeline aggregation endpoint has no UI.
There is no way to visually inspect how heartbeat runs unfolded over
time or how work was delegated between agents.
- **Solution:** a dashboard-adjacent Gantt-style page at
`/:companyPrefix/timeline`, linked from the sidebar's "Work" section,
rendering runs as bars on per-actor rows with delegation connectors,
kickoff chips, zoom, a lens filter, and a mini-map.
- Built with React + custom inline SVG (no chart dependency; consistent
with the existing Tailwind/Radix stack).
## What Changed
- **Frontend Gantt page** (`ui/src/pages/Timeline.tsx`,
`ui/src/components/timeline/WorkTimelineChart.tsx`): actor rows
(agents/system only — humans never get a row), overlapping runs packed
into concurrency sub-lanes, bars = heartbeat runs with a left colour tab
for issue identity, truncated task title + timing/status on hover,
click-through to the task.
- **Human activity markers & human rows** for kickoff/delegation
involving people, without giving humans their own run lane.
- **Kickoff avatar chips** at each bar's leading edge; straight
agent→agent delegation connectors (dashed for
retries/changes-requested); in-progress runs extend to a dashed "now"
line and fade out.
- **Zoom** (hour/day/week, auto-fit), full-window **mini-map** with a
draggable brush, **lens filter** (Everyone / per-user, server-side), and
colour **by task / by status**.
- **Pure layout/transform module** (`ui/src/lib/timeline/layout.ts`) —
packing, kickoff derivation, connector resolution, scales — unit-tested
in isolation.
- **Server contract (additive):** moved the `WorkTimeline*` DTOs into
`@paperclipai/shared` so the aggregation service and the UI consume one
contract; added `issueTitle` to each span so the tooltip shows the task
title with no N+1 client fetch.
- Sidebar link, query keys, API client (`ui/src/api/workTimeline.ts`),
and a Storybook story with fixtures.
## Verification
- `pnpm --filter @paperclipai/shared build` ✅
- `pnpm --filter @paperclipai/server typecheck` ✅ · `pnpm --filter
@paperclipai/ui typecheck` ✅
- `pnpm --filter @paperclipai/ui exec vitest run
src/lib/timeline/layout.test.ts
src/components/timeline/WorkTimelineChart.test.tsx` ✅ (15/15)
- `pnpm --filter @paperclipai/server exec vitest run
src/__tests__/work-timeline-service.test.ts` ✅ (5/5) — the DTO move +
`issueTitle` are additive; existing service tests use `objectContaining`
and still pass.
- Rendered `WorkTimelineChart` headless against a real slice of company
activity via a Storybook story; manual browser QA of the live page
passed on the feature branch.
## Risks
- **Low risk.** The change is UI-only plus an additive server DTO
refactor (types relocated to `@paperclipai/shared`, one new optional
field). No schema/migration changes, no change to endpoint behaviour
beyond the extra `issueTitle` field. The page is behind its own route
and does not alter existing views.
## Model Used
- Claude, Opus 4.8 (`claude-opus-4-8`), via Claude Code with extended
thinking and 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 (only the merged endpoint PR #8875 is related; no duplicates)
- [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
ticket id
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [ ] All Paperclip CI gates are green
- [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - The inbox is where operators quickly scan which issues are active,
blocked, or waiting for attention
> - A blocked parent can still have active descendant work, but the
inbox previously depended on only loaded rows to infer that state
> - That made collapsed or partially loaded issue trees look more stuck
than they really were
> - This pull request carries live descendant summary data through the
issue list API and inbox UI
> - The benefit is a more accurate blocked-inbox signal, so operators
can distinguish truly stalled work from blocked parents that still have
live child activity
## Linked Issues or Issue Description
No public GitHub issue was found for this exact inbox descendant-status
polish.
Feature request fields:
**Subsystem affected**
Cross-cutting: `server/`, `packages/shared`, plugin/MCP API surfaces,
and `ui/` inbox rendering.
**Problem or motivation**
Inbox rows need to show when blocked or collapsed parents still have
live descendant work, even when the live child row is not loaded in the
current client tree. Without a server-provided descendant summary, a
parent can look stalled even though active work continues below it.
**Proposed solution**
Expose an optional live descendant count on issue list results, request
it from inbox views, and use it to render covered blocked status and
live-below indicators. Keep the field opt-in so other issue list callers
keep their existing payload shape and query cost.
**Alternatives considered**
Relying only on client-loaded subtree state was ruled out because it
misses collapsed or unloaded descendants. Always returning the count was
also avoided because most list callers do not need this extra summary.
**Roadmap alignment**
This is scoped operator-visibility polish for the existing inbox. It
does not duplicate a named `ROADMAP.md` milestone.
**Additional context**
The recursive summary query is guarded against parent cycles, and the UI
still falls back to loaded subtree live counts when server summary data
is absent or stale.
## What Changed
- Added optional `includeLiveDescendantSummary` support to issue list
contracts, SDK surfaces, MCP tools, routes, services, and tests.
- Added `liveDescendantCount` to issue list results when requested.
- Updated inbox and blocked-inbox queries to request live descendant
summaries.
- Updated inbox row status rendering so blocked parents with live
descendants show covered blocker treatment without duplicating the
live-below chip.
- Hardened live descendant summary traversal against parent cycles and
preserved the loaded-subtree fallback path for blocked inbox rows.
- Added focused tests for the API parameter, service behavior, helper
logic, cycle handling, and inbox UI query/rendering behavior.
## Verification
- `pnpm exec vitest run
server/src/__tests__/issue-list-assignee-filter-routes.test.ts
ui/src/lib/inbox-live-descendants.test.ts
ui/src/components/IssueColumns.test.tsx
ui/src/components/BlockedInboxView.test.tsx ui/src/pages/Inbox.test.tsx`
- `pnpm --filter @paperclipai/ui typecheck`
- Rebased cleanly onto current upstream `master` before pushing.
- Confirmed the branch diff does not include `pnpm-lock.yaml` or
`.github/workflows/*` changes.
## Risks
Low to moderate risk. The new descendant count is opt-in on list
requests, but it adds query work when the inbox asks for it. The
recursive traversal now tracks visited ancestors to avoid cycle
failures. The UI uses the server count as a supplement to existing
loaded-tree state, so stale or absent counts fall back to the prior
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, GPT-5 coding agent, tool-enabled with local shell and git
access. Reasoning mode and context window are managed by the
Paperclip/Codex runtime.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - Environments are now managed as instance-level runtime resources
rather than per-company rows
> - The custom environment image setup tables were introduced with their
own `company_id` columns and route query parameters
> - That split made one saved environment image state depend on an extra
company context even though the environment itself is the durable owner
> - It also made saved-environment probes harder because applying the
active custom image template could require a company context when no
secret-backed config needed one
> - This pull request scopes custom image templates and setup sessions
directly to the saved environment
> - The benefit is that reusable environment images follow the same
instance-scoped model as environments while secret resolution still uses
company context only when secrets require it
## Linked Issues or Issue Description
No matching public GitHub issue was found.
Bug report:
### What happened?
saved environment custom-image routes and persistence required a
`companyId` even though environments are instance-scoped, and saved
sandbox probes did not opt into active custom-image template application
unless a company context was present.
### Expected behavior
custom-image templates and setup sessions should be owned by the saved
environment, and saved sandbox probes should apply the active template
while still requiring a company context only for secret-backed runtime
config.
### Steps to reproduce
1. Configure an instance-scoped sandbox environment with custom-image
setup support.
2. Start or inspect a custom-image session or template for that saved
environment.
3. Probe the saved environment without a custom-image-specific
`companyId` query parameter.
### Paperclip version or commit
current `master` after the environment custom-image template migration.
### Deployment mode
Local dev (pnpm dev) or authenticated local Paperclip instance.
### Installation method
Built from source (pnpm dev / pnpm build).
### Agent adapter(s) involved
Not adapter-specific (core bug).
### Database mode
Embedded PGlite/Postgres dev database.
### Access context
Board human operator.
### Privacy checklist
No logs, secrets, tokens, private URLs, or local machine paths are
included.
Duplicate search performed:
- `gh search prs "environment custom image companyId
repo:paperclipai/paperclip" --state open --limit 20`
- `gh search prs "custom image environment scoped
repo:paperclipai/paperclip" --state open --limit 20`
- `gh search issues "environment custom image
repo:paperclipai/paperclip" --state open --limit 20`
The returned results were unrelated adapter, Docker, auth, or
stale-workspace items.
## What Changed
- Removed redundant `company_id` columns from environment custom-image
templates and setup sessions.
- Added migration `0127_environment_custom_images_instance_scoped` to
collapse duplicate active rows per environment before dropping the old
company-scoped indexes/columns.
- Updated custom-image services, route handlers, shared validators, and
UI API/query keys to use environment-scoped custom-image state.
- Kept runtime secret resolution company-aware only when secret refs or
bindings require a company context.
- Made saved sandbox environment probes opt into active custom-image
template application.
- Updated DB, shared, server, and UI tests for the new
environment-scoped contract.
## Verification
- `pnpm --filter @paperclipai/db run check:migrations`
- `pnpm exec vitest run
packages/db/src/environment-custom-images-schema.test.ts
packages/shared/src/environment-custom-images.test.ts
server/src/__tests__/environment-custom-image-routes.test.ts
server/src/__tests__/environment-custom-images-service.test.ts
server/src/__tests__/environment-routes.test.ts
ui/src/pages/CompanyEnvironments.test.tsx`
- `pnpm -r typecheck`
- `pnpm test:run` before rebasing onto latest `master`; after the rebase
only the migration number changed, and the migration check plus focused
suite, typecheck, and build were rerun.
- `pnpm build`
## Risks
- Migration safety: the migration supersedes duplicate active templates
per environment and fails duplicate active setup sessions before adding
environment-only unique indexes. Operators with duplicate historical
active rows should review which active template is kept.
- Behavior shift: plugin custom-image setup calls now receive
`companyId: "instance"` when no secret binding determines a concrete
company context.
- Secret-backed configs still require an explicit or uniquely inferable
company context; environments with secret bindings spread across
multiple companies continue to fail fast.
> 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 via the `codex_local` adapter, GPT-5-based coding model
with tool-enabled repository inspection, editing, testing, git, and
GitHub CLI access. Exact context-window metadata was not exposed by the
runtime.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [ ] 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>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - Task/issue threads render each comment as a chat bubble; the author
determines whether it shows as a left-aligned agent bubble (name + icon)
or a right-aligned "Board" bubble
> - Comments posted by an agent from a local execution environment are
written with a non-human author id (`local-board`/system), so they were
mis-rendered as blue "Board" bubbles instead of being attributed to the
authoring agent
> - This misattribution is confusing (it looks like the human board said
something an agent actually said) and it can drive false
wake/reconciliation behavior on the affected threads
> - This pull request adds server-side attribution derivation (lossless
run-id join first, then an explicit run-log post marker), persists the
derived agent so the read path stops re-scanning run logs, and stops the
client from labeling agent-derived comments "Board"
> - The benefit is agent comments render as the correct agent, genuine
human board comments are never reattributed, and reads get cheaper after
a one-time persistence
## Linked Issues or Issue Description
<!-- No public GitHub issue — describing the problem in-PR (bug report
shape). -->
**What happened?**
In a task/issue comment thread, comments authored by an agent from a
local execution environment are stored with a non-human author id
(`local-board`/system). The UI renders these as right-aligned blue
"Board" bubbles, implying a human board member authored them. The
mislabeling is also a wake/reconciliation hazard: an agent comment that
reads as "Board" can look like human board input.
**Expected behavior**
Such comments should render as the authoring agent (left-aligned bubble
with agent name + icon). Genuine human/board comments must continue to
render as "Board" and must never be reattributed to an agent.
**Steps to reproduce**
1. Have an agent post a comment on an issue from a local execution
environment (author id `local-board`).
2. Open the issue comment thread in the UI.
3. Observe the agent's comment rendered as a right-aligned blue "Board"
bubble instead of the authoring agent.
**Root cause**
The read path did not resolve the authoring agent for these comments,
and the client fell back to a "Board" label for the `local-board`
author.
## What Changed
- **Server derivation (`server/src/services/issues.ts`):**
- Resolve the authoring agent from the comment's run id first
(`createdByRunId`/`derivedCreatedByRunId` → `heartbeatRuns.agentId`) —
lossless when present.
- Second tier `run_log_comment_post`: read the run log lazily (only for
still-unresolved comments) to match the explicit `comment id:` post
marker.
- **Guard:** never reattribute a comment whose author maps to a genuine
user profile. Only the non-human sentinel (`local-board`, which is
itself a `user` row) and authors absent from the `user` table are
eligible.
- Pure timing-overlap tiers are intentionally **not** used (Option A) —
an agent comment and a human board comment posted during the same run
are indistinguishable rows, so any timing guess risks mislabeling a real
human comment.
- **Persistence
(`packages/db/src/migrations/0126_issue_comment_derived_attribution.sql`,
`packages/db/src/schema/issue_comments.ts`):** add stored `derived_*`
attribution columns and write the resolved agent back with a single bulk
`UPDATE ... FROM (VALUES ...)`, so reads stop recomputing from run logs.
Migration is additive (new nullable columns) with a batched, idempotent
backfill of the lossless run-id tier over historical rows.
- **Types (`packages/shared/src/types/issue.ts`):** expose the persisted
attribution fields and the `IssueCommentDerivedAuthorSource` union.
- **Client (`ui/src/lib/issue-chat-messages.test.ts`):** the message
builder already prefers a resolved agent id (`authorAgentId ??
runAgentId ?? derivedAuthorAgentId`), so once the server persists the
derived agent the bubble renders as the agent automatically — no client
code change needed. Adds a regression guard confirming a genuine board
comment with no derived agent is still rendered as "Board".
- **Tests:** derivation + message-building tests, including assertions
that genuine board/user comments are **not** reattributed.
## Verification
- `cd server && npx vitest run issues-service` — 94 tests pass: run-id
resolution, no-attribution on timing overlap alone (Option A), multi-run
ambiguity, same-agent multi-run, and the genuine-user guard. Exercises
the real persistence path (bulk UPDATE) against the test DB.
- `cd ui && npx vitest run issue-chat-messages` — 27 tests pass; client
no longer labels agent-derived comments "Board", and a genuine board
comment with no derived agent is not re-labeled.
- `cd server && npm run typecheck` — passes (exit 0).
- Manual: on a thread containing old agent-authored comments, the blue
"Board" bubbles render as the authoring agent; a genuine board comment
on the same thread still renders as "Board".
## Risks
- **Mis-reattributing a genuine board comment made during an agent run**
→ mitigated by the human-profile guard (only `local-board`/system
authors are eligible) and by dropping pure timing tiers (Option A): only
the lossless run-id join and the explicit run-log post marker attribute
history.
- **Backfill volume / run-log reads** → the migration backfill is
batched (5000 rows/loop) and results are persisted so reads stop
recomputing; the read-path persistence is a single bulk UPDATE rather
than per-comment round-trips. Migration adds only nullable columns (no
destructive change).
- The persistence/backfill has **not** been run against any production
database as part of opening this PR.
## Model Used
Claude Opus 4.8 (`claude-opus-4-8`), extended thinking, via Claude Code
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 — related open
PRs (#6006 narrow attribution run scan, #4729 attribution roll-up, #7014
reaped-run attribution) address different attribution paths; none fix
the `local-board` "Board" bubble rendering this PR targets. Supersedes
#8832 (same change; branch renamed to drop an internal ticket id per
CONTRIBUTING → Branch Naming)
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change and contains no internal
Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
## Thinking Path
Two interactive UI controls were missing WAI-ARIA attributes, so
screen-reader users couldn't perceive their state. The IssuesList
view-mode toggle already had `title` tooltips but no
`aria-label`/`aria-pressed` and its container had no `role="group"`; the
GoalTree expand/collapse chevron announced only "button". Approach:
attributes only, no logic/render changes, reusing the pattern already
shipped on the Agents page toggle. Per review feedback, GoalTree uses a
**stable** `aria-label` (`` `${goal.title} subtree` ``) with
`aria-expanded` for state, rather than a dynamic label that
double-announces state.
## Issue
_No existing tracking issue — described inline per CONTRIBUTING.md →
"Link Issues or Describe Them In-PR"._
**What happened**
Two components expose buttons with no accessible name or state, making
them unusable via screen reader: (1) the `IssuesList` view-mode toggle
doesn't convey which view is active; (2) the `GoalTree` expand/collapse
chevrons have no name and no expanded/collapsed state.
**Expected behavior**
Both controls announce their purpose and current state to assistive
technology.
**Steps to reproduce**
Enable VoiceOver, open the Issues page and Tab to the view-mode toggle,
then open the Goals page with nested goals and Tab to a tree chevron —
each control announces only "button", with no name and no
pressed/expanded state.
## What Changed
**`ui/src/components/IssuesList.tsx`** — `role="group"` +
`aria-label="View mode"` on the container; `aria-label` ("List
view"/"Board view") and `aria-pressed` on each button.
**`ui/src/components/GoalTree.tsx`** — stable `aria-label` (``
`${goal.title} subtree` ``) and `aria-expanded` on the chevron button.
2 files, ARIA attributes only, no behavioral change.
## Verification
1. Issues page → toggle announces "List view, pressed" / "Board view,
not pressed", grouped as "View mode".
2. Goals page with nested goals → each chevron announces "<goal title>
subtree" with expanded/collapsed state.
3. Manual VoiceOver pass; no visual/behavioral change for sighted users.
## Risks
Minimal — additive HTML attributes with no impact on logic, rendering,
or state. Worst case is a suboptimal announcement string, trivially
adjusted.
## Model Used
Original change human-authored by @bluzername. Two follow-up commits
(stable `aria-label` refinement; removal of a stray tooling file)
applied via maintainer edit; the refinement was drafted with Claude Opus
4.8.
## Checklist
- [x] I searched the GitHub PR list (open + recently closed) for
similar/duplicate PRs before opening — none found.
---------
Co-authored-by: Andrew Aymeloglu <aaymeloglu@gmail.com>
## Thinking Path
> - Paperclip orchestrates ai-agents for zero-human companies
> - But humans want to watch the agents and oversee their work
> - Human users interact with the dashboard UI to monitor costs,
schedules, and agent behavior
> - The UI has toggle button groups where you click one option and other
become unselected (like date range presets, day-of-week selectors)
> - Visually the active button look different (filled vs outlined), but
screen reader users hear no difference - all buttons sound same
> - The `aria-pressed` attribute is what screen readers need to announce
which toggle is active and which is not
> - I searched entire UI codebase for `aria-pressed` and found zero
usage anywhere
> - This pull request adds `aria-pressed={isActive}` to two toggle
button groups: date range presets in Costs page and day-of-week selector
in ScheduleEditor
> - Now screen reader users can tell which button is currently selected
without relying on visual styling only
## Problem
The app has button groups that work like toggles - you click one button
to select it and the others become unselected. Visually this work fine
because the active button change to a different variant (filled vs
outlined). But for screen reader users, ALL buttons sound exactly the
same - just "button, 7 days", "button, 30 days", etc with no way to tell
which one is currently active.
I searched the entire UI codebase for `aria-pressed` and found zero
results. This attribute is what screen readers need to announce "7 days,
pressed" vs "30 days, not pressed" for toggle button groups.
## What I changed
Added `aria-pressed={isActive}` to two toggle button groups:
1. **Costs.tsx** - Date range preset buttons (7d, 30d, 90d, Custom).
Screen reader now announce which date range is selected.
2. **ScheduleEditor.tsx** - Day of week selector (Mon, Tue, Wed...).
Screen reader now announce which day is selected for weekly schedule.
## How to test
1. Go to Costs page, use VoiceOver (Cmd+F5 on Mac)
2. Tab through the date preset buttons
3. Active button should announce "pressed", others "not pressed"
4. Same for ScheduleEditor - create/edit trigger with weekly preset, tab
through day buttons
2 files, 2 lines added.
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - Issue-thread interactions are how agents ask board users for typed
decisions and structured answers inside an issue thread
> - Confirmation interactions already become stale when a later
board/user comment supersedes the pending decision
> - Question interactions had the same workflow risk, because a
board/user could answer in a comment while the old question card stayed
pending
> - This pull request extends the supersede-by-comment lifecycle to
ask-user-question interactions and makes that status visible in the UI
> - The benefit is agents get a clear continuation signal and users do
not see stale question forms after the discussion has moved on
## Linked Issues or Issue Description
No exact public GitHub issue was found.
Bug report:
**Pre-submission checklist**
- [x] I have searched existing open and closed issues and this is not a
duplicate.
- [x] I am on the latest released version of Paperclip, or can reproduce
on `master`.
- [x] I have confirmed the error originates in Paperclip itself, not in
an agent adapter, API provider, or local configuration.
**What happened?**
Pending `ask_user_questions` interactions could remain open after a
later board/user comment changed or answered the request in-thread. That
left a stale form visible and kept the interaction in a pending state
even though the discussion had moved on.
**Expected behavior**
Question interactions should follow the same default
supersede-on-comment behavior as confirmation interactions, with an
explicit expired result that points to the superseding comment.
**Steps to reproduce**
1. Create an `ask_user_questions` interaction on an issue.
2. Add a board/user comment created at or after that interaction.
3. Observe that before this change, the question interaction stayed
pending instead of expiring as superseded by the comment.
**Paperclip version or commit**
Current `master` before this PR.
**Deployment mode**
Self-hosted server or local dev. The bug is in shared issue-thread
interaction lifecycle handling.
**Installation method**
Built from source.
**Agent adapter(s) involved**
Not adapter-specific. This is a core issue-thread interaction bug.
**Database mode**
Applies to the normal Paperclip database-backed interaction lifecycle.
**Access context**
Board user comments supersede agent-created questions.
**Relevant logs or output**
No crash output. The stale pending interaction was visible in the issue
thread state.
**Relevant config (if applicable)**
None.
**Additional context**
Confirmation-style interactions already supported this stale-by-comment
behavior. This PR brings question interactions into the same lifecycle
model.
**Privacy checklist**
- [x] I have reviewed all pasted output for PII and included no private
instance links, local ticket ids, secrets, logs, or screenshots.
## What Changed
- Added `supersedeOnUserComment` support to `ask_user_questions`
payloads, defaulting it to `true` during interaction creation.
- Expire pending question interactions when a later board/user comment
supersedes them, including a result with `expirationReason:
"superseded_by_comment"` and the superseding `commentId`.
- Updated interaction summaries and cards so expired question requests
show a clear amber state with a jump link to the comment and correct
singular/plural copy.
- Updated agent onboarding guidance to describe the new default and how
to opt out.
- Added shared, server, and UI test coverage for the new lifecycle
behavior.
## Verification
- `pnpm exec vitest run
packages/shared/src/issue-thread-interactions.test.ts
server/src/__tests__/issue-thread-interaction-routes.test.ts
server/src/__tests__/issue-thread-interactions-service.test.ts
ui/src/components/IssueThreadInteractionCard.test.tsx
ui/src/lib/issue-thread-interactions.test.ts --reporter=dot` passed: 5
files, 73 tests.
- `pnpm --filter @paperclipai/shared typecheck && pnpm --filter
@paperclipai/server typecheck && pnpm --filter @paperclipai/ui
typecheck` passed.
- `pnpm exec vitest run
server/src/__tests__/issue-thread-interactions-service.test.ts
--reporter=dot && pnpm --filter @paperclipai/server typecheck` passed
after the final type-safety cleanup.
- Confirmed the branch is rebased on current `origin/master`.
- Confirmed the diff does not touch `pnpm-lock.yaml`,
`.github/workflows`, or database migrations.
## Risks
- Low-to-medium risk: `ask_user_questions` now defaults to expiring
after later board/user comments. Existing callers that need questions to
stay open through discussion can set `supersedeOnUserComment: false`.
- Expired question interactions store an empty `answers` array, so
downstream consumers should treat the explicit `expirationReason` as the
meaningful outcome.
> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.
## Model Used
OpenAI Codex, GPT-5-based coding agent in Paperclip CodexCoder runtime,
with terminal and repository tool use. Exact context window is not
exposed in this runtime.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have 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>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - Agent runs are assembled by the heartbeat service from agent config,
project workspaces, environment config, secret bindings, skills, and
runtime session state.
> - The heartbeat service intentionally reuses adapter sessions,
execution workspaces, and sandbox leases when that preserves useful
state.
> - Reuse becomes incorrect when the effective next-run config changes
after a saved session, workspace, or lease was created.
> - Stale reuse can make a later run appear pinned to old agent,
environment, secret, instruction, or workspace settings.
> - This pull request records non-sensitive fingerprints for the
effective session, workspace, and lease config at run boundaries.
> - When those fingerprints drift, Paperclip refreshes persisted runtime
config or starts fresh execution instead of reusing stale state.
> - The benefit is predictable next-run config freshness without storing
raw secret values, full env maps, provider credentials, or private path
details.
## Linked Issues or Issue Description
- Refs #8058
- Related PRs checked during dedup search: #4968, #4155, #84, #8480.
These cover nearby workspace/session routing or model-config freshness
areas, but do not duplicate this effective run config fingerprinting
path.
## What Changed
- Added effective run config fingerprinting for session, workspace, and
lease reuse decisions, with canonicalization that ignores generated
runtime noise and redacts sensitive values.
- Updated heartbeat reuse logic to compare stored and next-run
fingerprints, reset stale saved sessions, refresh persisted workspace
config snapshots, replace stale reused workspaces when required, and
avoid stale sandbox lease reuse.
- Included plain environment value drift via value hashes, without
storing the raw env values.
- Root-bound instruction content hashing so legacy direct absolute
instruction paths are represented but not read for config fingerprints.
- Batched secret/version metadata lookups for environment lease
fingerprinting.
- Added workspace operation/run result freshness metadata so operators
can inspect non-sensitive decision categories.
- Surfaced config freshness labels and next-run copy in the UI and docs.
- Added focused coverage for fingerprint redaction, session reset
decisions, workspace refresh/replace behavior, environment lease drift,
and persisted workspace restoration.
## Verification
- `git diff --check`
- Sensitive-data scan before push:
- `git diff --unified=0 origin/master...HEAD | rg -n --pcre2
"(AWS_ACCESS_KEY_ID|AWS_SECRET_ACCESS_KEY|ghp_[A-Za-z0-9_]{20,}|github_pat_[A-Za-z0-9_]{20,}|sk-[A-Za-z0-9]{20,}|-----BEGIN
(RSA |OPENSSH |EC |DSA )?PRIVATE KEY-----|AKIA[0-9A-Z]{16})"`
- `git diff --unified=0 origin/master...HEAD | rg -n --pcre2
"[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}"`
- `pnpm exec vitest run
server/src/__tests__/effective-run-config-fingerprints.test.ts
server/src/__tests__/heartbeat-workspace-session.test.ts
server/src/__tests__/environment-runtime.test.ts`
- `pnpm --filter @paperclipai/server typecheck`
- `pnpm -r typecheck`
- `pnpm --filter @paperclipai/db clean`
- `pnpm test:run`
- `pnpm build`
- UI screenshots from Cutter:
-
https://artifacts.cutter.sh/8797/run-2f4827c-2026-06-30T18-57-25/preview/change-01.png
-
https://artifacts.cutter.sh/8797/run-2f4827c-2026-06-30T18-57-25/preview/change-02.png
-
https://artifacts.cutter.sh/8797/run-2f4827c-2026-06-30T18-57-25/preview/change-03.png
## Risks
- Medium: overly broad fingerprints could start fresh sessions,
workspaces, or sandbox leases more often than necessary.
- Medium: missing a config category would allow stale reuse to persist
for that category.
- Medium: legacy direct absolute instruction paths are no longer
content-hashed unless they are paired with an absolute managed
instructions root.
- Low data risk: fingerprint metadata stores hashes and category names,
not raw secrets, raw env values, provider credentials, or private path
details.
> 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 GPT-5 via Codex CLI / Codex coding agent, tool-enabled with
shell, Git, GitHub CLI, local test execution, and code editing. The
exact deployed model variant and context window are not exposed by 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: Cody <cody@paperclip.ing>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - A growing part of that work runs in sandboxed environments rather
than on the operator's local machine.
> - Today sandbox providers can start fresh workspaces and run probes,
but they do not have a shared contract for capturing and reusing
prepared sandbox state.
> - Operators need a way to set up tools, credentials, and project
dependencies once, then reuse that prepared image for later agent runs.
> - This pull request adds reusable sandbox custom images across the
provider contract, server runtime, and board UI.
> - It also keeps probes and sandbox copy flows aligned with
pre-authenticated/custom-image environments.
> - The benefit is faster, more reliable sandbox runs without repeatedly
rebuilding the same environment setup.
## Linked Issues or Issue Description
No public GitHub issue was found for this change. Inline feature request
follows.
### Problem or motivation
Sandboxed agents need reusable prepared runtime state so repeated runs
do not require manual setup every time. Operators often need system
packages, CLIs, SDKs, dependency caches, credentials, and project
tooling available before an agent can work productively.
### Proposed solution
Add a provider-level custom-image capability, server-side setup/capture
lifecycle, Daytona/fake provider support, and board UI controls for
creating, testing, selecting, and deleting custom images.
### Alternatives considered
Leaving this as provider-specific setup outside Paperclip would keep the
control plane blind to image state and would not give agents consistent
environment metadata. Re-running setup commands for every lease is
simpler, but slower and less reliable for interactive or credentialed
setup.
### Roadmap alignment
Checked `ROADMAP.md`; this aligns with the Cloud / Sandbox agents
roadmap area and does not duplicate any related public issue or PR found
by search.
Additional context:
- Subsystem affected: cross-cutting (`packages/db`, `packages/shared`,
`packages/plugins`, `server`, `ui`).
- Duplicate search: searched GitHub for `sandbox custom image` and
`sandbox template environment`; no related public issues or PRs were
found.
## What Changed
- Added custom-image shared types, validators, constants, API paths, and
database schema/migration.
- Added server services/routes for custom-image templates and setup
sessions, including runtime cleanup and provider metadata handling.
- Extended plugin/sandbox provider capabilities for interactive setup,
template capture, and template deletion.
- Implemented custom-image support in the fake sandbox provider and
Daytona provider.
- Updated environment runtime/config handling so active custom images
flow into leases, probes, and agent execution.
- Added board UI controls and API client support for custom-image setup,
capture, selection, status, and error states.
- Hardened sandbox copy/probe behavior for insecure clipboard contexts
and pre-authenticated sandbox images.
- Added targeted coverage across shared validators, DB schema, server
routes/services, provider plugins, adapter probes, and UI flows.
## Verification
- `pnpm install --frozen-lockfile --ignore-scripts`
- `pnpm vitest run
packages/adapters/claude-local/src/server/test.probe.test.ts
packages/adapters/claude-local/src/server/test.ts
packages/adapters/codex-local/src/server/test.remote.test.ts
packages/adapters/codex-local/src/server/test.ts`
- `pnpm --filter @paperclipai/adapter-claude-local typecheck`
- `pnpm --filter @paperclipai/adapter-codex-local typecheck`
- `pnpm vitest run
packages/db/src/environment-custom-images-schema.test.ts
packages/shared/src/environment-custom-images.test.ts
packages/shared/src/validators/plugin.test.ts
server/src/__tests__/environment-custom-images-service.test.ts
server/src/__tests__/workspace-runtime.test.ts
packages/plugins/sandbox-providers/daytona/src/plugin.test.ts
ui/src/pages/CompanyEnvironments.test.tsx
ui/src/pages/CompanySettings.test.tsx`
- `pnpm -r typecheck`
- `pnpm build`
- `rm -rf packages/db/dist && pnpm test:run`
- Public-safety scan of the final diff found no internal Paperclip issue
links, private instance URLs, or real secret patterns.
## Risks
- Adds a database migration and new environment runtime tables, so
migration ordering and rollback need care.
- Provider implementations may differ in how reliably they can
capture/delete images; unsupported providers surface capability-gated UI
states.
- Custom-image state can contain operator-prepared tooling and
credentials inside the provider image, so providers must enforce their
own access controls and cleanup semantics.
- Broad surface area across shared contracts, server runtime, plugins,
adapters, and UI means CI and Greptile review should be watched closely.
> 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 GPT-5 (`gpt-5`) via Codex CLI with tool use and code execution.
Assisted with branch cleanup, conflict resolution, local verification,
and PR preparation. Earlier branch implementation work was assisted by
Paperclip-managed Claude/Codex 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
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: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - CI runs a set of required PR checks; the Canary Dry Run job is the
slowest (~4m)
> - Its dominant cost is `release.sh` Step 2 `pnpm build` (~133s), whose
critical path is the `ui` package
> - In `ui`, `tsc -b` runs ~46s *before* `vite build` (~20s), but
`ui/tsconfig.json` sets `noEmit: true`, so `tsc -b` is a pure type-check
that emits nothing — vite produces all artifacts
> - The PR workflow already runs a parallel Typecheck job
(`typecheck:build-gaps`) that auto-type-checks any workspace package
whose build script omits `tsc`
> - This pull request drops `tsc -b` from the `ui` build so the
type-check moves off the build/release critical path onto the slack-rich
Typecheck job
> - The benefit is ~46s shaved off the slowest PR check with no loss of
type coverage
## What Changed
- `ui/package.json`: `build` script changed from `tsc -b && vite build`
to `vite build`.
- Type coverage is preserved automatically:
`scripts/run-typecheck-build-gaps.mjs` already type-checks any workspace
whose `build` script omits `tsc` and defines a `typecheck` script. With
this change, `@paperclipai/ui` (whose `typecheck` is `tsc -b`) is now
picked up by the parallel Typecheck job. No script edit was required —
the gap-detection is generic.
## Verification
- `pnpm --filter @paperclipai/ui build` produces `ui/dist` via vite
alone (no `tsc` invocation).
- `pnpm typecheck:build-gaps` now type-checks `@paperclipai/ui` and
exits 0.
- A type error in `ui` still fails the parallel Typecheck job, so
type-error coverage is unchanged.
## Risks
- Low. The type-check is relocated, not removed. This is build-tooling
only — no migration, no runtime behavior change. If
`scripts/run-typecheck-build-gaps.mjs` ever stopped covering `ui`, a
type error could slip past the build job — but its gap-detection is
generic (any package with a tsc-less build plus a `typecheck` script),
and `ui` qualifies.
## Model Used
Claude (Anthropic), `claude-opus-4` family, extended-thinking mode with
tool use, operating as an automated CI-health agent.
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - The issue detail surface includes an Issue Properties pane that
shows live metadata for a selected issue.
> - Watchdog configuration can be changed from that pane, but cached
issue detail data also feeds the pane key and rerender behavior.
> - If the cache does not reflect watchdog changes immediately, the
properties pane can keep stale watchdog state until a later refetch
completes.
> - This pull request updates the watchdog save/delete paths to write
the returned watchdog state into cached issue detail data.
> - It also includes watchdog fields in the issue properties panel key
so watchdog configuration changes invalidate the memoized panel state.
> - The benefit is a properties pane that updates consistently after
operators set or remove a watchdog.
## Linked Issues or Issue Description
Refs #8789
This PR fixes stale watchdog state in the Issue Properties pane after
watchdog save/delete mutations.
## What Changed
- Write the saved watchdog summary into cached issue detail data after a
successful watchdog upsert using the exact
`queryKeys.issues.detail(issue.id)` key.
- Clear cached issue detail watchdog data after a successful watchdog
delete using the exact `queryKeys.issues.detail(issue.id)` key.
- Include stable watchdog fields in `buildIssuePropertiesPanelKey` so
watchdog configuration changes produce a new panel key.
- Add focused tests for watchdog cache updates and panel-key
invalidation, including save and delete cache assertions.
## Verification
- `pnpm --filter @paperclipai/ui exec vitest run
src/components/IssueProperties.test.tsx
src/lib/issue-properties-panel-key.test.ts`
- Result: 2 files passed, 40 tests passed.
## Risks
Low risk. The change is scoped to UI cache handling for issue detail
watchdog state and panel-key generation. The main risk is an unexpected
query-cache shape, covered by focused tests around the existing issue
detail query key.
> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.
## Model Used
OpenAI Codex coding agent running GPT-5-class model with repository tool
use and local command execution. The exact backend model identifier and
context window are not exposed in this runtime.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have 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: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - Issue detail pages and run output cards both render user-facing
attachment previews
> - Media attachments were handled through separate gallery entry
points, so attachments from issue comments and issue output did not
share one consistent viewing path
> - A shared gallery path makes image browsing more predictable across
the issue surface
> - This pull request unifies the issue media attachment gallery
behavior across attachments, output cards, and issue detail rendering
> - The benefit is a more consistent image preview experience with
focused regression coverage around the affected UI paths
## Linked Issues or Issue Description
Refs #8788
This PR fixes inconsistent issue media preview behavior across
attachment and output surfaces.
## What Changed
- Unified issue media attachment gallery behavior across issue
attachments, issue output, output primary cards, and issue detail
rendering.
- Added image gallery modal coverage and regression tests for
attachments, output image handling, keyboard navigation, and issue
detail interactions.
- Preserved the primary video output Open action while still allowing
gallery browsing when a gallery handler is available.
- Updated issue attachment and issue output helper logic to preserve
shared image gallery metadata.
## Verification
- `pnpm --filter @paperclipai/ui exec vitest run
src/components/ImageGalleryModal.test.tsx
src/components/issue-output/IssueOutputSection.test.tsx
src/components/IssueAttachmentsSection.test.tsx
src/pages/IssueDetail.test.tsx`
- Result: 4 files passed, 47 tests passed.
## Risks
Low to medium risk. The change is UI-scoped but touches shared issue
attachment/output rendering paths, so regressions would most likely
appear as image/video preview ordering, missing gallery entries, or
incorrect non-image attachment 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, GPT-5 coding agent session with shell and git/GitHub CLI
tool use. Exact serving revision and context window were not exposed by
the runtime.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have 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
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - Pipeline automations let operators standardize repeated issue and
workflow actions.
> - Pipeline-created issues currently need a way to derive useful titles
from routine variables.
> - Without a configurable title template, automated pipeline output is
harder to scan and distinguish.
> - This pull request adds a title-template field through shared
contracts, server persistence, API routes, and the pipeline settings UI.
> - The benefit is clearer issue titles for pipeline-created work while
preserving the existing pipeline behavior when no template is
configured.
## Linked Issues or Issue Description
Refs #8790
This PR adds configurable generated-issue title templates for pipeline
automations.
## What Changed
- Added `issueTitleTemplate` to the shared pipeline automation contract
and field constants.
- Persisted and returned the title template through pipeline service and
route code.
- Applied title-template rendering when pipeline automations create
issue work.
- Added pipeline settings UI controls for editing the title template and
reusing routine variables.
- Moved title-token cursor restoration out of the React state updater
and into a layout effect.
- Added server and UI coverage for storing, returning, and rendering
pipeline title templates.
## Verification
- `NODE_ENV=test pnpm run preflight:workspace-links && NODE_ENV=test
pnpm exec vitest run server/src/__tests__/pipelines-service.test.ts
server/src/__tests__/pipelines-routes.test.ts
ui/src/pages/PipelineSettings.test.ts`
- Result before review follow-up: 3 files passed, 58 tests passed.
- `pnpm --filter @paperclipai/ui exec vitest run
src/pages/PipelineSettings.test.ts`
- Result after review follow-up: 1 file passed, 7 tests passed.
- Branch was merged with current `paperclipai:master` at `f019f54bb3`
before opening this PR.
- Searched existing PRs for the same head branch and for pipeline
title-template duplicates; no matching existing PR was found.
- Note: GitHub could not open a PR directly from `cryppadotta/paperclip`
because that repository is not a fork of `paperclipai/paperclip`. The
same updated branch SHA was pushed to `paperclipai/paperclip` so this PR
can compare normally against `master`.
## Risks
Low to moderate risk. The change touches pipeline automation persistence
and generated issue creation, so regressions would most likely appear as
missing or incorrectly rendered generated issue titles. Existing
behavior should remain unchanged when `issueTitleTemplate` is unset.
## Model Used
OpenAI Codex, GPT-5-based coding agent, tool-enabled execution in a
Paperclip heartbeat, with repository inspection, Git, GitHub CLI, and
local 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
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - The ⌘K command palette is the primary fast-navigation surface across
tasks and projects
> - When searching, projects were ranked below tasks, so typing a
project name (e.g. "Paperclip") surfaced tasks first and buried matching
projects — often only one project was visible even when several matched
> - Users expect name-matching projects to appear at the top, like other
quick-switchers
> - This pull request promotes matching projects above tasks in the
palette (with a sensible cap) and ensures the project name is always
visible in the row
> - The benefit is faster, more predictable project navigation from ⌘K
## Linked Issues or Issue Description
No public GitHub issue exists for this, so the underlying problem is
described inline below following the [feature request
template](.github/ISSUE_TEMPLATE/feature_request.yml).
**Subsystem affected**
UI — the ⌘K command palette (`ui/src/components/CommandPalette.tsx`).
**Problem or motivation**
When typing in the ⌘K palette, projects were ranked *below* tasks. A
query matching several projects (e.g. "paperclip") surfaced mostly tasks
above the fold and pushed matching projects to the bottom of the list,
so often only one — or zero — matching projects were visible. Users
expect name-matching projects to appear at the top, the way other
quick-switchers behave.
**Proposed solution**
Compute matching projects from the typed query client-side (exact >
prefix > substring > description > fuzzy-subsequence ranking), promote
them into a dedicated "Projects" group above the Tasks group, and cap
both groups so neither crowds the other out. Keep the project name
always visible in each row.
**Alternatives considered**
A dedicated "Projects" search mode/scope toggle — rejected as heavier
and more UI for what is really a ranking problem. A backend
search-ranking change — unnecessary since the project list is already
loaded client-side.
**Roadmap alignment**
Small, self-contained UX improvement to an existing surface; not
roadmap-level core feature work.
## What Changed
- Added a `scoreProjectMatch` + `matchedProjects` memo in
`CommandPalette` that ranks already-loaded projects against the typed
query (exact > prefix > substring > description > fuzzy subsequence,
capped at 5).
- Promoted the matched "Projects" group above the Tasks group, and
reduced the task cap from 10 → 6 when projects are promoted so neither
group crowds the other out. Empty-query preview order is unchanged.
- Fixed the project-name span so the name stays visible in the typeahead
row (the name was being starved to 0px width by a flexible description
span).
## Verification
- `cd ui && npx vitest run CommandPalette` — added test seeds
react-query project data and asserts matching projects are promoted to
the top of the palette, non-matching projects are excluded, and
selecting a promoted project navigates correctly (6/6 passing).
- `pnpm --filter @paperclipai/ui typecheck` — passes.
- Manual (screenshots below): open ⌘K, type "paperclip"; matching
projects appear at the top with names visible.
**Before** — typing "paperclip" surfaces Tasks first; matching projects
are buried below the fold:

**After** — matching projects are promoted into a "Projects" group above
Tasks, names visible:

## Risks
Low risk. UI-only change scoped to the command palette; no backend or
data changes. Worst case is palette ordering for project-name queries.
Ranking is pure and unit-tested; the prefix-score length penalty is
clamped so the prefix > substring > description > fuzzy invariant holds
even for pathologically long project names.
## Model Used
Claude — claude-opus-4-8 (Opus 4.8), extended thinking, tool use (Claude
Code agent).
## Checklist
- [x] Thinking path included
- [x] Model used specified
- [x] Checked ROADMAP.md; small self-contained UX change
- [x] Searched GitHub for duplicate/related PRs
- [x] Described the issue in-PR (no public issue exists)
- [x] No internal/instance-local references
- [x] Branch name descriptive, no internal ticket id
- [x] Tests run locally and pass
- [x] Added/updated tests
- [x] Screenshots included for the visual change
- [ ] Docs updated (n/a — no user-facing docs for this)
- [x] Risks documented
- [ ] All CI gates green (verify after push)
- [ ] Greptile 5/5 (drive after push)
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - The issue/task chat subsystem renders board and agent comments as
markdown inside chat bubbles.
> - Human board comments use a saturated blue bubble so the message can
be distinguished from agent output.
> - The shared `.paperclip-markdown` rule forced `var(--foreground)` for
body text and related markdown elements.
> - On the blue bubble, that foreground color is too dark, so
paragraphs, lists, links, and emphasis lose contrast.
> - This pull request adds an explicit on-accent markdown treatment for
blue/current-user message bubbles.
> - The benefit is readable markdown without changing neutral markdown
surfaces elsewhere in the app.
## Linked Issues or Issue Description
Fixes#8688
## What Changed
- Added a `paperclip-markdown-on-accent` class for markdown rendered on
accent-colored message bubbles.
- Updated current-user issue chat messages to use the accent-safe
markdown class.
- Added CSS overrides for body text, list markers, headings, emphasis,
links, blockquotes, and inline code on accent backgrounds.
- Added a regression test proving current-user blue-bubble markdown
receives the accent-safe class.
## Before Screenshot:
<img width="816" height="618" alt="Screenshot 2026-06-27 at 14 22 59"
src="https://github.com/user-attachments/assets/2a982326-9ff3-40ce-9955-ceb081d8a2e0"
/>
## After Screenshot:
<img width="821" height="624" alt="Screenshot 2026-06-27 at 15 05 17"
src="https://github.com/user-attachments/assets/4722a0db-9f6a-4775-a7eb-47328c2f71e0"
/>
## Verification
- `CI=true corepack pnpm exec vitest run
ui/src/components/IssueChatThread.test.tsx`
- `CI=true corepack pnpm --filter @paperclipai/ui typecheck`
- GitHub Actions PR workflow passed on latest head
`f645cca3fc9675071c201471c0df938880c0c351`, including Build, Typecheck +
Release Registry, e2e, general test shards, serialized server shards,
Canary Dry Run, and aggregate verify.
- Greptile Review passed with Confidence Score 5/5 on latest head
`f645cca3fc9675071c201471c0df938880c0c351`.
## Risks
Low risk. The new color treatment is opt-in and only applied to
current-user issue chat markdown on accent bubbles. Neutral comment,
document, artifact, and skill markdown surfaces keep the existing
`.paperclip-markdown` colors.
> 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 GPT-5 Codex in Codex desktop, with repository tool use and local
command 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
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - The experimental server info debug view helps local operators
inspect what code a running dev instance is actually serving
> - The view was moved into the account-menu drawer, which only mounts
while that drawer is open
> - That made stale health-query data easier to see after restarts, and
the server was also caching the running commit at process boot
> - A clean commit label alone is incomplete when the checkout has
uncommitted local changes
> - This pull request keeps the drawer health data fresh, refreshes git
metadata on demand, and adds a path-free checkout-state summary
> - The benefit is that the debug view reports restart time, running
commit, and dirty-checkout state without exposing local paths, secrets,
logs, or environment details
## Linked Issues or Issue Description
Fixes: #8752
## What Changed
- `SidebarServerInfo.tsx`: refetch the health query whenever the drawer
opens and poll every 2s while the dev server is active.
- `server-info.ts`: keep `processStartedAt` stable while refreshing git
HEAD through a short TTL cache instead of freezing commit metadata at
module boot.
- Shared health contract/OpenAPI: add `serverInfo.git.localChanges` with
only staged, unstaged, and untracked counts plus safe unavailable
fallbacks.
- `SidebarServerInfo.tsx`: add a `Checkout state` row that renders
clean/dirty/unavailable copy without file paths.
- Tests: cover stale drawer refresh, interval polling, TTL commit
refresh, health response shape, checkout-state count parsing, and
path-free UI rendering.
## Verification
- `npx vitest run server/src/__tests__/server-info.test.ts
server/src/__tests__/health.test.ts
ui/src/components/SidebarServerInfo.test.tsx` -> 3 files / 19 tests
passing.
- `pnpm install --frozen-lockfile --ignore-scripts` -> refreshed stale
workspace links without lockfile/source churn.
- `pnpm --filter @paperclipai/shared --filter @paperclipai/server
--filter @paperclipai/ui typecheck` -> passing.
- `pnpm --filter @paperclipai/ui typecheck` -> passing after the
Greptile test-coverage fix.
- `pnpm check:tokens` -> no forbidden tokens found.
- Local diff scans for obvious secrets, credentials, private URLs, local
paths, and PII patterns -> no matches.
- GitHub PR checks on head `56defd446` -> all green, including `verify`,
canary dry run, e2e, security scans, and Greptile Review.
- Greptile latest summary -> Confidence Score 5/5, 0 new comments; the
prior P2 polling-coverage thread is resolved.
## Risks
Low risk. The UI remains behind the experimental
`enableServerInfoDebugView` flag. The extra git status call is throttled
by the existing server-info TTL and reports only counts, not paths or
file names. If git status is unavailable, the commit row still works and
the checkout-state row shows clear fallback copy.
## Model Used
Claude Opus (claude-opus-4-8), extended thinking, with tool use / code
execution assisted the original stale-metadata fix. OpenAI GPT-5 via
Codex local, with tool use and code execution, added the checkout-state
follow-up, Greptile fix-up, and PR 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
- [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>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - Paperclip runs agents through adapters, including built-in local and
gateway-style adapters.
> - Hermes gateway users need to connect Paperclip to an already-running
Hermes API server.
> - The gateway setup flow was missing clear non-local adapter
configuration fields and accepted fewer URL shapes than operators
naturally paste from Hermes.
> - It also surfaced sparse diagnostics when the gateway was unreachable
or when Paperclip generated onboarding prompts for gateway agents.
> - This pull request tightens Hermes gateway configuration, URL
normalization, diagnostics, and onboarding defaults.
> - The benefit is that Hermes gateway setup is easier to complete and
easier to debug without affecting unrelated adapters.
## Linked Issues or Issue Description
No public issue found for this exact follow-up. Related prior/in-flight
Hermes work:
- Refs #2363
- Refs #4359
- Refs #6473
Problem statement:
- **Type:** Adapter follow-up / setup reliability
- **Adapter:** `hermes_gateway`
- **Motivation:** Operators configure `hermes_gateway` against a running
Hermes API server, but the UI and onboarding flow did not expose enough
gateway-specific configuration or diagnostics.
- **Expected behavior:** Paperclip should render the gateway fields,
normalize common Hermes dashboard/API URL inputs, preserve sensible
gateway onboarding defaults, and report reachability failures with
actionable detail.
- **Deployment mode:** Built-in adapter package in the Paperclip
monorepo.
## What Changed
- Added UI config fields for non-local Hermes gateway settings,
including tests for rendering and field behavior.
- Accepted Hermes dashboard URLs by normalizing them to gateway API URLs
for execution.
- Improved gateway reachability and run URL diagnostics.
- Updated Hermes gateway onboarding text/default behavior so join
prompts preserve gateway configuration.
- Added focused server, UI, and adapter tests for the gateway
configuration and onboarding paths.
## Verification
- `pnpm install --frozen-lockfile --prefer-offline`
- `pnpm --filter @paperclipai/hermes-paperclip-adapter exec vitest run
src/gateway/server/execute.test.ts` — 20 passed
- `pnpm exec vitest run
server/src/__tests__/invite-accept-gateway-defaults.test.ts
server/src/__tests__/invite-onboarding-text.test.ts
ui/src/adapters/hermes-gateway/config-fields.test.tsx
ui/src/components/AgentConfigForm.render.test.tsx
ui/src/lib/agent-onboarding-prompt.test.ts` — 23 passed across 5 files
- Confirmed the PR diff excludes `pnpm-lock.yaml` and
`.github/workflows`.
## Risks
Low to moderate risk. The changes are scoped to Hermes gateway
configuration/onboarding and generic non-local adapter field rendering.
The main risk is rejecting an unusual Hermes URL shape that should be
accepted; the normalization tests cover dashboard and API URL variants
added here.
> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.
## Model Used
OpenAI Codex, GPT-5-based coding agent in a tool-enabled local CLI
environment, with shell/GitHub/Paperclip API access. Exact runtime model
identifier was not exposed in this session.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [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>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - Operator secrets and provider vaults let a company connect external
secret stores without copying every value manually.
> - The existing AWS Secrets Manager import dialog already supports
importing remote secrets from any eligible vault.
> - From the Provider vaults tab, operators can see a specific vault
row, but there was no row-level import action that preserved that vault
context.
> - That meant importing from a specific vault required opening the
generic import dialog and reselecting the intended provider config.
> - This pull request adds a row-level refresh/import action that opens
the import dialog with the selected vault preselected.
> - The benefit is a tighter, less error-prone flow when a company has
multiple AWS provider vaults configured.
## Linked Issues or Issue Description
No public issue exists for this small UI follow-up.
Problem description:
- Users with multiple AWS Secrets Manager provider vaults need to
refresh/import secrets from the vault they are currently looking at.
- The existing import dialog defaulted to the configured default vault
or first eligible vault, even when the operator initiated the flow from
a specific provider-vault row.
- This creates extra selection work and makes it easier to import from
the wrong vault.
Related public PRs: Refs #5429, #8586.
## What Changed
- Added a Provider vaults row action for AWS Secrets Manager configs to
refresh/import existing remote secrets.
- Threaded an optional initial provider config id into
`ImportFromVaultDialog` so the selected vault is preselected when
eligible.
- Reset the initial vault selection when the dialog closes or routes to
vault management.
- Added render coverage proving the row action opens the import dialog
and previews against the selected vault id.
## Verification
- `pnpm exec vitest run ui/src/pages/Secrets.render.test.tsx` passed: 1
file, 9 tests.
- `pnpm --filter @paperclipai/ui typecheck` passed.
## Risks
Low risk. This is UI-only behavior scoped to AWS Secrets Manager
provider-vault rows and the existing import dialog. The main risk is
that future provider types may need their own row-level import labels or
eligibility rules instead of sharing this AWS-specific action.
> 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 GPT-5 Codex via Paperclip/Codex local agent, with repository file
access, shell command execution, GitHub CLI, and TypeScript/Vitest
verification. Exact context-window metadata was not surfaced in this
runtime.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have 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: Claude Opus 4.8 <noreply@anthropic.com>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - The Agents page is where operators scan the current agent roster,
runtime status, model, adapter, and last heartbeat.
> - Environment selection now affects where agents execute, but the
roster did not expose each agent's effective execution environment.
> - Operators need that context when multiple environments are
configured, especially when sandbox-backed environments use different
providers.
> - This pull request adds environment/provider metadata to the Agents
page while keeping it hidden when the column would not add useful
information.
> - The benefit is better operational visibility without adding noise
for single-environment or environment-disabled instances.
## Linked Issues or Issue Description
No matching public GitHub issue was found after searching for related
Agents environment-column issues and PRs.
## Problem or motivation
Operators use the Agents page to scan active agents, but when multiple
execution environments are configured there was no row-level indication
of which environment each agent uses. That makes it harder to
distinguish host-local agents from sandbox-backed agents and to see the
sandbox provider at a glance.
## Proposed solution
Show each agent's effective environment in the Agents page metadata when
environments are enabled and multiple environments are configured.
Resolve the value from the agent override, instance default, or local
fallback, and include sandbox provider display names when available.
## Alternatives considered
Always showing the column would add noise for single-environment
instances. Adding filters and grouping was also considered, but this PR
keeps the scope to read-only metadata until the product has more usage
data.
## Roadmap alignment
This is a small UI visibility improvement related to the roadmap's
cloud/sandbox agents direction, without adding a new core workflow or
adapter capability.
## Additional context
The column is hidden when the experimental environments setting is
disabled or when only one environment is configured.
## What Changed
- Added environment metadata loading to the Agents page, including
environment list, capabilities, and instance settings.
- Resolved each agent's effective environment from the agent override,
instance default, or local fallback.
- Rendered environment name and sandbox provider detail in list and org
views when environments are enabled and multiple environments are
configured.
- Hid the environment column when environments are disabled or only one
environment is configured.
- Added focused UI tests for display, fallback, loading, hidden-column,
disabled-feature, and no filter/grouping behavior.
- Addressed Greptile review feedback by preserving custom local
environment names, reserving the column while environment data loads,
and separating the capabilities query key.
## Verification
- `pnpm install --frozen-lockfile`
- `pnpm --filter @paperclipai/ui exec vitest run
src/pages/Agents.test.tsx` — 11 tests passed
- `pnpm --filter @paperclipai/ui build`
- Local PR-diff scan for secrets/private references before push
- GitHub PR workflow passed on head `bb770acc3`
- Commitperclip review check passed
- Greptile Review passed with confidence score 5/5 and no inline review
comments
## Risks
Low risk. The change is limited to the Agents page, query keys, and
tests. The main behavioral risk is extra metadata query traffic on the
Agents page when environments are enabled; those queries are skipped
when the experimental environments flag is disabled.
## Model Used
OpenAI Codex, GPT-5 coding agent with shell/tool use and code execution
in the local repository.
## 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>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - The dev/server UI exposes a `/api/health` endpoint and a lower-left
account drawer, but nothing surfaces *which* build the running instance
is on or when it last restarted
> - When iterating on a local dev instance it is hard to tell whether
the server you're looking at has actually restarted onto your latest
commit, or how stale the running process is
> - Developers need a lightweight, opt-in way to confirm the running
instance's identity without digging through logs or shelling into the
host
> - This pull request adds an experimental "Server Info Debug View"
setting that surfaces the running instance's last-restart time and
current commit as read-only rows in the account drawer
> - The benefit is a quick, in-UI sanity check of what the live server
is actually running, behind an experimental flag so it ships zero cost
to users who don't opt in
## Linked Issues or Issue Description
No public GitHub issue exists. Describing the underlying request inline
following the feature request template:
**Problem or motivation:**
When working against a local Paperclip dev instance there is no in-UI
way to confirm what the running server is — its current commit or when
it last restarted. You have to check logs or the host shell to know
whether the process picked up your latest build.
**Proposed solution:**
An opt-in experimental setting ("Server Info Debug View") that, once
enabled, renders a small read-only "Server" section at the bottom of the
lower-left account drawer showing **Last restarted** (the server process
start time) and **Running commit** (the current git HEAD short SHA +
subject).
**Alternatives considered:**
A separate top-right pill/overlay (like the work-life-balance plugin).
The account drawer was chosen to reuse existing menu-row styling and
avoid adding new always-present chrome.
**Roadmap alignment:**
Small, self-contained developer-experience aid gated behind an
experimental flag; does not overlap planned core roadmap work.
## What Changed
- Added `server/src/server-info.ts`: captures a `serverInfo` snapshot
once at boot — process start time and current git commit (SHA +
subject). Git is read via `execFileSync` with SHA validation and a
timeout.
- `/api/health` exposes the `serverInfo` snapshot, but only on
full-details health responses (board/agent in authenticated mode, or
local-trusted dev).
- Gated the UI surface behind a new `enableServerInfoDebugView`
experimental setting, wired through the shared instance type, validator,
settings normalizer, and OpenAPI schema.
- UI: added `SidebarServerInfo` rendering the read-only rows in the
account drawer (`BreadcrumbBar` / `SidebarAccountMenu`), plus the
experimental settings toggle and a typed `health` API client.
- Moved `ServerGitInfo` / `ServerInfoSnapshot` into
`@paperclipai/shared` so the server and UI share one definition instead
of duplicating it.
- Added unit tests for the server-info snapshot, health route exposure,
validator/normalizer, settings routes, the experimental settings page,
and the sidebar component.
## Verification
- `pnpm --filter @paperclipai/server exec vitest run
src/__tests__/health.test.ts src/__tests__/server-info.test.ts
src/__tests__/instance-settings-service.test.ts
src/__tests__/instance-settings-routes.test.ts` — 32 passed
- `pnpm --filter @paperclipai/ui exec vitest run
src/components/SidebarServerInfo.test.tsx
src/pages/InstanceExperimentalSettings.test.tsx` — 9 passed
- `tsc --noEmit` on both `@paperclipai/server` and `@paperclipai/ui` —
clean
- Manual: enable **Settings → Experimental → Server Info Debug View**,
refresh the UI, open the lower-left account drawer — a "Server" section
shows Last restarted and Running commit.
## Risks
- Low risk. The UI surface is fully opt-in via an experimental flag and
defaults off.
- The `serverInfo` field on `/api/health` is access-controlled to
full-details responses only (board/agent in authenticated mode, or
local-trusted dev) — never anonymous authenticated callers — so the git
SHA is not broadly exposed.
- The only new server work is a one-time git read at boot, guarded with
SHA validation and a timeout; failures degrade gracefully (the git block
reports `available: false` rather than throwing).
## Model Used
Claude — `claude-opus-4` (Anthropic), extended thinking with tool use,
via Claude Code.
## 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
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
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - Agent adapters are the boundary between the control plane and the
runtimes that actually do work.
> - Hermes support needs to be available as first-class local and
gateway adapters while still preserving the adapter-manager override
path for external packages.
> - The adapter work touches runtime execution, UI adapter metadata,
onboarding prompts, scoped credentials, release packaging, and smoke
coverage, so the handoff needs concrete verification rather than only
unit tests.
> - This pull request adds built-in Hermes local and Hermes gateway
support, keeps external adapter overrides compatible, and
documents/tests the gateway flow end to end.
> - The benefit is that operators can hire Hermes-backed agents without
a manual plugin install, while self-hosted installs can still
override/shadow the built-ins through Adapter manager packages.
## Linked Issues or Issue Description
No public GitHub issue exists for this exact Hermes built-in adapter,
gateway onboarding, and release-source work.
Problem description:
- Hermes local and gateway adapters need a public, reviewable source
path in the monorepo so package artifacts and built-in adapter behavior
match the application source.
- Operators need built-in `hermes_local` and `hermes_gateway` adapter
choices without losing the ability to install external Hermes packages
as overrides.
- Gateway onboarding needs secure defaults for API server URLs, API
keys, and generated agent setup text.
- Hermes-originated task bridge credentials need narrower API-key scope
configuration.
- Related public PRs found during duplicate search include #3027, #2363,
#7544, #7950, #8095, and #8543.
## What Changed
- Added the unified Hermes adapter package with local and gateway
server/UI/CLI exports, config schemas, transcript parsing, model
detection, and package metadata.
- Registered `hermes_local` and `hermes_gateway` as built-in adapters
across shared constants, server registries, CLI packaging, and UI
adapter registries.
- Kept the external adapter override path compatible so installed Hermes
packages can shadow built-ins and restore the built-in parser when
disabled.
- Added Hermes gateway onboarding docs, board-operator docs, Docker
smoke assets, and shell smoke harnesses for join/e2e validation.
- Added scoped task-bridge API-key support, authorization checks,
issue-origin handling, and tests for Hermes-created Paperclip tasks.
- Hardened gateway transport and redaction behavior for API keys,
headers, session data, and smoke diagnostics.
- Updated release packaging/bootstrap checks for the Hermes packages
while leaving `pnpm-lock.yaml` out of the PR per repository policy.
## Verification
Targeted local verification recorded before PR handoff:
- `pnpm --filter @paperclipai/hermes-paperclip-adapter exec vitest run
src/gateway/server/execute.test.ts` — 14/14 passed.
- `pnpm test:hermes-gateway-smoke` — 6/6 passed.
- Hermes package typecheck/build checks passed.
- Focused server/UI adapter tests passed — 31/31.
- Release helper Node tests passed — 18/18.
- `git diff --check origin/master..HEAD` passed.
Fresh Docker E2E smoke evidence:
- Ran `pnpm smoke:hermes-gateway-e2e` on 2026-06-26 with a fresh state
directory and fresh Docker container against a live Paperclip dev
server.
- Hermes direct execution reached `completed`.
- Hermes stop/cancel path reached `cancelled`.
- Hermes gateway created a Paperclip task, Paperclip ran the Hermes
agent, and the task reached `done` with the expected marker response.
- Temporary board auth keys, token files, smoke state, and Docker
containers were cleaned up after the run.
PR checks on head `b5eae40ce`:
- GitHub Actions passed: `policy`, `review`, `Typecheck + Release
Registry`, all general test shards, all serialized server shards,
`Build`, `Canary Dry Run`, `e2e`, and aggregate `verify`.
- External checks passed: Snyk and Socket Project Report.
- External Socket Pull Request Alerts remained pending after the
first-party CI matrix completed.
## Risks
- Medium risk: this spans adapter registration, package publishing,
gateway execution, onboarding docs, API-key scoping, and UI adapter
metadata.
- Migration risk is low: the scope-config migration adds a nullable
column and does not rewrite existing keys.
- Gateway execution depends on operator-provided Hermes API
configuration; the smoke covers the Docker gateway path but real
deployments may differ by network/auth setup.
- Direct Greptile review on the latest expanded diff is file-count
limited, although the commitperclip review gate passed.
> 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 coding agent, tool use enabled in a local repository
workspace. 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] Commitperclip review gate is green; direct Greptile review is
file-count limited on the latest expanded diff
- [x] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - Agent runs stream their transcripts through per-adapter stdout
parsers into the chat/run transcript UI
(`ui/src/adapters/transcript.ts`)
> - The Cursor CLI (local) streams assistant text as many small `text`
events (often a token or word each), and the parser emitted one
assistant entry per event and trimmed each
> - As a result the chat rendered one bubble per token ("every line a
new token") and dropped inter-token whitespace, making Cursor runs hard
to read
> - The render layer already coalesces consecutive `delta` entries
(`appendTranscriptEntry`), but the Cursor parser never tagged streamed
text as a delta
> - This pull request tags streamed `text` as a delta (without trimming)
so the existing render-time coalescer merges them into one assistant
block, while a `tool_call`/`tool_result` between deltas still breaks the
run
> - The benefit is readable Cursor transcripts with correct spacing and
preserved tool boundaries, with no change to the canonical event stream
(raw view unaffected)
## Linked Issues or Issue Description
No existing public issue — describing the bug inline (per
`.github/ISSUE_TEMPLATE/bug_report.yml`):
**What happened**
In the chat/run transcript, Cursor (local) assistant messages render as
one bubble per token/word, and inter-token spaces are dropped, making
the transcript unreadable. Root cause:
`packages/adapters/cursor-local/src/ui/parse-stdout.ts` (`type: "text"`
branch) emitted `{ kind: "assistant" }` per streamed `text` event
without `delta: true` and trimmed each, so the render-time coalescer
(`ui/src/adapters/transcript.ts`) never merged them and whitespace was
lost.
**Expected behavior**
Streamed assistant text should render as a single contiguous prose
block, with tool calls preserved as boundaries between blocks.
**Steps to reproduce**
1. Run a Cursor (local) agent that streams a multi-word assistant
message.
2. Open the run transcript in the chat UI.
3. Observe each streamed token/word rendered as its own bubble, with
inter-token spaces missing.
**Paperclip version**
Reproduced on current `master` (cutover base `e68188c43`).
**Deployment mode**
Self-hosted, `cursor_local` adapter.
## What Changed
- `packages/adapters/cursor-local/src/ui/parse-stdout.ts`: tag streamed
`text` events as `{ kind: "assistant", delta: true }` and stop trimming,
so the existing `appendTranscriptEntry` coalescer merges consecutive
deltas into one block.
- `ui/src/adapters/cursor-coalescing.test.ts` (new): dual-shape golden
fixtures (Cursor local + cloud) exercising the full render-time
projection via `buildTranscript`.
## Verification
- `pnpm --filter @paperclipai/ui exec vitest run
src/adapters/cursor-coalescing.test.ts src/adapters/transcript.test.ts`
→ **10/10 pass**.
- `pnpm --filter @paperclipai/ui --filter
@paperclipai/adapter-cursor-local typecheck` → **green**.
- The golden fixtures assert the run `text → tool_call → tool_result →
text → consolidated final` renders as exactly **two prose blocks with
the tool between them**, **no duplication** of the consolidated final,
and **inter-token whitespace preserved** across coalesced deltas.
## Risks
- **Low risk.** Pure classification at parse time; the canonical event
stream and the raw view are unchanged — only the "nice" render-time
projection changes. The coalescing logic (`appendTranscriptEntry`) is
pre-existing and already covered by tests. No schema, migration, or
behavioral change outside transcript rendering.
## Model Used
- **Claude Opus 4.8** (Anthropic), extended/high reasoning mode, driven
via the Cursor agent with tool use + code execution. Diagnosis and
fixtures grounded in the repo's actual parser/render code.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above (none found)
- [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 references)
- [x] My branch name describes the change
(`fix/cursor-transcript-coalescing`) 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 (N/A —
no documented behavior changes)
- [x] I have considered and documented any risks above
- [ ] All Paperclip CI gates are green (pending CI run)
- [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
(pending review)
- [x] I will address all Greptile and reviewer comments before
requesting merge
Co-authored-by: Sebastian Heyneman <sebastian@joinnova.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Reviewed by CTO for PAP-12039. Client-side ordering change only, with focused helper coverage; CI, security scans, and Greptile are green.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Reviewed by CTO for PAP-12039. Scope is limited to external object URL/link label fallback and focused UI regression coverage; CI and Greptile are green.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - The pipeline subsystem models repeatable work as items moving
through stages, with agent automation, review gates, blockers, drift
notices, and linked work.
> - Operators need this to be usable as one coherent workflow surface,
not just as backend primitives or disconnected route experiments.
> - The branch now carries the pipeline data model, service/routes,
CLI/tutorial path, aggregation feeds, operator UI, stage automation
controls, liveness/retry handling, and follow-up polish that make the
primitive reviewable end to end.
> - This pull request is the single review target for that pipeline
workflow primitive stack.
> - The benefit is that reviewers can evaluate the full operator
experience and server contract together against `master`.
## Linked Issues or Issue Description
No public GitHub issue exists for this work. The underlying feature
request is described inline.
### Problem or motivation
Paperclip needs a first-class way to model multi-stage agent/company
workflows where upstream items can spawn downstream work, request
review, carry fields across pipelines, surface drift, retry automation,
and show operators where work is blocked or active. Without a unified
pipeline primitive, these workflows spread across ad hoc issues,
routines, and comments, making the state hard to inspect or operate.
### Proposed solution
Add the pipeline workflow primitive stack: database schema and
migrations, shared validators/types, server services and REST routes,
aggregation and liveness helpers, CLI/tutorial smoke support, and the
React operator UI for pipeline lists, boards, item detail,
review/learnings views, settings, stage automation, secrets, carry-over
fields, and retry/recovery flows.
### Alternatives considered
- Keep workflows as loosely linked issues and routines: rejected because
operators need a single board/detail/settings surface for repeated
workflow patterns.
- Ship backend primitives first and defer UI: rejected for this branch
because the operator experience is the main way to validate the
primitive.
- Add a narrower one-off content workflow: rejected because the same
primitives are useful across future company processes.
## What Changed
- Added and evolved pipeline schema, migrations, shared contracts,
server services, REST routes, route tests, and CLI/tutorial smoke
support.
- Added pipeline aggregation, health/liveness, drift acknowledgment,
blocker/carry-over, automation retry, stage automation environment, and
permission recovery behavior.
- Added the operator UI for pipeline index/board/item
detail/settings/review/learnings flows, including stage secrets,
automation controls, markdown/item descriptions, linked issue assets,
liveness banners, and source automation metadata.
- Refactored issue document frame rendering through the shared
`DocumentFrameHeader` component to keep document controls consistent
with the pipeline document surfaces.
- Kept this PR as the single base-branch review target for the current
pipeline branch.
## Verification
Current branch refresh:
- `pnpm vitest run server/src/__tests__/pipelines-service.test.ts` — 31
passed
- `pnpm vitest run server/src/__tests__/pipelines-routes.test.ts` — 19
passed
- `pnpm --filter ./server typecheck` — passed
- `pnpm --filter ./ui typecheck` — passed
- Verified Pipelines remains gated by `enablePipelines === true`:
sidebar item is hidden unless the flag is enabled, direct pipeline
routes redirect to `/dashboard` when disabled, and the Experimental
settings UI still has no Pipelines toggle.
- GitHub status checks on `df071c710646de625131064c3fb6588b5e97964a` —
all complete with no failing conclusions, including Actions, Socket,
Superagent/Security, and Greptile Review
- Greptile summary on `df071c710646de625131064c3fb6588b5e97964a` —
Confidence Score 5/5
- GitHub review-thread sweep — 0 unresolved Greptile threads
Previously recorded during branch development:
- Server pipeline service/route and aggregation tests
- Shared validator tests
- UI pipeline page/settings/item-detail/learnings/liveness tests
- Pipeline tutorial smoke path
## Risks
- High review surface: this is a large feature branch spanning database,
shared contracts, server behavior, CLI/docs, and UI.
- Migration ordering and schema compatibility need reviewer attention
because this branch has been kept current across multiple `master`
syncs.
- GitHub still reports merge state `BLOCKED` because the PR is awaiting
normal human review/branch-protection completion; all current status
checks are green.
- Branch-name checklist exception: this PR uses the pre-existing
requested branch name, which predates the current public-branch naming
rule. The PR title/body avoid internal issue references.
> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.
## Model Used
OpenAI Codex coding agent based on GPT-5, with repository tool use,
shell execution, git/GitHub CLI operations, and local verification
commands. Earlier commits in this branch were assisted by Paperclip
agents and other AI coding agents as recorded in commit authorship.
## 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)
- [ ] 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>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - The onboarding and company switcher UI are the first places users
create or select an organization
> - Some of that surface still used older team/workspace wording even
though the product model is company-centric
> - Mixed wording makes the setup path feel inconsistent and can make
users wonder whether they are creating a team, workspace, or company
> - This pull request updates the affected UI copy to consistently say
company
> - The benefit is a clearer first-run and navigation experience without
changing behavior
## Linked Issues or Issue Description
No public issue exists for this small UI polish change.
### Problem or motivation
The company creation and switcher surfaces used mixed
team/workspace/company wording for the same concept, which makes the
setup path feel inconsistent.
### Proposed solution
Update the visible copy, accessibility label, inline comment, e2e
expectations, and matching test expectations to use company-centric
language consistently.
### Alternatives considered
Leave the existing wording alone, but that preserves inconsistent
terminology in a high-traffic setup path.
### Roadmap alignment
This is focused UI polish and does not overlap with a roadmap-level core
feature.
### Additional context
The create action keeps its trailing ellipsis because it opens the
onboarding wizard rather than completing immediately.
## What Changed
- Updated front door and onboarding wizard labels from team-oriented
copy to company-oriented copy.
- Updated the sidebar company menu from workspace/team wording to
company wording, including the trigger accessibility label and empty
fallback text.
- Kept the sidebar create action ellipsis for the dialog/wizard
affordance.
- Updated component and Playwright test expectations for the new copy.
## Verification
- `pnpm exec vitest run ui/src/components/SidebarCompanyMenu.test.tsx`
- Attempted `npx playwright test --config tests/e2e/playwright.config.ts
tests/e2e/onboarding.spec.ts tests/e2e/nux-phase4-screenshots.spec.ts
tests/e2e/planning-mode-visual-verification.spec.ts
tests/e2e/conference-room-typing-intro.spec.ts`; local browser launch is
blocked by missing host Chromium dependencies (`libatk1.0-0t64`,
`libatspi2.0-0t64`, `libxcomposite1`, `libxdamage1`, `libxfixes3`,
`libxrandr2`, `libgbm1`, `libasound2t64`).
- Screenshots intentionally omitted because this is a copy-only change
and no design screenshots are needed for review.
## Risks
Low risk. This is copy-only UI polish plus matching test updates; no
data model, API, migration, workflow, lockfile, or behavior changes are
included.
## Model Used
OpenAI GPT-5 Codex (`gpt-5`) via the Paperclip Codex agent, with
tool-assisted repository inspection, GitHub CLI usage, and local command
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>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - Scheduled routines can prompt agents with variables that are filled
in at dispatch time.
> - Existing routine variable handling supported plain text-like values,
but date inputs need a structured contract so routines can pass
consistent date values.
> - Operators also need date variables to be easy to configure and
override from the routine UI.
> - This pull request adds a date variable type across shared
validation, server dispatch, and UI editing/run dialogs.
> - The benefit is that routine authors can define date inputs once and
agents receive validated ISO-style date values when routines run.
## Linked Issues or Issue Description
Refs #219
Feature request:
- Problem/motivation: Scheduled routines need first-class, typed date
variables so operators can configure dates without relying on free-form
text conventions.
- Proposed solution: Add an `x-date` routine variable type with shared
parsing/validation, server dispatch support, and UI date-picker controls
in routine variable editors and run dialogs.
- Alternatives considered: Continue treating dates as plain text, but
that leaves validation and formatting to individual operators and
agents.
- Roadmap alignment: This is a focused improvement to the completed
Scheduled Routines milestone and does not duplicate an active roadmap
item.
Related PR search:
- Searched existing PRs/issues for `routine date picker`, `date
variables`, and `scheduled routine date variable`; no direct duplicate
PR was found.
## What Changed
- Added the shared `x-date` routine variable contract, parsing,
defaults, and validation coverage.
- Extended routine dispatch to validate and pass date variable values.
- Added date input controls to the routine variable editor and routine
run variables dialog.
- Added focused tests for shared validation, server dispatch, and the UI
date controls.
## Verification
- `git diff --check public/master...HEAD`
- `pnpm run preflight:workspace-links && pnpm exec vitest run
packages/shared/src/routine-variables.test.ts
packages/shared/src/validators/routine.test.ts
server/src/__tests__/routines-service.test.ts
ui/src/components/RoutineRunVariablesDialog.test.tsx
ui/src/components/RoutineVariablesEditor.test.tsx`
- 5 test files passed
- 68 tests passed
## Risks
Low to medium risk. This adds a new routine variable type across
shared/server/UI paths, so the main risk is compatibility with existing
routine variable payloads. The change keeps existing variable types
intact and adds targeted validation tests for the new date 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 coding agent based on GPT-5, with terminal, git, GitHub
CLI, and local test execution capabilities.
## 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>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - The web UI (`ui/`) renders inside a single persistent shell
(`Layout`) whose `#main-content` element is the scroll container for
every route
> - Because that scroll container survives route changes, browser
back/forward (a history `POP`) lands on the previous page with the
scroll offset of the page you just left
> - Concretely: scroll deep into an issue detail, hit browser back, and
the inbox renders scrolled to the issue-detail offset instead of where
you left off — making it hard to find your place
> - The app already reset scroll for forward (`PUSH`) navigation but
deliberately did nothing on `POP`, so nothing restored the prior
position
> - This pull request records each history entry's scroll offset as the
user scrolls and restores it on `POP`, while keeping the existing
reset-to-top behavior for `PUSH`/`REPLACE`
> - The benefit is that back/forward returns you to the exact place you
were, so the inbox (and any scrolled page) keeps your reading position
## Linked Issues or Issue Description
No public GitHub issue — describing the bug inline following the bug
report template.
**What happened**
From the inbox, open an issue, scroll down within the issue detail, then
press the browser Back button. The inbox is restored at the wrong scroll
position — it shows the Y offset from the issue-detail page rather than
the position you had in the inbox.
**Expected behavior**
Browser back/forward returns the page to the scroll position it had when
you left it.
**Steps to reproduce**
1. Open the inbox and scroll to a known position partway down the list.
2. Click into an issue.
3. Scroll down within the issue detail.
4. Press the browser Back button to return to the inbox.
5. Observe the inbox is scrolled to the issue-detail offset instead of
where you left off.
**Deployment mode**
Web UI (`ui/`), any deployment — client-side scroll behavior only.
Root cause: `#main-content` is a single scroll container that stays
mounted across route changes. Scroll was only reset on forward (`PUSH`)
navigation and left untouched on `POP`, so the stale offset from the
outgoing page persisted onto the page being returned to.
## What Changed
- Added `NavigationScrollMemory` (`ui/src/lib/navigation-scroll.ts`): a
per-history-key map of `#main-content` scroll offsets, clamped to `>=
0`.
- Added `applyMainContentScrollTop` helper to restore a saved offset
onto the main content element (null-safe).
- In `Layout` (`ui/src/components/Layout.tsx`): continuously record the
active history entry's scroll offset on scroll, and on `POP` navigation
restore the remembered offset (re-applying on the next animation frame
so a late-laying-out cached page doesn't clamp the offset to a shorter
interim height). Forward `PUSH`/`REPLACE` keeps the existing
reset-to-top behavior.
- Added unit tests covering the remember/recall logic and the restore
helper.
## Verification
- `ui` unit tests pass, including the new `navigation-scroll` cases
(remember/recall per key, clamping, and DOM restore).
- TypeScript clean on the changed files.
- Manual: inbox → open issue → scroll down → browser Back returns the
inbox to its previous scroll position; forward navigation still resets
to top.
## Risks
Low risk. Scoped to client-side scroll restoration in the web UI; no
API, schema, or migration changes. The only behavioral change is that
`POP` navigation now restores a saved offset instead of leaving the
container untouched; `PUSH`/`REPLACE` behavior is unchanged. Memory is
per-session and bounded by visited history keys.
## Model Used
Claude (Anthropic), Opus-class model via Paperclip's `claude_local`
adapter, with extended thinking and 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
- [ ] 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
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - The board UI needs to render issue details, comments, properties,
and rich external object labels clearly during normal operator
workflows.
> - Three small UI regressions made those workflows harder to scan: rich
object URLs could show weak labels, interrupting comments could briefly
flash in the wrong state, and watchdog labels could overflow the
properties panel.
> - These are related quality fixes in the same UI surface, with focused
regression coverage for each behavior.
> - This pull request groups the fixes so they go through normal review
instead of bypassing CI.
> - The benefit is a quieter, more predictable issue detail experience
for board operators.
## Linked Issues or Issue Description
No public GitHub issue was found for these regressions after searching
related PRs and issues.
### Pre-submission checklist
- Existing open and closed GitHub issues and PRs were searched for
duplicates.
- The fixes are based on current `master` behavior.
- The regressions originate in Paperclip board UI code, not an adapter,
API provider, or local configuration.
### What happened?
In the board UI, three issue-detail regressions made normal review
workflows harder to scan:
- URL-rich external objects could fall back to a weak generic label
instead of showing a useful URL label.
- An interrupting issue comment could briefly flash through the wrong
state while issue run data refreshed.
- Long watchdog property instructions could truncate or overflow instead
of wrapping inside the properties panel.
### Expected behavior
- URL-rich external objects should surface a clear URL label.
- Interrupting comments should stay visually stable while live run state
refreshes.
- Long watchdog property values should wrap within the available
properties panel width.
### Steps to reproduce
1. Open an issue detail view that includes URL-rich external object
metadata, an interrupting run/comment state, or long watchdog
instructions.
2. Observe the rendered issue detail thread and properties panel.
3. Compare the rendered label, comment state, and watchdog row wrapping
against the expected stable/readable behavior above.
### Paperclip version or commit
Current `master`, fixed by this PR branch at
`1c763001c9a16fa6cf3faad6f58a83c4b202c928`.
### Deployment mode
Local dev (`pnpm dev`) / board UI.
### Installation method
Built from source (`pnpm dev` / `pnpm build`).
### Agent adapter(s) involved
Not adapter-specific (core board UI bug).
### Database mode
Not database-related.
### Access context
Board operator UI.
### Relevant logs or output
Not applicable.
### Relevant config
Not applicable.
### Additional context
The PR includes focused regression tests for all three behaviors.
Browser-visible before/after evidence for the watchdog wrapping change
was posted in
https://github.com/paperclipai/paperclip/pull/8613#issuecomment-4794863305.
### Privacy checklist
- All pasted output was reviewed for sensitive data; this PR body
contains no private config, logs, or internal instance links.
## What Changed
- Prefer direct URLs as rich object labels when rendering external
object metadata.
- Keep interrupting issue comments from flashing through the wrong
thread state while issue chat data is refreshing.
- Allow watchdog property labels to wrap cleanly inside the issue
properties panel.
- Added focused regression coverage for the external object helper,
issue detail interrupt behavior, and watchdog property wrapping.
## Verification
- `pnpm exec vitest run ui/src/lib/external-objects.test.ts
ui/src/lib/issue-chat-messages.test.ts
ui/src/components/IssueProperties.test.tsx
ui/src/pages/IssueDetail.test.tsx`
## Risks
Low risk. The changes are scoped to UI rendering/state handling and add
regression coverage for the touched behavior. No database, API,
workflow, lockfile, or migration changes are included.
> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.
## Model Used
OpenAI Codex using GPT-5 (`gpt-5`) with repository shell/tool access.
The changes were prepared by an AI coding agent with local command
execution for git inspection and focused Vitest 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
- [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>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - The execution workspace subsystem powers project and workspace views
by listing runtime state, branch metadata, issue links, and status
summaries.
> - The existing workspace views relied on broad list data that can grow
expensive as a company accumulates many workspaces and linked issues.
> - That makes the Workspaces page and project workspace cards slower
than necessary because the UI does not always need the full workspace
detail payload up front.
> - This pull request adds a bounded overview contract for workspace
listings and moves the relevant UI surfaces to that cheaper path.
> - The benefit is faster workspace loading while preserving detail
fetches for pages that actually need full workspace data.
## Linked Issues or Issue Description
No public GitHub issue exists for this change. Inline bug report follows
the repository bug template.
### What happened?
Workspace index-style screens can load too much execution workspace
detail before the user asks for it. Several UI surfaces used fuller
workspace data paths for summary displays, which can make workspace
loading slower as workspace history grows.
### Expected behavior
Overview screens should request a bounded summary payload, while detail
screens should keep using the full workspace detail endpoint.
### Steps to reproduce
1. Run Paperclip from source with enough execution workspace history to
make workspace lists non-trivial.
2. Open the Workspaces page or a project workspace summary card.
3. Observe that summary UI needs only bounded workspace metadata but can
depend on broader workspace payloads.
### Paperclip version or commit
`master` at the time this branch was prepared.
### Deployment mode
Local dev (`pnpm dev`)
## What Changed
- Added shared types, validators, and path constants for bounded
execution workspace overviews.
- Added server service and route support for overview queries with
bounded linked issue/runtime metadata.
- Updated workspace overview UI API calls, query keys, breadcrumbs,
quicklooks, close dialogs, project summaries, and detail links to
consume the cheaper overview shape where appropriate.
- Added regression coverage for the new server route/service behavior
and the UI overview consumers.
- Registered the new workspace overview route in the generated OpenAPI
spec.
- Kept overview totals aligned with the project join and preserved
project slug links in workspace headers.
## Verification
- `pnpm exec vitest run
server/src/__tests__/execution-workspaces-service.test.ts
server/src/__tests__/execution-workspaces-routes.test.ts
ui/src/api/execution-workspaces.test.ts
ui/src/components/ProjectWorkspaceSummaryCard.test.tsx
ui/src/pages/Workspaces.test.tsx`
- `pnpm --filter @paperclipai/shared typecheck && pnpm --filter
@paperclipai/server typecheck && pnpm --filter @paperclipai/ui
typecheck`
- `pnpm exec vitest run server/src/__tests__/openapi-routes.test.ts
server/src/__tests__/execution-workspaces-routes.test.ts
server/src/__tests__/execution-workspaces-service.test.ts`
- `pnpm test:run:serialized -- --shard-index 1 --shard-count 4`
## Risks
Low to medium risk. The change introduces a new overview contract across
shared/server/ui layers, so the main risk is a mismatch between summary
and detail payload expectations. The added route/service/UI tests cover
the intended split, and full detail pages continue using the detail
path.
> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.
## Model Used
OpenAI Codex, GPT-5-based coding agent with repository tool use and
local command execution. Exact served model identifier and context
window were not exposed in this session.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [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>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - Agent configuration includes adapter model settings and optional
cheap model profiles used by runtime lanes.
> - The agent configuration screen already exposes a Test action for
adapter environment checks.
> - When a cheap model profile is configured, that Test action only
exercised the primary model configuration.
> - That left users able to save a cheap model profile that had not been
validated by the same configuration test flow.
> - This pull request makes the Test action probe both the primary model
and the configured cheap model.
> - The benefit is earlier feedback when the cheap model is unavailable
or misconfigured.
## Linked Issues or Issue Description
No exact public issue found.
Problem description:
- What happened: the agent configuration Test action validated the
primary adapter model but did not validate an enabled cheap model
profile.
- Expected behavior: when a cheap model is configured and enabled, the
Test action should also test that cheap model using the same environment
selection.
- Steps to reproduce: configure an agent with a primary model and
enabled cheap model profile, then click Test in the agent configuration
UI.
- Paperclip version/commit: current `master` at PR creation.
- Deployment mode: local development UI behavior.
Related public context found during GitHub search:
- #4881 added cheap model profiles for local adapters.
- #6534 is related cheap-primary-model preservation work.
- #6956 tracks broader agent configuration settings exposure.
GitHub searches performed:
- `cheap model config test`
- `AgentConfigForm cheap model`
- `modelProfiles cheap`
- `adapter environment cheap model`
## What Changed
- Updated `AgentConfigForm` so the adapter environment Test action runs
the primary model check first.
- Added a second cheap model check when the cheap profile is enabled and
resolves to a model.
- Built the cheap test payload from the resolved cheap profile config
instead of a model-only override, preserving adapter-default and saved
cheap-profile fields.
- Merged the individual results into a single labeled result so the UI
can show both outcomes together.
- Preserved request/API failures so they still surface through the
existing error UI instead of becoming synthetic adapter checks.
- Added render coverage asserting both primary and cheap test calls,
non-model cheap-profile fields, and request failure handling.
## Verification
- `git diff origin/master...HEAD | rg -n
"(API[_-]?KEY|SECRET|TOKEN|PASSWORD|PRIVATE[_-]?KEY|BEGIN RSA|BEGIN
OPENSSH|Bearer
[A-Za-z0-9._-]+|ghp_[A-Za-z0-9_]+|sk-[A-Za-z0-9]+|AIza[0-9A-Za-z_-]+|OPENAI_API_KEY|ANTHROPIC_API_KEY)"
|| true` produced no matches.
- `git diff --check origin/master...HEAD`
- `pnpm --filter @paperclipai/ui exec vitest run
src/components/AgentConfigForm.render.test.tsx --reporter=dot`
- `pnpm --filter @paperclipai/ui typecheck`
- GitHub PR checks on head `3d9ba15d2` are green, including Build,
Typecheck + Release Registry, General tests, serialized server suites,
e2e, Canary Dry Run, security scans, and policy/review checks.
- Greptile Review passed on head `3d9ba15d2`; all review threads are
resolved.
## Risks
Low risk. The change is limited to the agent configuration UI test
action and its render test. The cheap-model probe now preserves
adapter-default and saved cheap-profile fields, matching the runtime
merge order more closely. The main residual risk is adapter-specific UI
coverage for cheap-profile fields that are stored but not directly
editable in this form.
## Model Used
OpenAI GPT-5 Codex via the Codex local agent environment, with
terminal/tool use for repository inspection, implementation, GitHub CLI
operations, and local verification. Exact context window was not exposed
by the runtime.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - The board UI has been graduating experimental conference-room task
experiences into the default issue and onboarding flows
> - Several task UI improvements were still coupled to the Conference
Room Chat experimental flag even though they are useful outside chat
itself
> - That coupling meant disabling chat also reverted unrelated defaults
such as work-mode labels, task status colors, team creation copy, and
the graduated issue thread
> - This pull request keeps chat-specific gating scoped to chat while
making the graduated task UI the default experience
> - The benefit is that operators can use the newer task workflows
without needing to enable the separate chat experiment
## Linked Issues or Issue Description
No public GitHub issue exists for this exact change.
### Problem or motivation
The Conference Room Chat experimental flag was controlling unrelated
task UI defaults, which made non-chat workflows regress when chat was
disabled.
### Proposed solution
Remove that flag from task-thread, work-mode, onboarding, status, and
team-creation presentation paths while leaving chat-specific behavior
separately gated.
### Alternatives considered
Keeping the flag as a broad umbrella until chat graduates would avoid a
behavior change, but it keeps unrelated UI improvements hidden behind
the wrong capability switch.
### Roadmap alignment
Checked `ROADMAP.md`; this is focused graduation/polish for existing UI
surfaces rather than a new roadmap-level core feature.
Related search:
- Searched open PRs and issues for `conference room chat experimental
flag`: no matches.
- Searched open PRs and issues for `graduated issue thread`: no matches.
## What Changed
- Removes Conference Room Chat flag branching from task-thread
rendering, work-mode labels, task status colors, and team creation copy.
- Makes the onboarding completion path create/reuse an onboarding
project, create the first assigned task, and send the user to the
dashboard instead of chat.
- Deletes the legacy onboarding wizard and classic task-thread files now
that the graduated flow is the default.
- Updates focused UI tests and affected E2E specs for the default task
experience.
- Adds a user-visible onboarding error if restored state is missing the
company or agent required for launch.
## Verification
- `pnpm run preflight:workspace-links && pnpm exec vitest run
ui/src/components/NewIssueDialog.test.tsx
ui/src/components/OnboardingWizardVariant.test.tsx
ui/src/components/RunChatSurface.test.tsx
ui/src/components/SidebarCompanyMenu.test.tsx
ui/src/components/StatusBadge.test.tsx ui/src/lib/agent-order.test.ts
ui/src/lib/onboarding-launch.test.ts ui/src/lib/work-mode-meta.test.ts
ui/src/pages/IssueDetail.test.tsx`
- `pnpm --filter @paperclipai/ui typecheck`
- `npx playwright test --config tests/e2e/playwright.config.ts --list
tests/e2e/conference-room-typing-intro.spec.ts
tests/e2e/planning-mode-visual-verification.spec.ts`
- Attempted targeted Playwright execution locally, but this host is
missing Chromium system libraries (`libatk1.0-0t64`, `libatspi2.0-0t64`,
`libxcomposite1`, `libxdamage1`, `libxfixes3`, `libxrandr2`, `libgbm1`,
`libasound2t64`). CI runs the specs in the proper Actions environment.
## Risks
- Medium UI behavior risk: this intentionally changes the default
experience for users who have not enabled Conference Room Chat.
- Medium onboarding risk: completion now creates/reuses a project and
creates the first task instead of only navigating.
- Low migration risk: no database schema or migration changes are
included.
- The PR avoids `pnpm-lock.yaml` and `.github/workflows` changes.
## Model Used
OpenAI Codex, GPT-5-class coding model, tool-enabled local repository
workflow with shell, git, 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>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - Skills can be installed, inspected, filtered, and grouped inside
company settings
> - Skill category metadata already exists in the data model and list
filters, but users could not edit categories after a skill was created
or imported
> - That made category filters and counts drift from the way operators
actually want to organize their skills
> - This pull request adds category editing to the existing skill
settings dialog and sends those edits through the existing
company-scoped skill update API
> - The server mutation now includes category information in the
activity log so settings changes are auditable
> - The benefit is that operators can keep installed skills organized
without reinstalling or recreating them
## Linked Issues or Issue Description
No duplicate or closely related public GitHub issues or PRs were found
for `skill categories settings`.
Feature request fields:
**Subsystem affected**
Cross-cutting: `server/` REST API routes/services and `ui/` React board
settings.
**Problem or motivation**
Company operators can create or import skills with categories, and
Paperclip already exposes category filters and category counts. After
installation, though, operators could not edit a skill's categories from
the skill detail settings screen. That made it difficult to keep skills
grouped correctly as workflows evolved.
**Proposed solution**
Add category editing to the existing skill settings dialog. The category
field accepts comma-separated values, normalizes them into slugs,
deduplicates repeated categories, allows clearing all categories, and
saves categories together with the existing sharing setting through the
company-scoped skill update API.
**Alternatives considered**
One alternative was to keep categories editable only during
create/import flows, but that forces users to recreate or reinstall
skills just to adjust grouping metadata. Another was a separate
categories-only action, but batching settings into one explicit Save
action keeps the dialog predictable.
**Roadmap alignment**
This supports the completed Skills Manager roadmap area by making
installed skills easier to organize and maintain inside company
settings.
**Additional context**
The server already persisted skill categories and supported category
list filters/counts. This PR wires the existing metadata into the
settings editing path and adds focused route, service, and UI tests.
## What Changed
- Added category editing to the skill detail settings dialog, including
comma-separated input, normalized deduplication, reset, dirty-state
handling, and save feedback.
- Updated the skill settings mutation path to save categories and
sharing scope together, then refresh detail/list cache entries.
- Included updated categories in `company.skill_updated` activity
details.
- Added server route/service coverage for category updates,
normalization, filtering, counts, clearing, and activity logging.
- Added UI coverage for saving category edits, clearing categories,
reordered no-op category sets, saving sharing changes together, and
preserving draft input after a failed save.
- Updated the Storybook skill detail harness for the renamed settings
callback props.
## Verification
- `pnpm run preflight:workspace-links && pnpm exec vitest run
server/src/__tests__/company-skills-routes.test.ts
server/src/__tests__/company-skills-service.test.ts
ui/src/pages/CompanySkills.test.tsx`
- Latest-head GitHub checks are green for typecheck, build, e2e, general
tests, serialized server suites, policy, commitperclip review, Socket
Security, Snyk status, and Canary Dry Run.
- Greptile Review succeeded on the latest head with zero unresolved
review threads.
## Risks
Low risk. The change uses the existing company skill update API and
category normalization path. The main behavioral change is that the
settings dialog now batches sharing and category edits behind an
explicit Save button instead of saving sharing immediately on select
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 coding agent based on GPT-5, with tool-enabled repository
inspection, shell execution, git, and GitHub CLI access.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have 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>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - Agents and board users operate inside a company-scoped control plane
where permissions decide which mutating actions they can perform
> - Company skills are part of the reusable agent-company setup surface,
but skill mutation had been coupled to broader agent-creation authority
> - That coupling meant importing or managing skills required a
permission that also implies hiring power, which is broader than the
operation needs
> - Paperclip already has a grant-based permission vocabulary, so skill
mutation should be authorized through a dedicated `skills:create`
capability while preserving existing default behavior for trusted agents
> - This pull request adds the skill creation permission contract,
enforces it on company skill mutations, exposes it in agent permission
management, and documents the changed CLI/API expectations
> - The benefit is a narrower, auditable permission path for skill
import/create/update/delete flows without forcing agents to receive
broader agent-creation authority
## Linked Issues or Issue Description
No public issue is linked.
Problem: company skill mutation APIs were effectively tied to broader
agent creation authority. This PR splits skill mutation authorization
onto the public `skills:create` permission while keeping existing
default skill creation behavior for agents unless explicitly disabled.
Related public PR found during duplicate search: #5330. That PR uses an
older `canManageSkills` shape; this PR implements the `skills:create`
grant path instead.
## What Changed
- Added `skills:create` to shared permission constants and agent
permission types/validators as `canCreateSkills`.
- Backfilled default human/member role grants for `skills:create`.
- Updated company skill mutation routes to require board/user or agent
access to `skills:create`, while preserving legacy/default agent
behavior through `canCreateSkills` unless explicitly disabled.
- Updated agent permission update handling, UI permission controls,
duplicate-agent payloads, plugin SDK fixtures, and agent detail API
surfaces for `canCreateSkills`.
- Added regression coverage for skill route authorization, permission
schema/default behavior, invite grants, omitted permission updates, and
duplicate-agent payloads.
- Updated CLI and Paperclip skill documentation for the new skill
creation permission.
## Verification
- `pnpm exec vitest run
server/src/__tests__/agent-permissions-service.test.ts
server/src/__tests__/agent-permissions-routes.test.ts
server/src/__tests__/company-skills-routes.test.ts
server/src/__tests__/invite-join-grants.test.ts
ui/src/lib/duplicate-agent-payload.test.ts` — 5 files, 90 tests passed.
- `pnpm --filter @paperclipai/shared typecheck && pnpm --filter
@paperclipai/server typecheck && pnpm --filter @paperclipai/ui
typecheck` — passed.
- `pnpm test:run ...changed files...` was attempted first, but the
stable wrapper rejects explicit file arguments; direct Vitest was used
for the same targeted files.
## Risks
- Moderate authorization risk: this changes the gate for company skill
mutations, so the tests cover board grant checks, agent explicit grant
checks, legacy default allowance, and explicit denial.
- Migration/backfill risk is low: the migration only grants
`skills:create` to existing human roles that already need broad
management capability.
- UI/API compatibility risk is low: `canCreateSkills` remains default-on
for full agent permissions, and the update validator preserves omitted
values so unrelated permission edits do not re-enable disabled skill
creation.
> 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 coding agent with terminal/tool use 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>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - The issue thread is where operators watch active agent work and
decide whether a run is healthy.
> - Backend runtime-progress plumbing can expose a concise current
status, but users only benefit if the issue UI renders it in context.
> - The UI should show that current status as part of the active run
surface without turning it into durable transcript content.
> - This pull request adds the issue-thread rendering and message
formatting needed for sandbox runtime status.
> - The benefit is that active sandbox setup phases are visible from the
normal issue detail view.
## Linked Issues or Issue Description
Refs #248
No exact public GitHub issue was found for this UI rendering work. The
underlying problem is that active sandboxed runs can have backend
progress state without any concise issue-thread display, leaving users
to infer whether setup is still moving. This PR adds the UI layer on top
of the runtime-status API branch.
GitHub search performed for related or duplicate work: `sandbox runtime
status`, `sandbox restore index`, and `runtime progress`. No direct
duplicate PR was found.
## What Changed
- Included current runtime status in the heartbeat API client shape.
- Rendered active run status text in the issue chat thread when
available.
- Updated issue-chat message formatting helpers for runtime status
display.
- Added focused component and formatting tests for the new active-run
status behavior.
## Verification
- Local PII scan before push: high-confidence secret patterns, internal
issue links, local user paths, and private URL patterns checked across
all three split diffs; no real secrets or internal links found.
- `git diff --check feat/sandbox-runtime-status..feat/sandbox-status-ui`
- `pnpm exec vitest run ui/src/components/IssueChatThread.test.tsx
ui/src/lib/issue-chat-messages.test.ts` — 2 files, 91 tests passed.
- `pnpm run typecheck` passed on `feat/sandbox-status-ui`.
- `pnpm run build` passed on `feat/sandbox-status-ui`; Vite reported
existing CSS `::highlight` and chunk-size warnings.
- Visual evidence (desktop + mobile) is attached to the tracking issue
rather than committed to the repo.
## Risks
- This PR is stacked on the runtime-status API branch and depends on
that response/event shape.
- The status line is intentionally concise; long or sensitive status
content should continue to be bounded/redacted by the backend.
- The visual change is limited to active issue-thread runs with current
runtime status data.
> 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 GPT-5 via Codex coding agent, with shell/tool execution in a
local worktree. Exact context-window metadata is not exposed by the
runtime.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Paperclip CTO <cto@paperclip.local>
Co-authored-by: Paperclip CTO <noreply@paperclip.ing>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - Sandboxed agent runs can spend meaningful time preparing a remote
workspace before the agent transcript shows useful output.
> - Operators need short, current progress text for those setup phases,
but that text should not become durable run history.
> - The existing live-run websocket path already carries run updates to
the UI, so the backend can reuse that channel instead of adding polling.
> - This pull request adds an ephemeral runtime-progress contract, a
process-local status store, and heartbeat integration for
sandbox-managed runs.
> - The benefit is a clearer active-run experience without database
migrations or persistent progress rows.
## Linked Issues or Issue Description
Refs #248
No exact public GitHub issue was found for this status-message plumbing.
The underlying problem is that active sandboxed runs currently have
setup phases, such as workspace sync and restore, where the operator
cannot see concise current progress through the live run state. This PR
addresses that gap for the backend/runtime layer while keeping progress
messages ephemeral.
GitHub search performed for related or duplicate work: `sandbox runtime
status`, `sandbox restore index`, and `runtime progress`. No direct
duplicate PR was found.
## What Changed
- Added shared runtime-progress types and the `heartbeat.run.progress`
live event type.
- Added a process-local heartbeat run runtime-status store with TTL,
bounded/redacted messages, and terminal cleanup.
- Threaded runtime progress callbacks through heartbeat execution and
active/live run serialization.
- Emitted sandbox-managed runtime phase updates for sync, adapter
startup, restore/export, and finalization paths.
- Added backend and adapter-utils tests for ephemeral status behavior,
terminal cleanup, live serialization, and sandbox progress callbacks.
## Verification
- `pnpm install --frozen-lockfile`
- Local PII scan before push: high-confidence secret patterns, internal
issue links, local user paths, and private URL patterns checked across
all three split diffs; no real secrets or internal links found. The only
secret-like text is an intentional fake test fixture (`sk-test-secret`).
- `git diff --check origin/master..feat/sandbox-runtime-status`
- `pnpm exec vitest run
server/src/services/heartbeat-run-runtime-status.test.ts
server/src/__tests__/heartbeat-runtime-state.test.ts
server/src/__tests__/agent-live-run-routes.test.ts
packages/adapter-utils/src/sandbox-managed-runtime.test.ts` — 4 files,
23 tests passed.
- `pnpm run typecheck` passed on both top stacks that include this
branch: `feat/sandbox-status-ui` and `fix/sandbox-restore-index-sync`.
- `pnpm run build` passed on both top stacks that include this branch;
Vite reported existing CSS `::highlight` and chunk-size warnings.
- `pnpm run test:run` was attempted on `fix/sandbox-restore-index-sync`;
it failed in two unrelated broad-suite tests. One depends on this host's
Git default branch behavior, and one depends on local Claude
model-discovery environment. The changed focused suites above pass.
## Risks
- Runtime progress is process-local by design, so status disappears
after TTL, terminal cleanup, or server restart.
- Clients that do not consume `heartbeat.run.progress` simply keep
existing behavior.
- Message redaction is intentionally generic; overly specific phase
details should stay out of runtime-progress payloads.
> 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 GPT-5 via Codex coding agent, with shell/tool execution in a
local worktree. Exact context-window metadata is not exposed by the
runtime.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Paperclip CTO <cto@paperclip.local>
Co-authored-by: Paperclip CTO <noreply@paperclip.ing>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work, and task status is one of the most-scanned signals across its
whole UI.
> - The Tasks UI shows status through small coloured ring icons and
chips spread across the list, kanban, task detail, the properties
flyout, inline `@`-mentions and the breadcrumb.
> - Those ring glyphs lean heavily on colour to distinguish states,
which is hard to read for colour-blind users, and the status hues were
hard-coded in component classes rather than a single source.
> - We want color-blind-safe, distinct *shapes* per status plus a single
`--status-*` colour-token system the chips and icons share.
> - This pull request adds a unified `StatusGlyph` (one shape per
status) and the `--status-*` colour-token system, and adopts them across
every task-status surface so the new glyphs + colours render by default.
> - The benefit is a more accessible, consistent status language with
one source of truth for status hues.
## Linked Issues or Issue Description
This is a **feature** (no public issue filed). Following the feature
issue template:
- **Problem / motivation:** Task status is communicated mostly by colour
(ring fills/borders), which is hard to distinguish for colour-blind
users, and the status hues are duplicated across component classes with
no single source of truth. Several community PRs have nibbled at parts
of this (see related PRs below).
- **Proposed solution:** A single `StatusGlyph` component with a
distinct *shape* per status (not just colour), backed by a `--status-*`
CSS-variable colour system (base hues + AA-tuned icon hues +
`.status-chip` / `.status-fill` color-mix helpers), adopted across all
task-status surfaces.
- **Alternatives considered:** Recolouring the existing rings in place
(rejected — still colour-only, no shape differentiation).
- **Roadmap alignment:** Additive UI only; no overlap with planned core
work.
Related community PRs (partial / different approaches to the same area —
not duplicates):
- Refs #3806 — Show issue ref and status icon in breadcrumbs and
properties
- Refs #1760 — Improve design of cancelled task status icon
- Refs #1856 — a11y title/aria-label on status and priority icons
## What Changed
- **Colour token system:** `--status-agent-*` / `--status-task-*` base
hues, AA-tuned `--status-task-icon-*` hues (light + dark), and
`.status-chip` / `.status-fill` color-mix helpers in `index.css`;
matching status→CSS-var maps in `status-colors.ts`.
- **`StatusGlyph`** — one `viewBox="0 0 24 24"` glyph per status with
distinct, color-blind-safe shapes (dashed ring, open ring, half-fill,
ring+dot, disc+check, ring+bar, ring+slash, and `in_queue` = the blocked
shape recoloured blue). Sizes `sm`/`md`/`lg`.
- **Adoption (renders by default)** across `StatusIcon`, `StatusBadge`
(agent + issue chips), `MarkdownBody` inline mentions, `IssueRow`,
`IssuesList`, `IssueProperties`, `BreadcrumbBar` + `BreadcrumbContext`,
and the task-detail header/breadcrumb.
- **Tests** for `StatusGlyph`, `StatusIcon`, `StatusBadge`, `IssueRow`,
`IssuesList`, `MarkdownBody` lock the rendered behaviour.
Scope notes: no experimental flag and no Theme Editor surfaces. The
generic `StatusBadge` (runs/goals/approvals) is unchanged.
Project-status recolour is deferred (no in-scope consumer).
## Verification
- `pnpm --filter @paperclipai/shared build` — green (tsc).
- `pnpm --filter @paperclipai/ui build` — green (tsc + vite).
- Targeted unit tests green: `StatusGlyph`, `StatusIcon`, `StatusBadge`,
`IssueRow`, `IssuesList`, `MarkdownBody`, plus consumer suites that
render these (`IssueProperties`, `IssueDetail`, `Search`,
`IssueChatThread`, `IssueFiltersPopover`, `InterruptHandoffViews`) — all
passing.
- Remaining: interactive light + dark visual confirmation across list /
kanban / detail / properties / inline mentions / breadcrumb. The glyph
shapes + AA-tuned hues were previously QA'd on the originating feature
branch.
## Risks
Low risk. Additive UI: a new component + CSS tokens, adopted at existing
status call sites. The generic `StatusBadge` and all non-status UI are
untouched; no schema/migration changes. Behaviour is exercised by unit +
consumer test suites.
## Model Used
Claude Opus 4.8 (`claude-opus-4-8`), extended thinking, with tool use /
code execution (file edits, local builds + vitest).
## 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
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - The new issue dialog lets operators create follow-up tasks and
optionally reuse an existing execution workspace
> - Reusing a workspace depends on a searchable selector that can
include workspace names, branches, and local paths
> - The selector previously treated all matched text equally, so hidden
path text could outrank the visible workspace label and unrelated fuzzy
letter matches could leak into results
> - The reusable workspace popover also needed to stay inside the modal
so scrolling and layering behave like the rest of the dialog
> - This pull request improves the shared searchable select scoring and
applies it to reusable execution workspace choices
> - The benefit is a more predictable workspace reuse flow when an
operator searches by branch, task name, or workspace label
## Linked Issues or Issue Description
No public GitHub issue found for this selector bug.
### Pre-submission checklist
- [x] I have searched existing open and closed issues and this is not a
duplicate.
- [x] I am on the latest released version of Paperclip (or can reproduce
on `master`).
- [x] I have confirmed the error originates in Paperclip itself — not in
my agent adapter, API provider, or local configuration.
### What happened?
Workspace searches could rank hidden path matches ahead of direct
visible label matches, and broad fuzzy matching could match letters
spread across unrelated workspace metadata.
### Expected behavior
Direct label/name matches should sort ahead of weaker hidden metadata
matches, and fuzzy matching should stay constrained enough to avoid
unrelated workspace results.
### Steps to reproduce
1. Open the new issue dialog.
2. Choose reuse existing execution workspace.
3. Search for a term that appears in one workspace label and only in
another workspace path.
4. Observe that the path-only match can rank ahead of the direct visible
label match.
### Paperclip version or commit
Current `master` before this change.
### Deployment mode
Local dev (`pnpm dev`).
### Installation method
Built from source (`pnpm dev` / `pnpm build`).
### Agent adapter(s) involved
- [x] Not adapter-specific (core bug)
### Database mode
Not database-related.
### Access context
Board (human operator).
### Node.js version
Not version-specific.
### Operating system
Not OS-specific.
### Relevant logs or output
No logs; this is client-side selector behavior.
### Relevant config (if applicable)
None.
### Additional context
This PR also keeps the reusable workspace selector popover inside the
modal and contains command-list scroll events to keep the dialog
interaction stable.
### Privacy checklist
- [x] I have reviewed all pasted output for PII (usernames, file paths,
API keys, tokens, company names) and redacted where necessary.
## What Changed
- Added fuzzy scoring helpers for searchable text fields, including
field weights for visible labels versus secondary search metadata.
- Updated `SearchableSelect` to sort filtered results by score while
preserving original order for ties and custom filters.
- Updated reusable execution workspace matching to prefer visible
labels, then descriptions, then hidden search text.
- Kept the reusable workspace selector popover inside the new issue
modal and contained wheel/touch scrolling in the command list.
- Added unit/component coverage for selector ranking, reusable workspace
matching, modal popover containment, and scroll containment classes.
## Verification
- `pnpm exec vitest run ui/src/lib/searchable-select.ts
ui/src/lib/reusable-execution-workspaces.test.ts
ui/src/components/SearchableSelect.test.tsx
ui/src/components/NewIssueDialog.test.tsx`
- `pnpm --filter @paperclipai/ui typecheck`
## Risks
Low risk. The change is scoped to client-side searchable selector
ranking and modal popover behavior. The main behavior shift is that
searches are intentionally less permissive for unrelated fuzzy letter
spreads, which should reduce noisy results but could hide a result
someone previously reached through very loose matching.
## Model Used
OpenAI Codex, GPT-5-based coding agent with repository file access,
shell/tool execution, and medium reasoning effort. Exact hosted model
build and context window were not surfaced 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>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - Agents rely on local skills, provider-backed secrets, and workspace
file previews during normal execution.
> - Local skill imports need bounded reference-file inventory so direct
skill discovery stays accurate without accidentally walking too much of
the filesystem.
> - Secrets provider setup needs actionable AWS discovery errors so
operators can recover from IAM/config problems without losing manual
form input.
> - The issue detail file viewer should reopen cleanly after the first
close so users can keep inspecting files during task review.
> - This pull request collects the small fixes and regression tests for
those related operator workflows.
> - The benefit is more predictable local skill imports, clearer secrets
setup failure states, and a less brittle file preview interaction.
## Linked Issues or Issue Description
- No public GitHub issue was found for this extracted local work.
- Related prior sync context: #8536.
- Problem: local skill reference discovery, AWS provider-vault discovery
errors, and issue file preview reopening each had narrow workflow
regressions that made operator recovery harder.
- Expected behavior: skill imports inventory reference files within
bounded local skill directories, AWS discovery failures present safe
actionable guidance while preserving manual values, and closing the
first file preview does not prevent opening another preview.
- Reproduction scope: import a local skill with referenced files,
attempt AWS Secrets Manager discovery with insufficient IAM/list
permissions, and open/close/reopen file previews from an issue detail
page.
- Duplicate search: searched GitHub PRs/issues for `skill inventory
secrets file viewer` and `skill inventory secrets AWS file viewer`; no
matching public duplicate was found.
## What Changed
- Bounded direct local skill file inventory discovery and added
regression coverage for reference file imports.
- Preserved and surfaced safe, actionable AWS Secrets Manager
discovery/import errors in server responses and the secrets UI.
- Kept AWS provider-vault manual form values intact when discovery fails
or returns no candidates.
- Fixed issue file viewer state so closing the first preview still
allows later file previews to open.
- Updated the secrets render test harness to avoid the missing
`React.act` export in the current React package set.
## Verification
- `pnpm run preflight:workspace-links && pnpm exec vitest run
server/src/__tests__/company-skills-service.test.ts
server/src/__tests__/secrets-routes.test.ts
server/src/__tests__/secrets-service.test.ts
ui/src/context/FileViewerContext.test.ts
ui/src/pages/Secrets.render.test.tsx`
- Result: 5 test files passed, 116 tests passed.
- Install note: the isolated worktree needed `NODE_ENV=development pnpm
install --frozen-lockfile --prod=false --force` before local
verification because it initially had no dev dependencies installed.
## Risks
- Low-to-medium risk: this touches skill import inventory,
secrets-provider error handling, and file-viewer UI state, but each
change is covered by focused regression tests.
- No migrations.
- No dependency or lockfile changes.
- CI is rerunning on the latest head after review fixes; Greptile is 5/5
with no unresolved Greptile threads.
> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.
## Model Used
- OpenAI Codex coding agent, GPT-5-family model as provided in the
Paperclip run environment, with repository tool use and local command
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>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - The board UI, plugin examples, and plugin SDK all share the same
React dependency graph.
> - Dependabot PR #8471 raised `react` and `@types/react`, but left
`react-dom` and `@types/react-dom` on older 19.x ranges.
> - That let dependency resolution install incompatible React runtime
versions, which makes React refuse to run and breaks UI-oriented test
suites.
> - Current PR policy keeps `pnpm-lock.yaml` owned by CI for
non-dependabot authors, so this PR updates manifests and lets CI
regenerate the lockfile artifact.
> - This pull request replaces #8471 with a complete React toolchain
alignment across the package manifests it touched.
> - The benefit is a single React 19.2.7 runtime graph that keeps
dependency consumers from deduping onto a stale `react-dom` patch.
## Linked Issues or Issue Description
Refs #8471
This PR replaces #8471, which updated `react` and `@types/react` but
left `react-dom` and `@types/react-dom` behind. The resulting dependency
graph can install mismatched `react` and `react-dom` versions, producing
React's incompatible-version runtime error in UI tests.
## What Changed
- Aligned `react-dom` package ranges to `^19.2.7` anywhere the React
dependency set is present.
- Aligned `@types/react-dom` package ranges to the 19.2 line.
- Kept the `react@^19.2.7` and `@types/react@^19.2.17` bumps from #8471.
- Added root pnpm overrides for `react` and `react-dom` so peer-only
consumers resolve to the same 19.2.7 runtime instead of a stale patch.
- Left `pnpm-lock.yaml` out of the PR, per the PR workflow policy; CI
regenerates and shares the lockfile artifact when manifests change.
## Verification
- `cd ui && NODE_ENV=test npx vitest run` passed locally before handoff:
243 files / 1739 tests.
- `NODE_ENV=development pnpm install --lockfile-only --ignore-scripts
--no-frozen-lockfile` passed locally on the rebased branch.
- Confirmed regenerated `pnpm-lock.yaml` resolves `react` and
`react-dom` to 19.2.7 and contains no `react@19.2.4` /
`react-dom@19.2.4` entries.
- GitHub Actions are green on this PR, including `policy`, `Typecheck +
Release Registry`, all general test shards, `Build`, `e2e`, and
`verify`.
- Greptile completed at 5/5 with zero unresolved threads.
## Risks
Low risk. This is a dependency-version alignment only, but React
dependency changes can expose package-manager resolution issues. The
root overrides intentionally constrain the React runtime pair to the
same patch version to avoid that class of failure.
## Model Used
OpenAI Codex based on GPT-5, using command-line tool execution and
GitHub CLI operations in a Paperclip-managed workspace.
## 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>
Bumps
[hermes-paperclip-adapter](https://github.com/NousResearch/hermes-paperclip-adapter)
from 0.2.0 to 0.3.0.
<details>
<summary>Commits</summary>
<ul>
<li>See full diff in <a
href="https://github.com/NousResearch/hermes-paperclip-adapter/commits">compare
view</a></li>
</ul>
</details>
<br />
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
<details>
<summary>Dependabot commands and options</summary>
<br />
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
</details>
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
## Thinking Path
> - Paperclip is the open source app people use to manage AI-agent
companies.
> - The control plane coordinates issues, workspaces, documents,
routines, and board review flows across company-scoped data.
> - The local source branch contained related schema, service, and UI
changes for workspace issue scoping and document/routine annotations.
> - These changes need to move together because db schema, shared types,
server services, and UI consumers form one contract.
> - This pull request extracts the migration-bearing control-plane work
from the source branch onto `origin/master`.
> - The benefit is a standalone branch with deterministic migration
order and focused review for the highest-risk part of the split.
## Linked Issues or Issue Description
No GitHub issue exists for this branch split. Internal source task:
[PAP-11234](/PAP/issues/PAP-11234).
Problem/motivation:
- Workspace operations need explicit issue scoping so readiness and
blocker handling can be derived correctly.
- Document annotations need reliable live updates, save failure
surfacing, normalized activity keys, and better comment panel behavior.
- Routine descriptions need the same annotation contract as issue
documents so operators can discuss and edit routine text without
special-case infrastructure.
Proposed solution:
- Add the workspace-operation `issueId` migration and readiness scoping.
- Add routine document/annotation schema, shared types, services,
routes, and UI editing support.
- Keep the related migrations in one PR so the renumbered `0106` and
`0107` migrations land in a deterministic order after current `master`.
Alternatives considered:
- Split migrations into separate PRs, rejected because that would create
migration-numbering conflicts and make each branch less standalone.
- Merge this with UI polish, rejected because this branch needs deeper
server/db review.
Roadmap alignment:
- Checked `ROADMAP.md`; the roadmap mentions future recurring routine
capabilities generally, but no duplicate implementation PR for these
annotation/workspace changes was found.
## What Changed
- Added `0106_workspace_operations_issue_id.sql` and
`0107_routine_description_annotations.sql`, plus schema exports.
- Scoped workspace readiness to blocker issues and attached workspace
operation issue ids.
- Scoped issue-thread interaction accept finalization to the source run.
- Added routine document annotation contracts across
db/shared/server/UI.
- Improved document annotation live updates, activity-key normalization,
save failure surfacing, and comment panel behavior.
- Added issue workspace property controls and compact
blocked-by/quick-control UI updates.
- Added focused server and UI regression tests for the new contracts.
## Verification
- `CI=true NODE_ENV=development pnpm install --frozen-lockfile
--prefer-offline`
- `NODE_ENV=test pnpm exec vitest
server/src/__tests__/document-annotation-routes.test.ts
server/src/__tests__/issue-thread-interactions-service.test.ts
server/src/__tests__/issues-service.test.ts
server/src/__tests__/routine-document-annotation-routes.test.ts
server/src/__tests__/routines-routes.test.ts
server/src/__tests__/workspace-runtime.test.ts
ui/src/components/IssueDocumentAnnotations.test.tsx
ui/src/components/IssueProperties.test.tsx
ui/src/components/WorkspaceRuntimeControls.test.tsx
ui/src/context/LiveUpdatesProvider.test.ts --run` — 10 files, 273 tests
passed.
- `NODE_ENV=test pnpm -r --filter @paperclipai/db --filter
@paperclipai/shared --filter @paperclipai/server --filter
@paperclipai/ui typecheck` — passed, including db migration numbering
check.
## Risks
- Migration-bearing PR; merge this branch before any later PR that adds
migrations with higher numbers.
- Cross-layer contract risk across db/shared/server/ui, mitigated with
targeted tests and affected-package typecheck.
- Review should pay special attention to company scoping in new
routine/document annotation paths.
> 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 GPT-5 Codex via Paperclip `codex_local` / CodexCoder, GPT-5-class
coding model with tool use and shell execution. Exact runtime snapshot
and context-window setting were not exposed by the Paperclip run
context.
## 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 available from the run context)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] If this change affects the UI, I have included before/after
screenshots (N/A per source task: do not add screenshots/images unless
specifically part of the work)
- [x] I have updated relevant documentation to reflect my changes (N/A;
no public docs changed)
- [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>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - This change touches several already-shipped subsystems —
document-comment annotations, the selected-agent Conference Room chat
surface, routine description annotations, workspace-operation tracking,
and the board polling/inbox UI
> - A batch of incremental fixes and two small backend additions had
accumulated on a local mainline and were deployed to a live instance,
but never landed upstream — so each `origin/master` sync kept
re-diverging
> - Leaving them un-upstreamed means the same delta has to be re-merged
on every sync and risks being lost or silently reverted
> - This pull request rebases that delta cleanly on top of current
`origin/master` (preserving recent upstream work such as reusable
sandbox leases and the relation-list collapse controls) and brings it up
for review
> - The benefit is that mainline and the deployed instance converge, and
these fixes/additions get normal review + CI + Greptile coverage
## Linked Issues or Issue Description
No single GitHub issue tracks this; it is a bundle of bug fixes and two
small feature additions. Following the issue-template fields:
**Bug fixes (what was wrong → what this does):**
- Document comments rendered out of document order, didn't live-update
across clients, swallowed save failures, and lost the markdown text
selection on re-render. Now: doc-order sort, live updates, surfaced
save-failure state, stable selection across re-renders.
- The board polling hot path returned oversized payloads on every poll.
Now: an opt-in `summary` projection trims the heartbeat-run list
payload.
- Assorted UI fixes: sidebar nav peek/streamlining edge cases, markdown
file-viewer re-mount/line-height issues on iOS Safari, inbox badge/skill
deep-link tab selection, and `⌘.` work-mode cycling on iOS.
**Feature additions:**
- `workspace_operations.issue_id` — associate a workspace operation with
the issue that triggered it (new migration `0106`, schema, service,
shared type).
- Routine **description annotations** — comment threads on a routine's
description document, mirroring issue document annotations (new
migration `0107`, `routine_documents` schema, routes/service,
editable-sections UI).
- Selected-agent **Conference Room chat** surface wiring and live
issue-thread updates.
**Related PR:** #8229 (`feat(control-plane): add annotation and
workspace controls`, draft) covers overlapping
annotation/workspace-control territory — flagging it so a reviewer can
reconcile the two rather than double-merging.
## What Changed
- `feat(workspace)`: `workspace_operations.issue_id` migration +
schema/service/type; issue workspace property controls reconciled with
upstream's evolved "Service" row.
- `feat(routines)`: routine description annotations —
`routine_documents` schema, migration `0107`, routes/service,
`editable-sections` UI.
- `fix(document-comments)`: doc-order sort, live updates, save-failure
surfacing, stable markdown selection (+ storybook story, rerender test).
- `feat(chat)`: selected-agent Conference Room chat surface and live
issue-thread updates (`LiveUpdatesProvider`, `issue-chat-messages`,
interactions service).
- `perf(board)`: opt-in `summary` projection for the board
polling/heartbeat-run list payload, plus assorted sidebar / file-viewer
/ inbox / IssueProperties UI fixes.
Organized into 5 logical commits. Migrations are numbered incrementally
after upstream's latest (`0105`) — `0106` then `0107`, no journal
collision.
## Verification
- Built by 3-way merging the deployed delta onto current
`origin/master`; the only merge conflict (`IssueProperties.test.tsx`,
two adjacent test blocks) was resolved in favor of upstream's evolved
"green service link above the workspace row" layout, which matches the
merged component's rendered output.
- Confirmed recent upstream work is preserved post-merge: reusable
sandbox lease teardown (`#8513`), the IssueProperties relation-list
collapse controls, and the sidebar streamlined-nav default.
- Confirmed the net diff vs `origin/master` is exactly the intended
feature delta (65 files) and that overlapping server files (`issues.ts`,
`agents.ts`, `heartbeat.ts`) only add feature code without disturbing
upstream logic.
- This delta is already running on a live deployed instance.
- Full typecheck/test suite + Greptile to run in CI (see checklist).
## Risks
- Two new migrations (`0106`, `0107`). Both are additive (new table /
new nullable column) and ordered after upstream's `0105`; no data
backfill, low risk. If another migration-bearing PR merges first,
renumber before merge.
- Largest blast radius is in the merged overlapping UI/service files;
covered by the existing test suites for those files plus CI.
- Overlaps thematically with draft PR #8229 — reviewers should reconcile
rather than merge both blindly.
## Model Used
Claude Opus 4.8 (`claude-opus-4-8`), extended thinking, with tool use
(git, shell). Agentic coding workflow.
## 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)
- [ ] 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
- [ ] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip is the open source control plane people use to coordinate
AI agents, issues, approvals, comments, and work products.
> - The involved subsystem is issue context: markdown links, issue
properties, related work, lists, filters, inbox/sidebar status, and
plugin-provided external context.
> - The gap is that URLs to external systems currently remain mostly
plain links, so humans and agents must manually open them to understand
status, identity, and liveness.
> - This matters because external work objects such as GitHub issues and
pull requests are part of the operational state of a Paperclip company.
> - The implementation keeps core provider-neutral: shared contracts,
storage, sync, routes, and UI surfaces live in core while providers can
contribute detection and status resolution.
> - This pull request adds the external object reference foundation,
GitHub provider support, issue-surface rendering, filters,
sidebar/list/inbox signals, and test/story coverage.
> - The benefit is that linked external work becomes inspectable
Paperclip context without hardcoding every provider directly into the
UI.
## Linked Issues or Issue Description
No public GitHub issue exists for this work.
Feature request:
- Problem: URLs in Paperclip issues, comments, documents, and related
surfaces do not expose provider status or object identity inline.
- Proposed behavior: detect supported external object URLs, persist
normalized references, refresh provider status, and render concise
status-aware links across issue surfaces.
- Users affected: board users, agents, and maintainers who triage issues
containing external work links.
- Acceptance: external object references are company-scoped,
provider-extensible, visible in key issue surfaces, filterable where
relevant, and covered by focused shared/server/UI tests.
Related PR search:
- No open duplicate PRs found for `external object references`.
- Closed related prior attempt: #4556.
## What Changed
- Added shared external-object contracts, validators, status/liveness
helpers, and plugin protocol declarations.
- Added database schema and additive migrations for external objects,
source mentions, and display metadata.
- Added server services/routes for detecting, syncing, summarizing,
refreshing, and resolving external objects across issues, documents,
comments, projects, and plugins.
- Added a GitHub external-object provider plus plugin SDK authoring
docs.
- Wired UI presentation across markdown links, comments, issue chat,
documents, properties, related work, issue rows, filters, inbox/sidebar
badges, and Storybook stories.
- Rebasing cleanup: moved the branch onto current `master`, repaired
stale worktree provision config, hardened environment-sensitive
tests/mocks, and removed committed screenshot artifacts from the PR
branch to keep the reviewable file set below tool limits.
## Verification
- `pnpm exec vitest run packages/shared/src/external-objects.test.ts
server/src/__tests__/external-object-routes.test.ts
server/src/__tests__/external-objects-service.test.ts
ui/src/components/ExternalObjectPill.test.tsx
ui/src/lib/external-objects.test.ts` passed after rebasing: 5 files, 56
tests.
- Historical branch verification before this PR creation included `pnpm
test:run`, `pnpm -r typecheck`, and `pnpm build`; this PR body does not
claim those were rerun after the final rebase.
## Risks
- Medium: this adds a new cross-surface sync path on
issue/document/comment writes. The implementation uses safe sync
wrappers so external-object failures warn instead of blocking core
mutations.
- Medium: the migrations introduce new tables and indexes. They are
additive and company-scoped.
- Medium: provider-specific URL parsing can miss or misclassify edge
cases. Shared canonicalization tests and provider tests cover current
GitHub shapes.
- Low: UI badge/filter behavior could add visual noise for object-heavy
issues; component tests and Storybook stories cover the intended
surfaces.
> Roadmap checked: `ROADMAP.md` references the plugin system as the
current extension path and does not list a duplicate core feature.
Related long-range docs discuss external references, work products,
preview URLs, and plugin extension points; this PR implements the scoped
external-object reference foundation.
## Model Used
OpenAI Codex, GPT-5 coding-agent runtime, with shell and GitHub CLI tool
use. Reasoning mode: medium. Exact deployed runtime model ID and context
window were not exposed in the 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>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - The issue page is one of the main surfaces where operators read
agent updates and manage task relationships.
> - On narrow mobile viewports, agent chat bubbles kept the desktop
`max-w-[85%]` cap, leaving the right side overly indented.
> - The same issue sidebar can also become noisy when many blockers or
sub-tasks are attached to a task.
> - This pull request adjusts the mobile chat bubble width while
preserving the existing desktop cap.
> - It also limits long issue-relation previews until the operator
expands them, then allows collapsing them again.
> - The benefit is a denser, more readable mobile issue page without
changing the underlying issue data model.
## Linked Issues or Issue Description
No public GitHub issue exists for this internal report.
Bug report:
- On mobile issue pages, agent chat bubbles are unnecessarily narrow
because the desktop max-width applies at all breakpoints.
- Long blocked-by and sub-task relation lists can dominate the issue
properties area before the operator needs the full list.
- Related prior work: #4861 improved issue-thread scale and markdown
polish, but did not address this mobile bubble width regression.
## What Changed
- Added a responsive agent-comment bubble width class so mobile bubbles
use `max-w-[calc(100%-0.5rem)]` and desktop keeps `sm:max-w-[85%]`.
- Added test coverage for the responsive agent bubble classes.
- Added a five-item preview limit for blocked-by and sub-task relation
pills in issue properties.
- Added an inline `and N more...` / `show less` toggle for long
blocked-by and sub-task relation lists.
- Reset relation preview expansion when the `IssueProperties` panel
receives a different `issue.id`.
- Added component test coverage for collapsed, expanded, re-collapsed,
and issue-switch reset states.
## Verification
- Passed: `pnpm exec vitest run
ui/src/components/IssueProperties.test.tsx` (`27 passed`).
- Attempted: `pnpm exec vitest run
ui/src/components/IssueChatThread.test.tsx`.
- Result: failed before reaching the changed assertion because the
existing suite imports `act` from `react`, which resolves to a
non-function in this local Vitest environment.
- Scope: 50 render tests fail with `TypeError: act is not a function`; 8
non-render tests pass.
- Attempted: `pnpm --dir ui exec vitest run
src/components/IssueChatThread.test.tsx` with the same `act is not a
function` result.
- Passed: Greptile rerun reported Confidence Score 5/5 on commit
`e0f9c40b7faedcfb877d4f4b0e9f2c77a6593c16`.
- Passed: all latest-head PR checks/statuses are green, including
Typecheck + Release Registry, Build, general tests, serialized server
suites, e2e, canary dry run, policy, security review, Socket, Snyk, and
Greptile.
## Risks
Low risk:
- The chat change only alters max-width classes on agent comment bubbles
and preserves the desktop cap.
- The relation-list change is presentational; hidden blockers and
sub-tasks remain in memory and can expand/collapse in place.
- Expansion state now resets when navigating between issues in a reused
panel instance.
- Main residual risk is visual polish on real mobile devices because
local browser screenshot verification was not run in this heartbeat;
screenshots were not added because the source task explicitly requested
not adding screenshots/images unless specifically part of the work.
> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.
## Model Used
OpenAI Codex coding agent based on GPT-5, with repository tool use,
shell execution, GitHub connector access, local Vitest verification,
Greptile follow-up, and PR check monitoring.
## 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)
- [ ] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [ ] If this change affects the UI, I have included before/after
screenshots
- [ ] 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
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - Agent configuration is where operators set identity, adapter
behavior, and execution environment defaults.
> - The environment override control should only appear when there is a
meaningful choice beyond the always-available local environment, or when
an existing saved override must remain visible so it can be inspected or
cleared.
> - The previous UI used an "Execution" header, explanatory helper copy,
and verbose inherited-default wording that made the agent config feel
noisier than necessary.
> - This pull request narrows the selector visibility to real
alternatives and forced Kubernetes mode, then updates the visible copy
to match the environment-focused mental model.
> - The benefit is a cleaner agent configuration surface that only asks
operators to make an environment choice when that choice exists.
## Linked Issues or Issue Description
No public GitHub issue exists for this change. Public GitHub searches
for related issues and PRs returned no matches:
- `agent config environment selector`
- `execution environment agent config`
Bug-style issue description:
What happened?
The agent config form could surface an environment override section even
when the operator had no meaningful non-local execution environment
choice. The section was labeled "Execution", included helper/inheritance
text, and described the inherited local default as "Inherit instance
default (Local)".
Expected behavior:
The selector should stay hidden unless there is more than one configured
environment choice, the section should be labeled "Environment", helper
text should be removed, and the inherited default option should read
like `Default: Local`. Existing saved non-local overrides should remain
visible even if the target environment is no longer runnable, so
operators can inspect or clear the stale selection.
Steps to reproduce:
1. Run Paperclip from `master` with environments enabled and only the
implicit local default, or Local plus one runnable non-local
environment.
2. Open an agent configuration form.
3. Inspect the environment override section visibility and copy.
Paperclip version or commit: `0b945f449` / current `master` before this
branch.
Deployment mode: Local dev (`pnpm dev`).
Installation method: Built from source.
Agent adapters involved: Not adapter-specific.
Database mode: Not database-related.
Access context: Board operator UI.
## What Changed
- Shows the environment override selector only for forced Kubernetes
mode, at least one runnable non-local environment, or an existing saved
non-local override.
- Keeps Local out of the runnable environment count so Local-only setups
do not show a redundant selector.
- Preserves stale saved non-local overrides in the selector options so
operators can see and clear them.
- Renames the section header from "Execution" to "Environment".
- Removes the helper/inheritance text above the selector.
- Changes the inherited option copy from `Inherit instance default
(...)` to `Default: ...`.
- Adds focused render tests for Local-only hiding, Local plus one
runnable non-local environment showing the concise selector copy, and
stale non-runnable override recovery.
## Verification
- `pnpm --filter @paperclipai/ui exec vitest run
src/components/AgentConfigForm.render.test.tsx`
- `pnpm --filter @paperclipai/ui typecheck`
- `git diff --check origin/master...HEAD`
- Checked `ROADMAP.md` for overlapping planned core work.
- Searched public GitHub issues and PRs for duplicate or related work;
no matches found.
## Screenshots
Before, when the selector was visible:
```text
Execution
Environment override
Inheriting the instance default: Local.
[Inherit instance default (Local)]
```
After, when Local plus a runnable non-local environment exists:
```text
Environment
Environment override
[Default: Local]
[E2B · sandbox]
```
After, when only Local is configured:
```text
(no Environment section is rendered)
```
## Risks
Low risk. This is isolated to the agent config UI. The main behavioral
risk is hiding the selector in an environment edge case; the current
branch keeps forced Kubernetes mode, runnable non-local environments,
and pre-existing saved non-local overrides visible. The added render
tests cover the Local-only, single non-local alternative, and stale
override cases.
## Model Used
OpenAI Codex local adapter, GPT-5-based Codex coding session. The exact
hosted backend model ID is not exposed in the runtime. Tool use and
local shell 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>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - The board UI has an experimental streamlined left navigation mode
that changes how projects and agents appear in the sidebar.
> - Today that mode is opt-in, so users keep seeing the classic
navigation unless they find and enable the experiment.
> - The requested product behavior is to make streamlined navigation the
default experience while keeping an explicit experiments opt-out.
> - This pull request flips the shared/server default, updates UI
consumers to treat only explicit `false` as classic mode, and covers
both the default-on path and opt-out behavior in tests.
> - The benefit is that new and legacy settings get the streamlined
sidebar by default without removing the classic-sidebar escape hatch.
## Linked Issues or Issue Description
Refs #7645
Related: #8430 takes the broader route of removing the classic sidebar.
This PR intentionally keeps the opt-out path.
## What Changed
- Default `enableStreamlinedLeftNavigation` to `true` in shared
validation and server-side normalization.
- Preserve explicit stored `false` as the experiments opt-out for the
classic sidebar.
- Render the sidebar and experimental settings toggle as streamlined-on
unless the setting is explicitly `false`.
- Add regression coverage for loading/default streamlined sidebar
behavior and the opt-out patch from the experiments page.
- Remove internal issue identifiers from newly touched source comments
before publishing.
## Verification
- `git diff --check origin/master...HEAD` — passed.
- `pnpm -r typecheck` — passed.
- `pnpm exec vitest run
server/src/__tests__/instance-settings-service.test.ts
ui/src/components/Sidebar.test.tsx
ui/src/pages/InstanceExperimentalSettings.test.tsx` — passed, 3 files /
27 tests.
- `pnpm build` — passed, with existing Vite/CSS/chunk-size warnings.
- `pnpm test:run` — failed in unrelated
`server/src/__tests__/workspace-runtime.test.ts`: the test `auto-detects
the default branch via symbolic-ref when origin/HEAD is set` creates a
temp repo on `main` then runs `git push -u origin main master`; `master`
does not exist in that temp repo. Summary: 1 failed, 214 passed, 1780
tests passed, 1 skipped.
## Risks
Low-to-medium behavioral risk: the default sidebar changes for users who
never explicitly set the experiment. Explicit `false` remains respected,
so users can still opt out via experimental settings.
> 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 GPT-5 via Codex local adapter, with tool use and code execution.
Exact context window was not surfaced in the runtime.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - The board issue thread composer is the subsystem involved here.
> - The composer can show both the mention coaching warning and the
handoff preview row near the send controls.
> - The handoff preview row sat flush against nearby composer UI, making
the warning and send-control area feel crowded.
> - This pull request adds vertical spacing around the visible handoff
preview row and avoids rendering a spacer when there is no preview.
> - The benefit is a cleaner, less cramped composer layout without
changing handoff behavior.
## Linked Issues or Issue Description
Refs: PAP-11281
Refs: PAP-11598
No public GitHub issue exists for this Paperclip task, so the issue is
described inline using the bug report template fields.
### What happened?
In the issue chat composer, the handoff preview row could render flush
against the mention coach warning above it and the send controls below
it.
### Expected behavior
When the handoff preview is visible, it should have a small amount of
vertical breathing room. When there is no preview, no empty wrapper
should add phantom margin.
### Steps to reproduce
1. Open an issue thread.
2. Type a reply that produces an active handoff preview near the mention
warning/send controls.
3. Inspect the composer spacing around the handoff preview row.
### Paperclip version or commit
Reproduced on the PAP-11281 work branch before this fix.
### Deployment mode
Local dev worktree.
## What Changed
- Added a `my-2` wrapper around `ComposerHandoffPreviewRow` in
`IssueChatThread`.
- Guarded the wrapper behind a named
`shouldRenderComposerHandoffPreview` helper so empty previews do not add
phantom vertical margin.
- Added a focused UI unit test covering the empty-preview and
visible-preview spacing guard.
## Verification
- `pnpm --filter @paperclipai/ui exec vitest run
src/components/IssueChatComposerHandoffPreview.test.ts` passed locally.
- `pnpm --filter @paperclipai/ui typecheck` passed locally.
- `git diff --check` passed before the follow-up commit.
- Reviewed the one-file UI diff and focused test diff.
Screenshots: intentionally omitted for this tiny spacing-only branch
because PAP-11598 explicitly says not to add design screenshots or
images to this PR unless they are specifically part of the work. No
screenshot or wireframe image files are committed.
## Risks
Low risk. This is a narrowly scoped layout-only change in the composer.
The main risk is slightly different vertical rhythm around the handoff
preview row.
> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.
## Model Used
OpenAI Codex coding agent, GPT-5-class model in tool-use/code-execution
mode. Exact hosted deployment identifier and context window were not
exposed in the runtime.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] If this change affects the UI, I have included before/after
screenshots
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
Notes for draft status: remote CI and Greptile are re-running after the
follow-up push. The checklist records the required PR-ready criteria;
this PR should remain draft until GitHub reports those checks green.
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - Local adapters (Claude, Codex, Cursor, Gemini, Grok, OpenCode, Pi,
ACPX) ship bundled "skills" — opinionated Markdown prompt bundles
materialized into the agent's runtime
> - One of those bundled skills, `paperclip-dev`, existed to let agents
develop Paperclip itself; it has now moved to its own external repo and
no longer belongs in the core tree
> - The adapter skill model also carried a `required` / `requiredReason`
attribute plus a `paperclip_required` `AdapterSkillOrigin` variant, all
of which only existed to mark bundled skills as non-optional in the UI
and adapter sync logic
> - With `paperclip-dev` gone, no bundled skill is "required" anymore,
and the type / runtime surface for `required` is dead weight — but it is
computed at request time and never persisted, so a clean removal is safe
(no compatibility shim needed)
> - This pull request deletes `skills/paperclip-dev/` and removes every
trace of the `required` / `requiredReason` field and the
`paperclip_required` origin across shared types, validators,
adapter-utils, all eight local adapters, server routes, the
company-skills service, the UI, the storybook fixtures, and the test
suite
> - The benefit is a smaller, simpler adapter-skill surface: one origin
(`company_managed`) for managed bundled skills,
`resolvePaperclipDesiredSkillNames` collapses to "just the configured
desired set", and the AgentDetail skills tab no longer renders a
"Required by Paperclip" section that no longer applies
## Linked Issues or Issue Description
<!-- No existing public GitHub issue; describing the underlying work
inline (feature_request template fields). -->
**Summary**
Remove the bundled `paperclip-dev` skill (now maintained in its own
external repo) and retire the `required` / `requiredReason` skill
attribute and the `paperclip_required` skill origin, which only existed
to support it.
**Problem or motivation**
`paperclip-dev` is the only bundled skill that was ever marked
"required". Now that it lives in a separate repository, shipping it
inside the core tree is wrong, and the entire `required` surface (a type
field, a validator field, a synthesized `paperclip_required` origin, UI
"Required by Paperclip" section, and required-skill merging in the
desired-skills calculation) becomes dead weight. The `required` value is
computed at request time and never persisted, so it can be removed
cleanly without a migration or compatibility shim.
**Proposed solution**
Delete `skills/paperclip-dev/`, drop the `required` / `requiredReason`
fields and `paperclip_required` origin everywhere they are produced or
consumed, collapse managed-skill origin to a single `company_managed`
value, and simplify `resolvePaperclipDesiredSkillNames` to return only
the configured desired set.
**Alternatives considered**
Keeping the `required` attribute as a no-op for forward compatibility —
rejected because it is request-time only (nothing persists it), so
leaving it in place is pure dead surface area with no callers.
**Roadmap alignment**
Internal cleanup / dead-code removal that simplifies the adapter-skill
surface; it does not introduce or duplicate any planned core feature in
ROADMAP.md.
## What Changed
- Deleted bundled `skills/paperclip-dev/` (moved to a separate repo).
- Dropped `required`, `requiredReason`, and the `paperclip_required`
origin from `packages/shared/src/types/adapter-skills.ts`,
`packages/shared/src/validators/adapter-skills.ts`, and
`packages/adapter-utils/src/types.ts`.
- In `packages/adapter-utils/src/server-utils.ts`: removed
`readSkillRequired()`; dropped `required`/`requiredReason` from
`listPaperclipSkillEntries()`,
`normalizeConfiguredPaperclipRuntimeSkills()`,
`buildPersistentSkillSnapshot()`, and `PaperclipSkillEntry`; collapsed
`buildManagedSkillOrigin()` to always return `company_managed`;
simplified `resolvePaperclipDesiredSkillNames()` to return only the
configured desired set (signature preserved so adapter call sites are
untouched).
- Walked all eight local adapters (`acpx-local`, `claude-local`,
`codex-local`, `cursor-local`, `gemini-local`, `grok-local`,
`opencode-local`, `pi-local`) and removed every remaining
`requiredReason` / `paperclip_required` reference.
- `server/src/services/company-skills.ts`: dropped the `required =
sourceKind === "paperclip_bundled"` synthesis when listing runtime skill
entries.
- `server/src/routes/agents.ts`: removed required-skill merging from the
desired-skills calculation in the persist-config path and the
unsupported-snapshot path (keeping the current version-aware
`desiredSkillEntries` structure).
- `ui/src/pages/AgentDetail.tsx`: dropped required-based filters, the
required tooltip, and the entire "Required by Paperclip" section from
the agent skills tab; storybook fixtures in
`ui/storybook/stories/acpx-local.stories.tsx` cleaned up to match.
- Tests: deleted the `required: false` case in
`paperclip-skill-utils.test.ts` and the "keeps required bundled skills
installed" case in every `*-local-skill-sync.test.ts`;
`acpx-local-execute.test.ts`, `cursor-local-execute.test.ts`,
`cursor-local-skill-sync.test.ts`, `agent-skills-routes.test.ts`, and
`packages/adapter-utils/src/server-utils.test.ts` were updated to drop
removed fields and map `origin: "paperclip_required"` →
`"company_managed"`.
- `server/src/adapters/registry.ts`: two `as unknown as
ServerAdapterModule["..."]` casts on `hermesListSkills` /
`hermesSyncSkills` (matching the existing `executeHermesLocal` pattern).
`hermes-paperclip-adapter@0.2.0` still depends on the published
`@paperclipai/adapter-utils` which keeps the retired
`paperclip_required` variant; the cast bridges the
workspace-vs-published type mismatch at the registry seam and can drop
once hermes upgrades.
## Verification
Run from the workspace root:
```sh
grep -rn "skills/paperclip-dev" .
grep -rn "paperclip_required" --include="*.ts" --include="*.tsx" .
grep -rn "requiredReason" --include="*.ts" --include="*.tsx" .
pnpm -w typecheck
pnpm --filter @paperclipai/server exec vitest run paperclip-skill-utils
pnpm --filter @paperclipai/server exec vitest run skill-sync
```
The first three greps return only the explanatory comment in
`server/src/adapters/registry.ts` (no live `paperclip_required` /
`requiredReason` usage) and zero `skills/paperclip-dev` source hits.
Locally:
- `pnpm -w typecheck` → all packages this PR touches pass
(adapter-utils, shared, server, ui, cli, and the
cursor/gemini/opencode/pi adapters).
- Affected vitest suites pass: `paperclip-skill-utils`, `server-utils`,
all eight `*-local-skill-sync`, `agent-skills-routes`, and the
`acpx`/`cursor`/`pi` execute suites.
## Risks
- Behavioral shift in the agent skills UI: the "Required by Paperclip"
section disappears. No bundled skill is required anymore, so this only
affects environments that previously surfaced `paperclip-dev` as a
forced-on row; those installs will see the skill move into the regular
"company-managed" list (and be uninstalled on next sync unless
explicitly listed as desired).
- Existing agents may still have the string `"paperclip-dev"` in their
persisted `desiredSkills`. That entry is inert (no source for it to
install from); a one-time DB cleanup is out of scope. Low risk.
- Hermes adapter type bridge: two casts in `registry.ts` paper over a
type-only divergence between the workspace `@paperclipai/adapter-utils`
and the published version still pinned by
`hermes-paperclip-adapter@0.2.0`. Runtime behavior is unaffected because
the retired `paperclip_required` value is no longer produced by anything
in this tree. The casts can be removed once hermes upgrades its
dependency.
## Model Used
- Provider: Anthropic
- Model: Claude Opus 4.7 (`claude-opus-4-7`)
- Capability: agent tool use via Paperclip's `claude_local` adapter
## 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)
- [ ] 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: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - Environments are configured through a JSON-schema-driven form
(`JsonSchemaForm`), and each sandbox provider (here, Daytona) supplies a
manifest describing its fields
> - In the Daytona environment config form, the "memory" field rendered
as a free-text input; on save the value round-tripped to `0`, producing
a server-side "number must be greater than zero" error even when the
user typed a valid number like `2`
> - Rather than patch the free-text input, memory is now a true dropdown
of the sandbox sizes Daytona actually supports, so an invalid value can
no longer be typed or coerced
> - The dropdown leads with a blank "None" row that is selected by
default (meaning "not configured — use Daytona's defaults"); `0` is
never offered because it is not a valid configuration
> - The benefit is that operators pick a valid memory size from a
constrained list, it saves correctly as an integer, and leaving it unset
cleanly omits the field instead of submitting `0`
## Linked Issues or Issue Description
No public GitHub issue exists; describing the bug inline per the bug
report template.
### What happened?
In the Daytona environment configuration form, the memory field is a
free-text input labelled "gigabytes of RAM". Entering a value such as
`2` and saving coerced the value to `0`, and the server rejected the
save with "the number must be greater than zero". There was no way to
enter a valid memory size through the form.
### Expected behavior
Selecting a valid memory size (e.g. `2`) keeps that value and saves
cleanly as an integer. Leaving memory unset is valid and submits no
value (Daytona defaults apply). `0` is never selectable.
### Steps to reproduce
1. Open the environment configuration form for a Daytona sandbox
provider.
2. In the memory field, type `2`.
3. Click Save.
4. Observe the value becomes `0` and the form errors with "the number
must be greater than zero".
## What Changed
- Daytona manifest: `memory` is now an `enum` of the supported sandbox
sizes `[1, 2, 4, 8]` (GiB). It stays optional, so "not configured"
remains valid. `0` is not in the list.
- `JsonSchemaForm` `EnumField`: optional enums now render a leading
blank **None** row that is selected by default when no value is set,
letting the user express "not configured" and clear a previous selection
(Radix `Select` forbids an empty-string item value, so the unset state
maps to a sentinel that translates back to `undefined`).
- `JsonSchemaForm` `EnumField`: when every enum option is numeric, the
selected value is coerced back to a number on change so the payload
keeps the schema's integer type (a stringified `"2"` would otherwise
fail server-side integer validation — this is what fixes the original
bug for the dropdown path).
- Tests: added `EnumField` coverage in `JsonSchemaForm.test.tsx` (blank
row present, no `0`, blank selected by default, numeric coercion, blank
→ unset) and Daytona manifest coverage in the plugin test (memory enum
is `[1,2,4,8]`, excludes `0`, stays optional).
## Verification
- `cd ui && npx vitest run src/components/JsonSchemaForm.test.tsx
src/pages/CompanyEnvironments.test.tsx` → 16/16 passing (14 + 2).
- `vitest run` in `packages/plugins/sandbox-providers/daytona` → 19/19
passing (incl. 3 new manifest tests).
- `cd ui && npx tsc --noEmit` → clean.
- Behavior: the memory field now renders as a dropdown showing **None /
1 / 2 / 4 / 8**, with **None** selected by default. Picking `2` stores
the integer `2`; picking **None** clears the field so it is omitted from
the payload (no `0`, no "must be greater than zero" error).
## Risks
- Low risk. The blank-row + numeric-coercion changes live in
`EnumField`. The blank row is only added for **optional** enums
(required enums are unaffected); numeric coercion only triggers when
every option is numeric, so existing string enums (`egressMode`,
`backend`, `sessionStrategy`) are unchanged. The manifest change is
scoped to the Daytona provider.
## Model Used
Claude (Anthropic), Opus-class model, used via the Paperclip agent
workflow (author + reviewer agents) with tool use 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 (none found)
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links
- [x] My branch name describes the change and contains no internal
Paperclip ticket id
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have considered and documented any risks above
- [x] I will address all Greptile and reviewer comments before
requesting merge
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - Agent adapter configuration is where operators edit the runtime
settings that control how each agent is invoked.
> - The adapter form includes a test action so operators can validate
the in-progress configuration before saving it.
> - In edit mode, that test action changed into `Save + Test` when the
form was dirty, which mixed validation with persistence.
> - Operators already have an explicit Save action for committing
adapter changes.
> - This pull request makes the adapter test action test-only so
validation never implicitly saves a dirty draft.
> - The benefit is clearer operator control: Test validates the current
draft, Save persists it.
## Linked Issues or Issue Description
No matching public GitHub issue or in-flight PR was found, so this PR
describes the bug inline.
### Pre-submission checklist
- I searched existing open and closed issues and did not find a
duplicate.
- I can reproduce this behavior on current `master` before this branch.
- I confirmed this originates in the Paperclip UI, not in a specific
local agent CLI or provider configuration.
### What happened?
When editing an existing agent adapter configuration, changing an
adapter setting made the adapter environment test button change from
`Test` to `Save + Test`. Clicking it saved dirty edits before running
the environment test.
### Expected behavior
The test action should always be a test action. Dirty draft values
should be validated without persisting them, and the explicit Save
button should be the only save path.
### Steps to reproduce
1. Open an existing agent adapter configuration.
2. Edit an adapter setting so the form becomes dirty.
3. Observe the adapter environment test action label and behavior.
### Paperclip version or commit
Current `master` before this branch, `c0743482b`.
### Deployment mode
Local dev/private operator UI.
### Installation method
Built from source with pnpm.
### Agent adapter(s) involved
Not adapter-specific. The behavior lives in the shared agent adapter
config form.
### Database mode
Not database-related.
### Access context
Board operator UI.
### Additional context
Public searches performed before opening this PR:
- `adapter test save`
- `Save + Test`
- `AgentConfigForm testEnvironment`
- `adapter configuration test button`
No logs or local config are needed for this UI-only behavior report.
## What Changed
- Removed the helper that converted dirty edit-mode adapter tests into
`Save + Test`.
- Removed the save-before-test helper path so clicking Test only invokes
the adapter environment test mutation.
- Kept draft testing behavior by continuing to build the test payload
from the current in-progress adapter config.
- Dropped unit tests that asserted the old save-and-test behavior.
## Verification
- `pnpm vitest run ui/src/components/AgentConfigForm.test.ts` -> 6/6
passed.
- `pnpm --filter @paperclipai/ui typecheck` was attempted locally and
failed on the existing unrelated
`packages/plugins/sdk/src/ui/components.ts` React module-resolution
error before reaching this change.
- Static review: `Save + Test`, `getAgentConfigTestActionLabel`, and
`runAgentConfigEnvironmentTest` no longer appear in `ui/src`.
- Before/after visual state for the dirty edit-mode adapter test action:
- Before: `Save + Test`
- After: `Test`
- PR CI passed: policy, commitperclip review,
typecheck/release-registry, build, general tests, serialized server
suites, e2e, canary dry run, aggregate verify, and security scans.
- Greptile passed with 5/5 confidence. The remaining
screenshot/documentation thread was answered and resolved because this
is a text-only button-label state already captured above.
## Risks
Low risk. This is a narrow UI behavior change in the adapter config
form. The main behavior change is intentional: testing a dirty adapter
draft no longer persists it. If an operator expected Test to save edits,
they must now use the explicit Save button.
## Model Used
- Anthropic Claude via Paperclip `claude_local` produced the initial
implementation commit.
- OpenAI GPT-5 Codex via Paperclip `codex_local` reviewed the change,
amended public commit metadata, pushed the branch, and prepared this
pull request. The adapter did not expose an exact context-window value
in the run payload; tool use and local shell execution were used.
## 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] If this change affects the UI, I have included before/after
screenshots
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip is the control plane for managing AI-agent work, and the
UI needs to stay legible because operators use it to steer execution.
> - This change sits in the Company Environments screen inside the
Paperclip app UI.
> - The screen carried redundant headings and several overlapping helper
sentences that added clutter without adding meaning.
> - The request was presentation-only: remove redundant copy and
simplify labels, with no change to environment-selection behavior.
> - The safest path was a single-file edit in `CompanyEnvironments.tsx`
that preserved every control and only removed/renamed text.
> - This is a `style:` change (UI copy/presentation), so no behavior is
altered and no new test is warranted.
## Linked Issues or Issue Description
No public GitHub issue exists for this small UI cleanup, so the problem
is described here instead.
- The Company Environments screen had redundant headings and overlapping
helper copy.
- The default environment control used a verbose label and a Local
option label that added noise without adding meaning.
- Requested outcome: simplify the copy so the screen is easier to scan
while preserving the same behavior.
Related public PRs reviewed for overlap:
- Refs #8380
- Refs #8391
- Refs #8398
- Refs #4902
## What Changed
- Removed the extra top-level `Environments` header inside the screen
content (both enabled and disabled states).
- Removed the introductory paragraph above the environment list.
- Renamed `Instance default environment` to `Default` and removed its
helper sentence.
- Removed the duplicate helper text below the default environment
select.
- Changed `Local (built-in default)` to `Local`.
- Removed the redundant `Saved environments` section heading.
- Removed the empty-state line `No saved environments yet. Local remains
the default until you add another target.`
- Removed the previously added UI test, which only asserted the
now-final copy and added no lasting value.
- Removed the now-unused `Settings` import and
`instanceDefaultEnvironment` variable.
## Verification
- Ran the existing `CompanyEnvironments` vitest suite locally: 4 passed.
- This is a presentation-only copy change with no API, state, or
behavior changes.
- Reviewer can open Company Settings -> Environments and confirm the
screen matches the requested wording cleanup with no behavior change.
## Risks
- Low. Copy-only UI change with no API or state-management changes.
- The only real risk is removing more context than intended from the top
of the screen; review should focus on clarity.
> 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
- Anthropic Claude Opus 4.8 via the Claude local adapter with tool use
in a Paperclip-managed workspace.
## 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)
- [ ] 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] This is a `style:` copy-only change; no new test is warranted
(test-coverage gate skips `style:`)
- [ ] If this change affects the UI, I have included before/after
screenshots
- [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
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - Agents run in execution environments, and the Company Environments
settings page lets operators configure them
> - That page rendered an "environment support by adapter" matrix table
plus two descriptive text boxes explaining the adapter/driver/sandbox
support model
> - That matrix duplicated information already surfaced where operators
actually pick a driver/provider, and added a wide, dense table that
provided no actionable value on this screen
> - This pull request removes the support-matrix table and the two
explanatory text boxes above it, along with the now-orphaned helper
component, derived constant, and unused imports
> - The benefit is a simpler, less cluttered environments settings page
that only shows the controls an operator acts on
## Linked Issues or Issue Description
No public GitHub issue exists for this. Describing the change following
the feature-request template:
### Problem or motivation
The Company Environments settings page showed an adapter support matrix
table (Adapter × Local/SSH/Sandbox) and two paragraphs of descriptive
text above it. The table restated the static adapter/driver support
model and the sandbox-provider plugin caveat, neither of which is
actionable on this page — operators don't change adapter capabilities
here, they create and edit environments. The table was wide enough to
require horizontal scroll and added visual noise without helping the
operator complete any task.
### Proposed solution
Remove the support-matrix table and the two descriptive text boxes above
it from `CompanyEnvironments.tsx`, along with the now-unused helper
component, derived constant, locals, and imports. Leave all environment
create/edit controls, provider selection, and sandbox-provider logic
untouched.
### Alternatives considered
Collapsing the table behind a disclosure/"Learn more" toggle instead of
removing it — rejected because the information is static, non-actionable
on this screen, and already available where operators pick a
driver/provider; hiding it would keep the maintenance cost without
adding value.
### Roadmap alignment
Not a roadmap feature — this is a small, focused UI cleanup that removes
non-actionable content from an existing settings page.
Related (already merged) work that last touched this copy:
paperclipai/paperclip#4902 (clarified sandbox-provider messaging in
company environments). No open duplicate PR was found.
## What Changed
- Removed the adapter support matrix `<table>` from
`ui/src/pages/CompanyEnvironments.tsx`
- Removed the two descriptive text boxes rendered above that table
(adapter support model + installed sandbox providers blurbs)
- Deleted the now-unused `SupportMark` helper component and the
`ENVIRONMENT_SUPPORT_ROWS` derived constant
- Removed the now-unused `sandboxSupportVisible` local and four
now-unused imports (`AGENT_ADAPTER_TYPES`,
`getAdapterEnvironmentSupport`, `Check`, `adapterLabels`)
- Updated `ui/src/pages/CompanySettings.test.tsx` to drop the two
assertions tied to the removed copy
## Verification
- `vitest run src/pages/CompanySettings.test.tsx` → 4/4 pass
- `tsc --noEmit` on the UI project → no errors in the changed files
- The remaining environment create/edit controls, provider selection,
and sandbox-provider logic are untouched (the `environmentCapabilities`,
`discoveredPluginSandboxProviders`, and `sandboxCreationEnabled` values
are still used by the form)
## Risks
Low risk — pure presentational removal of a read-only informational
table and static copy. No API, data model, or behavioral changes; no
state that other components depend on was removed.
## Model Used
Claude (Anthropic), model id `claude-opus-4-8` (Claude Opus 4.x family),
with extended thinking and tool use, run via Claude Code.
## 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
- [ ] If this change affects the UI, I have included before/after
screenshots
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [ ] 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>
## Thinking Path
> - Paperclip is the open source control plane people use to manage AI
agents and the work they do.
> - This change lives in the board UI, specifically the instance
environments settings page where operators define reusable execution
targets.
> - That page currently rendered the create/edit environment form inline
near the bottom of the screen.
> - Because the form appeared away from the action that triggered it, it
was easy to miss and felt inconsistent with the rest of the UI.
> - This pull request moves environment creation and editing into a
proper centered dialog with the existing form behavior preserved.
> - The benefit is a more obvious, consistent, and easier-to-complete
settings flow for environment management.
## Linked Issues or Issue Description
No public GitHub issue matched this exact UI problem when I searched
related issues and PRs.
### Pre-submission checklist
- [x] I have searched existing open and closed issues and this is not a
duplicate.
- [x] I am on the latest released version of Paperclip (or can reproduce
on `master`).
- [x] I have confirmed the issue originates in Paperclip itself, not
adapter or local configuration.
### What happened?
On `Settings -> Instance settings -> Environments`, clicking create or
edit revealed the environment form inline near the bottom of the page.
Because the form appeared away from the action that triggered it, it was
easy to miss.
### Expected behavior
Create and edit should open a centered modal dialog with a dimmed
backdrop and the same form controls.
### Steps to reproduce
1. Open `Settings -> Instance settings -> Environments`.
2. Click the create or edit action for an environment.
3. Observe that the form expands inline on the page instead of opening
in a modal.
### Paperclip version or commit
Observed on current `master` before this PR, for example
[`e93d78b46`](https://github.com/paperclipai/paperclip/commit/e93d78b46).
### Deployment mode
Local dev (`pnpm dev`).
### Installation method
Built from source (`pnpm dev` / `pnpm build`).
### Agent adapter(s) involved
- [x] Not adapter-specific (core bug)
### Database mode
Embedded PGlite (default, `DATABASE_URL` unset).
### Access context
Board (human operator).
### Node.js version
Node.js 25.6.1 in the local contributor environment.
### Operating system
macOS local development environment.
### Relevant logs or output
None. This was a visible UI behavior issue rather than a logged
server/runtime error.
### Relevant config (if applicable)
Not config-related.
### Additional context
Related PR/search context checked before opening this fix. UI preview
screenshots are attached below.
### Privacy checklist
- [x] I have reviewed all pasted output for PII and redacted where
necessary.
## What Changed
- Moved the environment create/edit form in
`ui/src/pages/CompanyEnvironments.tsx` into a shared dialog.
- Added an explicit `Add environment` action near the saved environments
list and wired row edit actions to open the same dialog in prefilled
mode.
- Preserved existing save/test behavior while resetting dialog-local
mutation state when the modal opens or closes.
- Extended `ui/src/pages/CompanyEnvironments.test.tsx` to cover
add-open/cancel and edit-open/save dialog flows.
- Updated `ui/src/pages/CompanySettings.test.tsx` so the existing
environments coverage opens and inspects the dialog through the Radix
portal.
## Verification
- `node node_modules/vitest/vitest.mjs run
ui/src/pages/CompanyEnvironments.test.tsx`
- `node node_modules/vitest/vitest.mjs run
ui/src/pages/CompanySettings.test.tsx`
- Manual review:
- Open `Settings -> Instance settings -> Environments`
- Click `Add environment` and confirm the form opens in a centered
dialog
- Click `Edit` on an existing environment and confirm the dialog opens
with existing values
- Confirm `Cancel`, `Test`, and save actions still behave as expected
- Before/after screenshots:
- [Cancel environment
creation/edit](https://artifacts.cutter.sh/8391/run-1175b5c-2026-06-20T18-47-11/preview/clip-01.mp4)
- [Add a new
environment](https://artifacts.cutter.sh/8391/run-1175b5c-2026-06-20T18-47-11/preview/change-02.png)
- [Save an
environment](https://artifacts.cutter.sh/8391/run-1175b5c-2026-06-20T18-47-11/preview/change-03.png)
## Risks
- Low risk overall because the change is contained to the environments
page and keeps the existing form fields and mutations.
- The main behavior change is modal layout, so the biggest risk is
regressions in tall-form scrolling or smaller-screen dialog ergonomics.
> I checked [`ROADMAP.md`](ROADMAP.md). This is a tightly scoped UI
polish fix, not duplicate roadmap-level core feature work.
## Model Used
- OpenAI GPT-5.4 via the `codex_local` Paperclip adapter
- High reasoning mode with tool use, shell execution, git operations,
and targeted test execution
- Model-assisted authoring and verification of the UI and test changes
## 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] If this change affects the UI, I have included before/after
screenshots
- [ ] 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: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip is the control plane operators use to manage AI-agent
companies.
> - Board operators rely on the settings UI and CLI docs to understand
which product surfaces are stable to depend on.
> - Experimental features already existed in the product, but the
operator-facing contract around them was too soft and too fragmented.
> - That created a risk that users would enable experiments without
being told clearly that they can break, change, or disappear.
> - The docs and the in-product settings page both needed the same
explicit warning language so the contract is visible at the moment of
decision.
> - This pull request adds that warning to the board-operator guide, CLI
references, and the experimental settings page.
> - The benefit is clearer operator expectations without changing the
underlying feature flags or rollout behavior.
## Linked Issues or Issue Description
No public GitHub issue exists for this docs/polish gap.
Problem description:
- Board operators could enable experimental features without a clear
operator-facing statement that those features are opt-in and come
without compatibility guarantees.
- The docs site, repo CLI reference, and in-product experimental
settings page did not present one consistent warning contract.
- This PR closes that gap by documenting the risk explicitly where
operators discover and enable those settings.
Related public search:
- Searched public issues/PRs for related work with `gh search issues
--repo paperclipai/paperclip 'experimental features warning'` and `gh
search prs --repo paperclipai/paperclip 'experimental features
warning'`.
- Reviewed open PR #6165 during that search and found it unrelated; it
changes experimental auth/routing flags rather than documenting
experimental-feature risk.
## What Changed
- Added a new board-operator guide at
`docs/guides/board-operator/experimental-features.md` that defines the
Paperclip contract for experimental features.
- Registered that guide in `docs/docs.json` so it appears in the public
docs navigation.
- Added matching caveat language next to `instance
settings:experimental` in `docs/cli/control-plane-commands.md`.
- Added the same caveat to `doc/CLI.md` so the repo CLI reference does
not drift from the published docs.
- Added a single page-level warning banner to
`ui/src/pages/InstanceExperimentalSettings.tsx` stating that
experimental features are opt-in, carry no compatibility guarantees, and
may change, break, or be removed.
- Added a targeted UI test in
`ui/src/pages/InstanceExperimentalSettings.test.tsx` that asserts
exactly one page-level warning renders with the new risk language.
## Verification
- `jq empty docs/docs.json`
- `git diff --check`
- `cd ui && pnpm vitest run
src/pages/InstanceExperimentalSettings.test.tsx`
- Manual review of the warning contract across:
- `docs/guides/board-operator/experimental-features.md`
- `docs/cli/control-plane-commands.md`
- `doc/CLI.md`
- `ui/src/pages/InstanceExperimentalSettings.tsx`
UI note:
- This is a copy-level warning addition rather than a layout rework. I
did not attach before/after screenshots in this PR body.
## Risks
- Low risk: this changes operator-facing documentation and warning copy,
not feature-flag behavior.
- The main failure mode is wording drift across docs and UI in future
edits, which is why this PR adds the same contract to all relevant
operator-facing surfaces.
> I checked `ROADMAP.md` before opening this PR. This is docs/UI polish
around an existing experimental surface, not overlapping roadmap-level
core feature work.
## Model Used
- OpenAI Codex Local using `gpt-5.4` with high reasoning and tool use
for coordination, review, docs changes, and PR preparation.
- Anthropic Claude Local using `claude-opus-4-8` with high reasoning and
tool use for the in-product warning and targeted UI 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
- [ ] If this change affects the UI, I have included before/after
screenshots
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [ ] 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>
## Thinking Path
> - Paperclip is the control plane for AI-agent companies, so execution
environment selection has to stay inspectable and predictable across
companies, agents, and runs.
> - The environment subsystem decides where an agent heartbeat actually
runs and how remote sandbox state is realized and restored.
> - That subsystem previously mixed company-scoped environment catalogs
with issue-level environment stamping, so a reassigned issue could keep
executing in the previous assignee's sandbox.
> - That behavior breaks the control-plane contract: changing the
assignee should change the executing agent/environment path unless there
is an explicit current override.
> - Fixing it cleanly required more than a narrow patch; the environment
model had to move to instance scope with a single inherited default and
per-agent override semantics.
> - This pull request rewires the schema, server/API surface, runtime
resolution, and UI around that model, then adds regression coverage for
cross-company inheritance and per-agent isolation.
> - The benefit is that environment choice now follows the approved
instance/agent configuration path instead of stale issue state, while
shared environments only need to be configured once per instance.
## Linked Issues or Issue Description
- No directly matching public GitHub issue or PR was found while
searching for this refactor.
### What happened?
Reassigning work between agents with different execution environments
could keep running in the previous sandbox because environment choice
was stamped onto the issue and outranked the current assignee. The same
subsystem also forced environment catalogs to be duplicated per company
even though the underlying execution environments were instance-wide
resources.
### Expected behavior
Execution should resolve through the current instance and agent
configuration path, with one instance-scoped environment catalog, one
instance default, optional per-agent override, and no stale issue-level
environment authority surviving reassignment.
### Steps to reproduce
1. Configure two agents to use different execution environments.
2. Assign an issue to the first agent so the issue records execution
state in that environment.
3. Reassign the same issue to the second agent and run another
heartbeat.
4. Observe that the pre-fix runtime can still sync or execute in the
original sandbox instead of the second agent's environment.
### Paperclip version or commit
Current `master` before this PR.
### Deployment mode
Self-hosted server.
### Installation method
Built from source (`pnpm dev` / `pnpm build`).
### Agent adapter(s) involved
- Claude Code
- Not adapter-specific (core bug in environment authority / resolution)
### Database mode
External Postgres.
### Access context
Both board reassignment and agent heartbeats were involved.
## What Changed
- Moved environments and their default selection contract to instance
scope in DB/shared types, including the migration that dedupes legacy
per-company environments and seeds the instance local default.
- Reworked environment CRUD/auth flows to use instance-scoped APIs and
added route/service coverage for instance-level environment management.
- Changed runtime resolution to prefer `agent default -> instance
default -> built-in local`, removed issue-level environment stamping
from the active execution path, and isolated sandbox/plugin leases by
`(executionWorkspaceId, agentId)`.
- Added environment env-var runtime precedence so environment-provided
values act as the baseline for agent execution.
- Moved the environment UI into instance settings and updated agent
configuration surfaces to reflect inherit/override behavior.
- Added regression coverage for instance-default inheritance across
companies and for the new runtime resolution behavior.
- Fixed a rebase-only duplicate `enableTaskWatchdogs` flag regression in
instance settings types/validators/services so the branch typechecks
cleanly on current `master`.
- Updated stale server tests so CI matches the shipped instance-scoped
environment contract.
## Verification
- `git diff --check`
- `pnpm --filter @paperclipai/shared typecheck`
- `pnpm --filter @paperclipai/db typecheck`
- `pnpm exec vitest run
server/src/__tests__/environment-runtime-driver-contract.test.ts
server/src/__tests__/agent-permissions-routes.test.ts
server/src/__tests__/environment-routes.test.ts
server/src/__tests__/environment-instance-routes.test.ts
server/src/__tests__/execution-workspace-policy.test.ts
server/src/__tests__/heartbeat-plugin-environment.test.ts
server/src/__tests__/instance-settings-routes.test.ts`
## Risks
- The migration changes environment scope and dedupes existing rows, so
installs with unusual legacy environment combinations should be reviewed
carefully during upgrade.
- Remote execution behavior now depends on instance-default inheritance
semantics instead of issue-level stamping, so any remaining code paths
that still assume issue-scoped environment authority would surface as
follow-up bugs.
- This PR includes both server/runtime behavior and UI relocation, so
reviewers should watch for authorization edge cases around instance
settings and environment management.
> I checked [`ROADMAP.md`](ROADMAP.md). This work fits the existing
Cloud / Sandbox agents direction as a bug-fix/refactor to current
behavior, not a new parallel product surface.
## Model Used
- OpenAI Codex coding agent in this Paperclip/Codex session; GPT-5-class
tool-using model with code execution and shell access. The exact backend
model ID is not exposed to the session runtime.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [ ] If this change affects the UI, I have included before/after
screenshots
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [ ] 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
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - Agents run inside environments, and the Environments settings page
lets users configure each environment and verify it with a "Test
provider" / "Test connection" button per row
> - When a user clicked one environment's test button, every environment
row's button switched to the disabled "Testing..." state at the same
time, then all flipped back together
> - That happened because all rows read the same shared
`environmentProbeMutation.isPending` flag, so a single in-flight probe
disabled and relabeled every button
> - This is confusing: it looks like every environment is being tested,
and it blocks interacting with other rows while one probe runs
> - This pull request tracks the specific environment id being probed in
dedicated state and scopes the disabled/label logic to that id
> - The benefit is that only the button the user actually clicked shows
"Testing..." and is disabled, while the other rows stay interactive
## Linked Issues or Issue Description
No public GitHub issue exists, so the bug is described inline below
following the bug report template.
### What happened?
On the Environments settings page, clicking "Test provider" on one
environment caused the test button on *every* environment row to change
to "Testing..." and become disabled at the same time, then all reverted
together when the probe finished.
### Expected behavior
Only the button for the environment the user clicked should show
"Testing..." and be disabled while its probe runs. Every other row's
button should stay enabled and unchanged.
### Steps to reproduce
1. Open instance settings → Environments with two or more configured
environments.
2. Click "Test provider" / "Test connection" on a single row.
3. Observe that all rows' buttons enter the "Testing..." disabled state
simultaneously instead of just the clicked one.
### Paperclip version or commit
`master` at commit a10f17800 (branch
`fix/environments-test-provider-button-scope`).
### Deployment mode
Local dev (`pnpm dev`). UI-only; reproduces independent of backend.
## What Changed
- Added a dedicated `testingEnvironmentId` state in
`ui/src/pages/CompanyEnvironments.tsx` to track which environment is
currently being probed.
- Set it in the probe mutation's `onMutate` and clear it in `onSettled`,
and reset it when the selected company changes.
- Scoped the test button's `disabled` state and `"Testing..."` label to
`testingEnvironmentId === environment.id` instead of the shared
`environmentProbeMutation.isPending` flag.
- Added `ui/src/pages/CompanyEnvironments.test.tsx` verifying that
clicking one environment's test button puts only that row into the
"Testing..." disabled state while other rows stay enabled.
## Verification
- `pnpm typecheck` (UI) passes clean.
- `vitest run src/pages/CompanyEnvironments.test.tsx` passes (new test
fails against the old shared-`isPending` behavior, confirming it guards
the fix).
- Manual: on the Environments page with multiple environments, click one
row's test button and confirm only that button shows "Testing..." / is
disabled while the others remain enabled, then it reverts on completion.
## Risks
- Low risk. Single-file UI change scoped to per-row button state; no
API, schema, or behavior changes to the probe itself. The probe mutation
still runs identically — only which buttons reflect the pending state
changed.
## Model Used
- Claude Opus 4.8 (Anthropic), `claude-opus-4-8`, with extended
reasoning and tool use, via Claude Code.
## 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
- [ ] If this change affects the UI, I have included before/after
screenshots
- [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
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - The web UI renders work items in a task/issue list and board
columns, each row leading with a status circle next to the title text
> - The status-circle wrapper `<span>` was not enforcing cross-axis
centering, so the circle aligned to the top of taller rows instead of
the title
> - This looked broken: the circle floated above the text rather than
sitting centered against it, across statuses
> - This pull request adds flex centering (`inline-flex` +
`items-center`) to the status-icon wrapper spans so the circle is
vertically centered with the title in every row
> - The benefit is a consistent, correctly aligned task list regardless
of task status or row height
## Linked Issues or Issue Description
No public GitHub issue exists; describing inline following the bug
report template.
### What happened?
In the task/issue list and board, the leading status circle was not
vertically centered with the task title text. On taller rows the circle
sat near the top instead of aligned with the title, so the list looked
misaligned across all statuses.
### Expected behavior
The status circle should be vertically centered with the title string in
every task row, regardless of status.
### Steps to reproduce
1. Open the task list (or board) view in the web UI.
2. Observe rows in different statuses, especially taller rows.
3. Notice the status circle is top-aligned rather than vertically
centered with the title text.
### Paperclip version or commit
`master` @ 411a9a51 (branch base for this PR).
### Deployment mode
Local dev (pnpm dev).
## What Changed
- `ui/src/components/IssueColumns.tsx`: added `items-center` to the
leading status-icon wrapper span (`inline-flex` container) in
`InboxIssueMetaLeading`.
- `ui/src/components/IssuesList.tsx`: added `inline-flex items-center`
to the two status-icon wrapper spans so the circle centers with the row
text (matching the `IssueColumns.tsx` change).
## Verification
- Manual: open the task list and board views; confirm the status circle
is vertically centered with the title across statuses and on taller
rows.
- Before/after matches the screenshots from the originating request
(broken: circle top-aligned; fixed: circle centered).
- `cd ui && npx tsc --noEmit` — type check passes for the touched
components.
## Risks
- Low risk. CSS-only change to wrapper spans; no logic, data, or API
changes. Limited to status-icon alignment in the task list/board rows.
## Model Used
- Claude Opus 4.6 (`claude-opus-4-6`), extended thinking + tool use, via
Claude Code.
## 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
- [ ] I have added or updated tests where applicable (CSS-only alignment
change; no meaningful unit test — retitled `refactor:` per contribution
gate)
- [x] If this change affects the UI, I have included before/after
screenshots
- [x] I have updated relevant documentation to reflect my changes (none
required)
- [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>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work, with a web UI that must work on mobile as well as desktop.
> - The UI layout root in `ui/src/components/Layout.tsx` branches on
`isMobile` to choose between a desktop flex column that clips overflow
and a mobile container.
> - The mobile branch was only `min-h-dvh` — it had no
horizontal-overflow guard, while the desktop branch already used
`overflow-clip`.
> - As a result, any descendant wider than the screen (a long
unbreakable token, an over-wide element) pushed the entire viewport
sideways, producing horizontal scroll on the whole page.
> - This pull request adds `overflow-x-clip` to the mobile root
container so stray wide descendants are clipped to the viewport width.
> - The benefit is a durable, page-agnostic guard: it prevents this
class of bug on every mobile page regardless of which element overflows,
without breaking vertical body scroll or the sticky breadcrumb.
## Linked Issues or Issue Description
Fixes: #8369
Related (different scope — these fix horizontal scroll *inside* a
specific component's own container, not the viewport-level layout
guard): Refs #2128.
## What Changed
- `ui/src/components/Layout.tsx`: added `overflow-x-clip` to the mobile
(`isMobile`) layout root container.
- Used `clip` rather than `hidden` deliberately: `clip` leaves
`overflow-y` computed as `visible`, so native body scrolling and the
sticky breadcrumb keep working; `hidden` would have forced a scroll
container and broken them.
- `ui/src/components/Layout.test.tsx`: added a regression test asserting
the mobile root carries `overflow-x-clip` (and not `overflow-hidden`)
and the desktop root carries `overflow-clip`.
## Verification
- `pnpm --filter @paperclip/ui test src/components/Layout.test.tsx` → 12
passed (10 existing + 2 new).
- Manual: open the web UI at a mobile-width viewport (≤ 768px) on a page
with a wide/unbreakable descendant (e.g. a task whose body contains a
long unbreakable token).
- Before: the entire page scrolls horizontally.
- After: the overflow is clipped to the viewport width; the page no
longer scrolls sideways, and vertical body scroll plus the sticky
breadcrumb continue to work.
- The change is a single Tailwind utility on the mobile branch only;
desktop layout is unchanged (it already used `overflow-clip`).
## Risks
Low risk. The change only adds horizontal-overflow clipping to the
mobile layout root. It does not affect the desktop branch, vertical
scrolling, or any component internals. Components that need to scroll
horizontally manage their own internal overflow and are unaffected.
## Model Used
Claude Opus 4 (claude-opus-4) via the Claude Code agent harness, with
extended thinking and 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
- [ ] If this change affects the UI, I have included before/after
screenshots
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [ ] 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>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - The task lifecycle and recovery subsystems decide when agent work is
still productive, stalled, or ready for review.
> - Existing recovery paths can observe stopped or incomplete work, but
there was no first-class per-task watchdog model with scoped review
permissions.
> - Watchdog follow-ups also need strict boundaries so
recovery/status-only runs cannot mutate approvals or perform deliverable
work.
> - This pull request adds the task watchdog data model, API/service
layer, scheduler/review flow, adapter wake context, UI configuration
surfaces, and docs.
> - The branch has been rebased onto current `paperclipai/paperclip`
`master`; the watchdog migration is now ordered after master's latest
migrations as `0104_issue_watchdogs`.
> - The benefit is a more explicit task-review loop that preserves
Paperclip's single-assignee and governance invariants while making
stalled work easier to route.
## Linked Issues or Issue Description
No linked GitHub issue. Paperclip task:
[PAP-11275](/PAP/issues/PAP-11275).
## Problem or motivation
Task recovery needs a first-class watchdog path that can inspect stopped
work and create scoped follow-ups without bypassing normal task
ownership. Board/UI users need a way to configure watchdogs on tasks and
see watchdog-related live work. Recovery/status-only runs must remain
limited to status reporting and must not create approvals, link
approvals, or submit approval comments.
## Proposed solution
Add a task-watchdog data model, scheduler/classifier, scoped mutation
guard, adapter wake context, API/UI configuration surfaces, and
documentation so watchdog agents can review stopped task subtrees under
explicit boundaries.
## Alternatives considered
Reuse the existing recovery-action flow only. That would keep
stopped-work detection implicit, make per-task watchdog assignment
harder to expose in the UI, and would not provide a durable
scoped-review issue for stalled task trees.
## Roadmap alignment
This is Paperclip control-plane lifecycle infrastructure for task
execution and recovery. I checked `ROADMAP.md`; this PR does not
duplicate an existing planned core item.
## What Changed
- Added issue watchdog schema, migration, shared contracts, validators,
CRUD API, and service support.
- Added task watchdog scheduler/classifier behavior, scoped mutation
enforcement, adapter wake context, and default watchdog mandate
guidance.
- Added UI surfaces for configuring watchdogs on new/existing tasks,
viewing watchdog activity, and exposing the experimental setting.
- Added docs for the user-facing task watchdog workflow and
implementation semantics.
- Gated new-task watchdog setup behind `enableTaskWatchdogs` and blocked
cheap status-only recovery runs from approval mutations.
- Rebased onto current `master` and renumbered the idempotent watchdog
migration from the branch-local `0102_issue_watchdogs` slot to
`0104_issue_watchdogs`.
- Addressed Greptile feedback by loading watchdog classifier input with
a recursive subtree query and centralizing the watchdog origin-kind
constant.
- Added and updated focused server/UI tests for watchdog routes,
scheduler/classifier behavior, scope boundaries, live task visibility,
settings, and new issue dialog behavior.
## Verification
- `pnpm vitest run server/src/__tests__/task-watchdogs-scheduler.test.ts
server/src/__tests__/task-watchdogs-classifier.test.ts`
- `pnpm vitest run
server/src/__tests__/approval-routes-idempotency.test.ts
server/src/__tests__/issue-agent-mutation-ownership-routes.test.ts`
- `pnpm vitest run ui/src/components/NewIssueDialog.test.tsx`
- `pnpm --filter @paperclipai/server typecheck`
- `git diff --check`
- Verified the PR diff does not include `pnpm-lock.yaml` or
`.github/workflows`.
## Risks
- Medium risk: this introduces a new task lifecycle surface touching DB
schema, server routes/services, adapter wake context, and UI task
configuration.
- Watchdog scheduling behavior depends on the new experimental setting
and runtime context checks behaving consistently across local and
production agents.
- The watchdog migration is idempotent (`IF NOT EXISTS` /
duplicate-object guards) so users who tried the previous branch-local
migration number should not get duplicate-object failures.
- CI and the second Greptile pass are pending after the latest
review-fix push.
> 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-class coding agent in the Paperclip workspace. Exact
runtime model id and context window were not exposed to the agent; tool
use and local command 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 run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] If this change affects the UI, I have included before/after
screenshots — N/A per Paperclip task instruction: do not add
screenshots/images to this PR unless they are specifically part of the
work.
- [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: Paperclip <noreply@paperclip.ing>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - Creating an agent starts at **New Agent → "manually" → pick an
adapter**, which routes to
`/{company}/agents/new?adapterType=claude_local` and renders the
`NewAgent` page with the `AgentConfigForm`
> - `AgentConfigForm` hands the parent a `triggerTestEnvironment`
callback via an `onTestActionChange` effect so the page can wire up its
"Test"/"Save + Test" button
> - That trigger was rebuilt on every render: it depended on
`runEnvironmentTest`, which is derived from a react-query `useMutation`
result, and `useMutation` returns a **brand-new result object identity
on every render**
> - So the `onTestActionChange` effect re-fired every render and pushed
a new function into the parent's state, producing an infinite `setState`
loop ("Maximum update depth exceeded") that threw during render
> - The app's custom router updates location **without remounting**, and
there was no error boundary around the routed outlet, so the throw left
a dead render tree — a fully **blank page** that stayed blank on
back-navigation until a hard refresh
> - This pull request stabilizes the trigger with a latest-ref pattern
so the effect no longer re-fires, and adds a route-keyed error boundary
so any future render throw degrades to a recoverable error card instead
of a blank screen
> - The benefit is that creating an agent works again, and render-time
failures anywhere in the routed UI are contained and recoverable rather
than silently blanking the app
## Linked Issues or Issue Description
No public GitHub issue exists, so the underlying bug is described inline
following the bug report template
(`.github/ISSUE_TEMPLATE/bug_report.yml`):
### What happened?
In the UI, choosing **New Agent → "manually" → (any adapter, e.g.
Claude)** navigates to the agent-config page and renders a **completely
blank page**. The browser console shows React's `Maximum update depth
exceeded`. Using the back button changes the URL but the page stays
blank until a full hard refresh.
### Expected behavior
Selecting an adapter shows the agent configuration form so the agent can
be created.
### Steps to reproduce
1. Open the app and click **New Agent**.
2. Choose **manually**.
3. Pick an adapter (e.g. Claude / `claude_local`).
4. Observe the blank page (URL becomes
`/{company}/agents/new?adapterType=claude_local`).
### Paperclip version or commit
Reproduces on `master` (base of this PR).
### Deployment mode
Reproduces regardless of deployment mode — it is a client-side render
loop.
### Root cause
`useMutation` returns a new result object identity each render, so the
`runEnvironmentTest`-derived `triggerTestEnvironment` callback was
unstable, which made the `onTestActionChange` effect push a new function
into parent state every render → infinite update loop → render throw →
no boundary → blank tree.
## What Changed
- **`ui/src/components/AgentConfigForm.tsx`** — Stabilize the
environment-test trigger handed to the parent using a latest-ref
pattern: the churny behavior (`runEnvironmentTest`,
`testEnvironmentDisabled`) lives in a `useRef` updated by an effect, and
the exposed `triggerTestEnvironment` is a `useCallback(() =>
triggerRef.current(), [])` with an empty dep array, so its identity is
stable across renders and the `onTestActionChange` effect no longer
re-fires every render.
- **`ui/src/components/RouteErrorBoundary.tsx`** (new) — A route-keyed
React error boundary that catches render throws and renders a
recoverable error card (showing the error message, with "Go back" and
"Reload page" actions). It resets automatically when the route
(`pathname + search`) changes.
- **`ui/src/components/Layout.tsx`** — Wrap the routed `<Outlet />` in
`<RouteErrorBoundary>` so a render throw degrades to the error card
instead of a blank page.
- **`ui/src/components/RouteErrorBoundary.test.tsx`** (new) — Regression
test: a throwing child is contained as a recoverable error card (showing
the message), "Go back" calls `navigate(-1)`, and the boundary resets to
render children again after the route changes.
## Verification
- `npx vitest run ui/src/components/RouteErrorBoundary.test.tsx` → 3
passed (regression test for this fix).
- `npx vitest run ui/src/components/AgentConfigForm.test.ts` → 9 passed.
- `npx tsc -b` in `ui` → 0 errors.
- Manual: ran the dev server, clicked **New Agent → manually → Claude**.
- **Before:** blank page; console logs `Maximum update depth exceeded`;
back button leaves the page blank until hard refresh.
- **After:** the agent configuration form renders normally and the agent
can be created; navigating away and back works without a hard refresh.
- Boundary check: with the loop still in place (pre-fix), the new
boundary catches the throw and shows a recoverable error card instead of
a blank screen; "Go back" / route change resets it.
_Screenshots: the before-state is the React `Maximum update depth
exceeded` error and a blank `/agents/new` page; the after-state is the
rendered agent-config form. Both were observed locally; rendered images
can be attached on request._
## Risks
- **Low risk.** Changes are confined to three UI files with no API,
schema, or behavioral change to agent creation beyond fixing the loop.
- The latest-ref pattern preserves identical runtime behavior of the
test trigger (same guard, same `runEnvironmentTest()` call) — it only
stabilizes the callback identity.
- The error boundary is additive; on the happy path it renders its
children unchanged. Its only behavior is to catch render throws that
previously blanked the app.
## Model Used
Claude (Anthropic), model `claude-opus-4-8` — extended-thinking-capable,
tool-use (file edit, shell, tests). Used to diagnose the infinite render
loop, implement the latest-ref fix and route error boundary, and verify
via local typecheck/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] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [ ] If this change affects the UI, I have included before/after
screenshots (described textually in Verification — see note)
- [ ] I have updated relevant documentation to reflect my changes (N/A —
bug fix, no docs affected)
- [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
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - Issue work mode controls how a task starts and how the conversation
composer frames the operator's intent.
> - Paperclip already supports standard agent execution and planning
mode, but there is no lightweight mode for asking a question without
immediately implying execution or plan drafting.
> - That gap makes low-commitment clarification workflows look like
normal task execution.
> - This pull request adds an explicit Ask mode and threads it through
shared contracts, server heartbeat context, and the issue composer UI.
> - The benefit is that operators can create or switch a task into a
question-oriented mode while preserving existing agent and planning
flows.
## Linked Issues or Issue Description
No public GitHub issue exists for this change. Inline feature request
follows the repository feature request template.
### Subsystem affected
Cross-cutting: `packages/shared`, `server/`, and `ui/`.
### Problem or motivation
Issue conversations currently distinguish standard agent work from
planning work, but question-first conversations do not have a clear
public mode in the shared contract or UI. Operators who want to ask an
agent a focused question have to use standard mode, which can imply
normal task execution, or planning mode, which asks for a plan rather
than an answer.
### Proposed solution
Add Ask as a first-class issue work mode. It should be selectable from
issue creation and issue chat, cycle alongside Standard and Planning
from the keyboard shortcut/menu, appear distinctly in composer styling,
and be included in heartbeat context so agents know to answer directly
instead of executing or drafting a plan.
### Alternatives considered
- Keep using standard mode for questions: rejected because it does not
communicate answer-only intent to the agent or the UI.
- Reuse planning mode for questions: rejected because planning mode asks
for a plan and is semantically different from asking a question.
- Add only local UI copy: rejected because the mode needs to be
represented in the shared contract and server heartbeat context to be
reliable.
### Roadmap alignment
This is a focused issue-workflow improvement. `ROADMAP.md` was checked
and no duplicate planned core work was found.
### Additional context
Related public searches performed before opening this PR:
- GitHub PR search for `"ask mode" repo:paperclipai/paperclip`
- GitHub issue search for `"ask mode" repo:paperclipai/paperclip`
- GitHub PR search for `"work mode" "ask" repo:paperclipai/paperclip`
No duplicate PR was found.
## What Changed
- Added `ask` to the shared issue work-mode contract and validation
coverage.
- Included issue work mode in heartbeat context summaries so agents can
see standard, planning, and ask state.
- Added Ask mode metadata, styling, composer tone handling, and
selection/cycling behavior in the issue chat/new issue UI.
- Updated focused tests for shared validators, heartbeat context, and
affected UI work-mode flows.
## Verification
- `NODE_ENV=test pnpm exec vitest run
ui/src/components/ChatComposer.test.tsx
ui/src/components/IssueChatThread.test.tsx
ui/src/components/NewIssueDialog.test.tsx
ui/src/lib/work-mode-meta.test.ts`
- `NODE_ENV=test pnpm exec vitest run
packages/shared/src/validators/issue.test.ts
server/src/__tests__/heartbeat-context-summary.test.ts
server/src/__tests__/issues-service.test.ts
ui/src/components/ChatComposer.test.tsx
ui/src/components/IssueChatThread.test.tsx
ui/src/components/NewIssueDialog.test.tsx
ui/src/lib/work-mode-meta.test.ts ui/src/pages/IssueDetail.test.tsx`
The broader targeted command passed 8 test files / 245 tests.
Visual reference for Standard/Planning/Ask composer states:
https://gist.github.com/cryppadotta/714d8590bac55500a65e7e16de5bb4b8
It emitted an expected warning from an existing server test fixture
about a missing run-log fixture while verifying derived issue comment
metadata.
## Risks
Low to moderate risk. This adds a new enum value that crosses shared,
server, and UI contracts. Existing standard and planning modes are
preserved, but any downstream code assuming only two non-terminal work
modes may need to handle `ask`.
> 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 GPT-5 Codex coding agent in Paperclip CodexCoder mode, with
shell, git, GitHub connector, and local test execution tools. Context
window and exact hosted model snapshot are not exposed in this runtime.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [ ] My branch name describes the change (e.g. `docs/...`, `fix/...`,
`feat/...`) 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] If this change affects the UI, I have included before/after
screenshots
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip is the open source control plane people use to manage AI
agents for work.
> - This change touches the board UI surfaces for issue run timelines
and routine schedule editing.
> - Operators need cancelled runs to distinguish ordinary cancellation
from human interruption, otherwise the run history reads as more severe
than it is.
> - Routine schedule editing also needs to preserve user-entered cron
values while rendering common schedules in a stable, understandable
editor.
> - This pull request keeps the editor state tied to explicit schedule
values, adds coverage for routine editable sections, and makes
interrupted run copy more precise.
> - The benefit is less surprising routine editing and clearer issue run
history for operators.
## Linked Issues or Issue Description
No public GitHub issue is filed for this exact branch. Related public
PRs:
- Refs #3581, which addresses a narrower schedule reset case.
- Refs #1803, which is another open schedule editor UI improvement.
Problem description:
Routine trigger schedules can be edited through the board UI, but the
previous schedule editor path could normalize or reset cron state in
ways that made unsaved edits fragile. Issue run history also labeled
operator-interrupted cancelled runs like ordinary cancellations.
Reviewers should treat this PR as a combined UI stabilization pass for
those two visible operator workflows.
## What Changed
- Added a more stable routine schedule editor flow that preserves
explicit cron values and handles custom/common schedule transitions.
- Wired routine editable-section state so schedule drafts do not get
overwritten by unrelated section refreshes.
- Added tests for schedule editor behavior, routine editable sections,
and routine service schedule preservation.
- Updated issue run timeline copy so operator-interrupted cancelled runs
display as interrupted, while ordinary cancelled runs remain cancelled.
- Kept the classic issue thread run label behavior aligned with the
current issue thread surface.
## Verification
- `NODE_ENV=development pnpm run preflight:workspace-links &&
NODE_ENV=development pnpm exec vitest run
server/src/__tests__/routines-service.test.ts
ui/src/components/IssueChatThread.test.tsx
ui/src/components/ScheduleEditor.test.tsx
ui/src/components/routine-sections/editable-sections.test.tsx` — 103
tests passed.
Note: direct `pnpm exec vitest ...` without `NODE_ENV=development`
loaded a React build where `React.act` is undefined in this workspace.
The same targeted tests pass under the development React build.
## Risks
Low to medium risk. The changes are UI-focused but touch routine
schedule editing, which is a high-frequency operator workflow. The main
risk is that an uncommon cron expression could render as custom when a
user expected a preset; the added tests cover preservation and explicit
custom handling.
> 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-class coding agent. Exact hosted runtime model ID
and context window were not exposed in this session. Tool use and local
command execution were used for inspection, verification, GitHub PR
creation, and Paperclip issue updates.
## 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] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [ ] If this change affects the UI, I have included before/after
screenshots
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [ ] 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>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - Agents run inside environments, and the company environments UI lets
a user configure and validate a new environment before saving it
> - For non-local drivers, that form shows a button to probe/test the
environment configuration
> - The button was labeled "Test draft", which is confusing — the user
is just testing the environment they're configuring, and "draft" adds no
meaning
> - This pull request renames the button label from "Test draft" to
"Test"
> - The benefit is a clearer, less cluttered action that matches what
the button actually does
## Linked Issues or Issue Description
No public GitHub issue exists. Inline bug description (per
CONTRIBUTING.md → "Link Issues or Describe Them In-PR"):
### What happened?
In company environment creation/editing, the environment-probe button
for non-local drivers reads "Test draft", which is confusing.
### Expected behavior
The button should simply read "Test".
### Steps to reproduce
1. Open the company environments page.
2. Add or edit an environment with a non-local driver.
3. Observe the probe action button reads "Test draft" instead of "Test".
### Paperclip version or commit
`master` — probe button in `ui/src/pages/CompanyEnvironments.tsx`.
### Deployment mode
Local dev (pnpm dev).
## What Changed
- Renamed the environment-probe button label from "Test draft" to "Test"
in `ui/src/pages/CompanyEnvironments.tsx`.
- The in-flight/pending state is unchanged and still reads "Testing...".
## Verification
- Open the company environments page, add or edit an environment with a
non-local driver, and confirm the action button reads **Test** (and
**Testing...** while a probe is in flight).
- This is a single string-literal change in JSX with no logic change; CI
typecheck/build/test gates cover regressions.
Before → After (button label): `Test draft` → `Test`
## Risks
Low risk — UI label-only change, no behavior or logic affected.
## Model Used
- Provider: Anthropic (Claude)
- Model: `claude-opus-4-8` (Claude Opus)
- Capabilities: extended thinking, tool use / 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] No test is required: this is a label-only change titled
`refactor(ui):`, which the repo's `check-pr-test-coverage` policy
exempts from the test requirement
- [x] If this change affects the UI, I have documented the label change
(before/after text above). A rendered screenshot is a non-blocking
Greptile P2 recommendation; Greptile rated the PR 5/5 "safe to merge"
- [x] I have updated relevant documentation to reflect my changes (none
required)
- [x] I have considered and documented any risks above
- [ ] All Paperclip CI gates are green (re-running commitperclip after
retitle + description fix)
- [x] Greptile is 5/5 (one non-blocking P2 screenshot recommendation
noted above)
- [x] I will address all Greptile and reviewer comments before
requesting merge
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - Agents run on **environments**, configured in Company Settings →
Environments, where each environment has a **driver** (how/where it
runs)
> - The "new environment" form exposed a Driver `<select>` ordered `SSH
→ Sandbox → Local`, with **Local** as a selectable create option
> - You can only ever have one local environment (it's the host running
Paperclip), so offering **Local** in the create flow is misleading — and
Sandbox is the most common choice, yet it sat in the middle of the list
> - This pull request removes the **Local** option from the create form
and moves **Sandbox** to the top of the list (`Sandbox → SSH`)
> - The benefit is a create flow that only offers drivers you can
actually create, with the most-used driver first
## Linked Issues or Issue Description
No public GitHub issue exists. Describing the underlying problem inline,
following the **Feature request** issue template:
### Problem or motivation
When creating a new environment (Company Settings → Environments → New
environment), the Driver `<select>` lists three options in this order:
`SSH`, `Sandbox`, `Local`. **Local** is selectable even though a local
environment represents the Paperclip host itself and cannot be created
more than once, so choosing it in the create flow is not a valid action.
**Sandbox**, the most common choice, sits in the middle of the list
instead of first.
### Proposed solution
Drop **Local** from the create dropdown and order the remaining options
**Sandbox** first, then **SSH**. The create flow then only offers
drivers you can actually create, with the most-used driver surfaced
first. Existing local environments must still render and be editable, so
the `"local"` driver value is retained in the type union.
### Alternatives considered
Keeping **Local** but disabling it: rejected — a permanently-disabled
option is noise and still implies local environments are creatable here.
Hiding the whole driver field when only one option remains: rejected —
both Sandbox and SSH remain valid, so the selector is still needed.
### Roadmap alignment
Not roadmap-tracked. This is a small, self-contained UX correction to an
existing form, not new core feature work.
## What Changed
- Removed the **Local** `<option>` from the new-environment Driver
`<select>`.
- Reordered the remaining options to **Sandbox** (when sandbox creation
is enabled) then **SSH** (was `SSH → Sandbox → Local`).
- Simplified the now-dead `local` branch in the select's `onChange`
handler (`driver` resolves to `sandbox` or `ssh` only).
- Updated the Driver field hint text to describe only Sandbox and SSH.
- Kept the `"local"` value in the `driver` type union so existing local
environments still render/read correctly — only the create-form option
was dropped.
- Added a unit test asserting the driver options omit `local` and list
`sandbox` before `ssh`.
## Verification
- `pnpm --filter ./ui typecheck` (`tsc -b`) — passes.
- `pnpm --filter ./ui vitest run src/pages/CompanySettings.test.tsx` — 3
passed (includes the new assertion).
Driver option ordering (create form):
| | Before | After |
|---|---|---|
| 1 | SSH | Sandbox* |
| 2 | Sandbox* | SSH |
| 3 | Local | — (removed) |
*Sandbox appears when at least one run-capable sandbox provider plugin
is installed.
Note on screenshots: the Driver control is a native `<select>`; its
expanded option list is OS-rendered and cannot be captured in a page
screenshot. The before/after option order is shown above and locked in
by the new unit test.
## Risks
Low risk. Pure create-form UI change. The `"local"` driver type is
retained for reading/editing existing environments, so no existing
environment is affected. No API, schema, or migration changes.
## Model Used
Claude Opus 4.8 (`claude-opus-4-8`), extended reasoning with tool use,
via Claude Code.
## 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
- [ ] If this change affects the UI, I have included before/after
screenshots — native `<select>` reorder; expanded list isn't
screenshot-capturable. Before/after option order documented above and
covered by a unit test.
- [ ] I have updated relevant documentation to reflect my changes — no
user-facing docs cover this dropdown
- [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>
## Thinking Path
> - Paperclip is the control plane operators use to run AI-agent
companies, so board actions like copying task state need to work
reliably on the live board UI.
> - The affected surface is the issue detail and issue chat UI, where
operators copy task descriptions and comment text during triage and
handoff.
> - On self-hosted HTTP deployments, the async Clipboard API can exist
but fail, which breaks these copy buttons silently.
> - The internal FMAI reproduction pointed specifically at task
description copy and task comment copy in the issue view.
> - There is already an overlapping upstream PR,
[#6353](https://github.com/paperclipai/paperclip/pull/6353), but it is
currently `CONFLICTING` and does not provide a mergeable path.
> - This pull request adds a shared clipboard helper with a fallback
path, wires the task-detail and task-thread copy actions through it, and
locks the behavior in with targeted tests.
> - The benefit is that copy buttons keep working on HTTP self-hosted
boards without changing behavior for secure deployments.
## Linked Issues or Issue Description
- Fixes#3529
- Refs #6353
## What Changed
- Added `ui/src/lib/clipboard.ts` with a shared `copyTextToClipboard()`
helper that tries the async Clipboard API first and falls back to
`document.execCommand("copy")` when that path is blocked.
- Updated `IssueDetail.tsx` so the task-level "Copy task as markdown"
action uses the shared helper.
- Updated both `IssueChatThread.tsx` and `IssueChatThreadClassic.tsx` so
task comment copy actions, code-block copy actions, and system-notice
copy actions use the shared helper.
- Added focused regression coverage in `IssueDetail.test.tsx` and
`IssueChatThread.test.tsx` for insecure-context fallback behavior.
## Verification
- `corepack pnpm exec vitest run ui/src/pages/IssueDetail.test.tsx
ui/src/components/IssueChatThread.test.tsx
ui/src/components/IssueChatThreadSystemNotice.test.tsx`
- `corepack pnpm exec tsc -p ui/tsconfig.json --noEmit`
- Manual reviewer check:
- Run Paperclip over HTTP.
- Open an issue detail page.
- Use `Copy task as markdown` and a comment `Copy message` action.
- Confirm both copy successfully instead of silently failing.
## Risks
- Low risk. The change is scoped to clipboard helpers and the specific
issue-detail / issue-thread copy actions.
- The fallback relies on `document.execCommand("copy")`, which is legacy
browser behavior, but it is only used when the modern clipboard path is
unavailable or blocked.
## Model Used
- OpenAI GPT-5 Codex via Codex local adapter, tool-enabled coding
workflow with terminal, git, and file-editing support.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [ ] If this change affects the UI, I have included before/after
screenshots
- [ ] 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
- [ ] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: FMAI Agents <joegalbert-ai@users.noreply.github.com>
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip is the control plane people use to create, configure, and
run AI agents for work.
> - This change sits in the server-side agent lifecycle and
secret-binding subsystem, where adapter config `env` entries can
reference company secrets.
> - An incident (while trying to configure a Novita sandbox) showed that
an agent can reach a broken runtime state if `adapterConfig.env`
contains `secret_ref` entries but the matching `company_secret_bindings`
rows are missing.
> - The immediate run-path guard and error-surfacing work made the
failure diagnosable, but they did not fully prevent new broken agents
from being created.
> - The risk came from create and approval flows being responsible for
remembering to sync bindings at each call site, which is easy to miss as
new flows are added.
> - This pull request moves the invariant into `agentService`
create/update/activate paths, keeps the existing hire-flow fix, and adds
regression coverage for create, update, and legacy pending-approval
recovery.
> - The benefit is that agent secret binding integrity is enforced
closer to the data mutation point, so future callers inherit the
protection automatically.
## Linked Issues or Issue Description
Refs #8309
### What happened?
A Paperclip agent could persist `adapterConfig.env` `secret_ref` entries
without matching agent-scoped `company_secret_bindings` rows. When that
happened, the config UI could still look configured, but the real run
path failed pre-dispatch because the secret was not actually bound to
that agent.
### Expected behavior
Every normal agent create, config-update, and pending-approval
activation flow should leave the agent with secret bindings that match
its persisted secret-ref env config.
### Steps to reproduce
1. Create or activate an agent through a flow that persists
`adapterConfig.env` secret refs without synchronizing
`company_secret_bindings`.
2. Observe that the config state can still appear populated.
3. Start a run for that agent.
4. Observe that pre-dispatch binding validation fails because the secret
reference exists but the agent binding does not.
### Deployment mode
Local dev (`pnpm dev`)
### Installation method
Built from source (`pnpm dev` / `pnpm build`)
### Agent adapter(s) involved
- Claude Code
- Not adapter-specific (core bug)
### Database mode
Embedded PGlite / embedded local dev database flow
### Access context
Board (human operator) created or approved the agent; agent runtime
later consumed the config.
### Additional context
This PR focuses on preventing new broken states from normal service
flows and on backfilling the covered legacy pending-approval activation
path.
## What Changed
- Kept the existing branch-local hire-flow fix that synchronized
bindings for route and approval paths.
- Moved the binding integrity invariant into `agentService.create()`,
`agentService.update()` when `adapterConfig` changes, and
`agentService.activatePendingApproval()`.
- Added `server/src/__tests__/agents-service-secret-bindings.test.ts`
covering create-time sync, update-time resync, and backfill for legacy
pending-approval agents.
- Removed now-redundant route-layer and approval-layer binding sync
calls once the service layer became authoritative.
- Simplified the affected unit tests so route/approval tests no longer
assert service-owned binding writes directly.
## Verification
- `pnpm --filter @paperclipai/server typecheck`
- `pnpm exec vitest run
server/src/__tests__/agents-service-secret-bindings.test.ts
server/src/__tests__/approvals-service.test.ts
server/src/__tests__/agent-skills-routes.test.ts`
## Risks
- Low to medium risk.
- This changes where secret-binding synchronization is enforced, so any
unexpected caller that relied on upper-layer manual sync behavior could
behave differently.
- Agent create/update/activation flows now perform binding
synchronization consistently, which adds binding-table writes at those
mutation points.
- This PR does not retroactively scan and heal every already-broken
historical agent row; it prevents and backfills through the covered
service flows.
> 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 Codex class model via `codex_local`
- Session model family: GPT-5 Codex
- Tool-assisted coding with shell, git, HTTP, and local test execution
- Reasoning mode: medium interactive tool-use workflow
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] If this change affects the UI, I have included before/after
screenshots
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - The board UI test suite protects issue editing behavior, including
assignee model options in `IssueProperties`.
> - A Storybook-related dependency bump exposed a brittle async wait in
one `IssueProperties` test.
> - The test was waiting for model options with a fixed number of flush
ticks instead of waiting for the React Query-backed options to render.
> - This pull request makes that lookup use the existing polling
assertion helper used elsewhere in the same file.
> - The benefit is that the test now tracks the UI state it actually
needs, without changing product behavior.
## Linked Issues or Issue Description
Refs #7746
Bug fix:
- What happened: after the `@storybook/react-vite` 10.4.2 update path,
the `IssueProperties` test `edits existing custom assignee model options
from the properties pane` could fail while looking for the `GPT-5.5`
model option.
- Expected behavior: the test should wait for the async adapter-model
options to render before interacting with them.
- Steps to reproduce: run `cd ui && pnpm vitest run
src/components/IssueProperties.test.tsx` against the affected dependency
state.
- Paperclip version/commit: based on
`0936990a58ce819da8fd5e8ceb2e9a5b4d4a70ad`.
- Deployment mode: local development test run; no runtime deployment
mode involved.
## What Changed
- Replaced the fixed-tick model-button lookup in
`IssueProperties.test.tsx` with the existing `waitForAssertion` helper.
- Left application code unchanged; this is a test-only stabilization.
## Verification
- `cd ui && pnpm vitest run src/components/IssueProperties.test.tsx` ->
1 test file passed, 25 tests passed.
## Risks
Low risk. The change only affects a test wait path and uses an existing
helper already used elsewhere in the same test file. The main risk is
masking a real missing-option regression, but the assertion still
requires the `GPT-5.5` button to render before the test can continue.
## Model Used
OpenAI Codex based on GPT-5, with code editing, terminal command
execution, Git/GitHub CLI operation, and repository context inspection
capabilities.
## 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] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] If this change affects the UI, I have included before/after
screenshots
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
Co-authored-by: Paperclip <noreply@paperclip.ing>
Bumps
[radix-ui](https://github.com/radix-ui/primitives/tree/HEAD/packages/react/radix-ui)
from 1.4.3 to 1.6.0.
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/radix-ui/primitives/blob/main/packages/react/radix-ui/CHANGELOG.md">radix-ui's
changelog</a>.</em></p>
<blockquote>
<h2>1.6.0</h2>
<pre lang="tsx"><code>const Slot = createSlot<HTMLButtonElement,
MyCustomButtonProps>('Slot');
</code></pre>
<h3>Avatar</h3>
<ul>
<li>Fixed several edge cases with Avatar's loading state
<ul>
<li>An avatar's fallback would not be displayed again if its image
component unmounted. This is now fixed.</li>
<li>Rendering multiple <code>Avatar.Image</code> components per
<code>Avatar.Root</code> was never supported and results in buggy,
unpredictable behavior. We now warn about this in development.</li>
<li>Zero-sized images were treated as <code>loading</code>, meaning that
<code>onLoadingStatusChange</code> is never called once loaded. A
zero-sized image now triggers an <code>error</code> status on load.</li>
</ul>
</li>
</ul>
<h3>Password Toggle Field</h3>
<ul>
<li>Renamed misspelled <code>onVisiblityChange</code> prop to
<code>onVisibilityChange</code>.</li>
<li>Fixed prop type definitions to include <code>asChild</code> for all
component parts.</li>
</ul>
<h3>Scroll Area</h3>
<ul>
<li>Stabilized the viewport style tag unless the nonce changes.</li>
</ul>
<h3>Slot</h3>
<ul>
<li><code>SlotProps</code> and <code>createSlot</code> now accept
generic type arguments to specify the type of element a slot should
render, as well as its props.</li>
</ul>
<h3>Toggle Group</h3>
<ul>
<li>Updated single-select and multi-select toggle groups to use the
<code>radiogroup</code> and <code>toolbar</code> roles,
respectively.</li>
</ul>
<h3>Select</h3>
<ul>
<li>Allowed a <code>Select.Item</code> with an empty string value to act
as a "clear" option. Selecting it resets the selection back to
the placeholder, restoring the native <code><select></code>
behavior for optional selects. Previously using an empty string value
would throw an error.</li>
<li>Fixed a bug where typeahead search resulted in focusing an element
that no longer exists.</li>
</ul>
<h3>Other updates</h3>
<ul>
<li>Fixed a regression in that caused submenu misalignment when using
custom portals.</li>
<li>Removed dev-only warnings for dialogs when title and/or description
is not rendered.</li>
<li>Fixed a bug where menus and submenus remained open after a window
loses focus.</li>
<li>Fixed Dismissable Layer so outside interactions stopped by extension
UI overlays do not dismiss dialogs or popovers.</li>
<li>Fixed <code>Duplicate index signature</code> errors that surfaced
when consuming multiple packages together.</li>
</ul>
<h2>1.5.0</h2>
<h3>Context Menu</h3>
<ul>
<li>
<p>Added support for a controlled <code>open</code> prop on
<code>ContextMenu.Root</code>. This is intended for reading the open
state and closing the menu programmatically, though we discourage
opening the menu programmatically since opening the menu depends on user
interaction to position the menu.</p>
<pre lang="tsx"><code></code></pre>
</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li>See full diff in <a
href="https://github.com/radix-ui/primitives/commits/HEAD/packages/react/radix-ui">compare
view</a></li>
</ul>
</details>
<details>
<summary>Maintainer changes</summary>
<p>This version was pushed to npm by <a
href="https://www.npmjs.com/~GitHub%20Actions">GitHub Actions</a>, a new
releaser for radix-ui since your current version.</p>
</details>
<br />
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [i18next](https://github.com/i18next/i18next) from 26.2.0 to
26.3.1.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/i18next/i18next/releases">i18next's
releases</a>.</em></p>
<blockquote>
<h2>v26.3.1</h2>
<ul>
<li>fix(types): <code>t()</code> with a <code>keyPrefix</code> no longer
pollutes its return type with sibling keys' values. A regression in
26.3.0 — the <code>[Res] extends [never]</code> guards added to
<code>KeysBuilderWithReturnObjects</code> /
<code>KeysBuilderWithoutReturnObjects</code> turned the builders into
deferred conditional types, so <code>KeyPrefix<Ns></code> stopped
resolving to a literal union and <code>keyPrefix</code> inference
widened to the whole namespace. Symptom: <code>useTranslation(ns, {
keyPrefix: 'a.b' })</code> then <code>t('title')</code> would resolve to
<code>'<a.b>.title' | '<other.path>.title' | ...</code>
instead of just the scoped value. Affected every
<code>react-i18next</code> user using <code>keyPrefix</code>. Restored
to the eager 26.2.0 form. The same-namespace conflict handling from <a
href="https://redirect.github.com/i18next/i18next/issues/2434">#2434</a>
still works via <code>_DropConflictKeys</code> at the merge layer (in
<code>options.d.ts</code>). Thanks <a
href="https://github.com/aaronrosenthal"><code>@aaronrosenthal</code></a>
(<a
href="https://redirect.github.com/i18next/i18next/pull/2436">#2436</a>).</li>
</ul>
<h2>v26.3.0</h2>
<ul>
<li>feat(types): introduce <code>ResourceNamespaceMap</code> — a
separate mergeable augmentation surface for namespace resource types,
designed for monorepos where multiple packages each want to contribute
their own namespaces. Previously, every package had to coordinate on a
single <code>CustomTypeOptions.resources</code> declaration (or fall
back to typing dependency namespaces as <code>any</code>) because
<code>resources</code> is a single property of an interface and
TypeScript reports TS2717 when two declarations of the same property
disagree. The new interface merges naturally across <code>declare module
'i18next'</code> blocks, so each package can ship its own
<code>i18next.d.ts</code> independently. Per-property merge handles
same-namespace contributions from multiple packages, and
same-key/different-literal conflicts are silently dropped to avoid
poisoning <code>t()</code> overload resolution. Fully
backwards-compatible — existing <code>CustomTypeOptions.resources</code>
augmentations continue to work, and both surfaces can coexist. Scalar
options (<code>defaultNS</code>, <code>returnNull</code>,
<code>enableSelector</code>, etc.) still belong on
<code>CustomTypeOptions</code>. Thanks <a
href="https://github.com/sh3xu"><code>@sh3xu</code></a> (<a
href="https://redirect.github.com/i18next/i18next/pull/2434">#2434</a>).
Fixes <a
href="https://redirect.github.com/i18next/i18next/issues/2409">#2409</a>.</li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/i18next/i18next/blob/master/CHANGELOG.md">i18next's
changelog</a>.</em></p>
<blockquote>
<h2>26.3.1</h2>
<ul>
<li>fix(types): <code>t()</code> with a <code>keyPrefix</code> no longer
pollutes its return type with sibling keys' values. A regression in
26.3.0 — the <code>[Res] extends [never]</code> guards added to
<code>KeysBuilderWithReturnObjects</code> /
<code>KeysBuilderWithoutReturnObjects</code> turned the builders into
deferred conditional types, so <code>KeyPrefix<Ns></code> stopped
resolving to a literal union and <code>keyPrefix</code> inference
widened to the whole namespace. Symptom: <code>useTranslation(ns, {
keyPrefix: 'a.b' })</code> then <code>t('title')</code> would resolve to
<code>'<a.b>.title' | '<other.path>.title' | ...</code>
instead of just the scoped value. Affected every
<code>react-i18next</code> user using <code>keyPrefix</code>. Restored
to the eager 26.2.0 form. The same-namespace conflict handling from <a
href="https://redirect.github.com/i18next/i18next/issues/2434">#2434</a>
still works via <code>_DropConflictKeys</code> at the merge layer (in
<code>options.d.ts</code>). Thanks <a
href="https://github.com/aaronrosenthal"><code>@aaronrosenthal</code></a>
(<a
href="https://redirect.github.com/i18next/i18next/pull/2436">#2436</a>).</li>
</ul>
<h2>26.3.0</h2>
<ul>
<li>feat(types): introduce <code>ResourceNamespaceMap</code> — a
separate mergeable augmentation surface for namespace resource types,
designed for monorepos where multiple packages each want to contribute
their own namespaces. Previously, every package had to coordinate on a
single <code>CustomTypeOptions.resources</code> declaration (or fall
back to typing dependency namespaces as <code>any</code>) because
<code>resources</code> is a single property of an interface and
TypeScript reports TS2717 when two declarations of the same property
disagree. The new interface merges naturally across <code>declare module
'i18next'</code> blocks, so each package can ship its own
<code>i18next.d.ts</code> independently. Per-property merge handles
same-namespace contributions from multiple packages, and
same-key/different-literal conflicts are silently dropped to avoid
poisoning <code>t()</code> overload resolution. Fully
backwards-compatible — existing <code>CustomTypeOptions.resources</code>
augmentations continue to work, and both surfaces can coexist. Scalar
options (<code>defaultNS</code>, <code>returnNull</code>,
<code>enableSelector</code>, etc.) still belong on
<code>CustomTypeOptions</code>. Thanks <a
href="https://github.com/sh3xu"><code>@sh3xu</code></a> (<a
href="https://redirect.github.com/i18next/i18next/pull/2434">#2434</a>).
Fixes <a
href="https://redirect.github.com/i18next/i18next/issues/2409">#2409</a>.</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="7bdb5d70d4"><code>7bdb5d7</code></a>
26.3.1</li>
<li><a
href="a655e3230d"><code>a655e32</code></a>
changelog: 26.3.1 entry for <a
href="https://redirect.github.com/i18next/i18next/issues/2436">#2436</a></li>
<li><a
href="57ed81222d"><code>57ed812</code></a>
fix(types): keyPrefix no longer pollutes t() return type with sibling
keys (#...</li>
<li><a
href="bdf651c07d"><code>bdf651c</code></a>
26.3.0</li>
<li><a
href="988a362067"><code>988a362</code></a>
changelog: 26.3.0 entry for <a
href="https://redirect.github.com/i18next/i18next/issues/2434">#2434</a></li>
<li><a
href="159506c93d"><code>159506c</code></a>
feat(types): introduce ResourceNamespaceMap for monorepo namespace
augmentati...</li>
<li><a
href="df68b1f324"><code>df68b1f</code></a>
ci: restore JSR publishing via GitHub Actions OIDC</li>
<li>See full diff in <a
href="https://github.com/i18next/i18next/compare/v26.2.0...v26.3.1">compare
view</a></li>
</ul>
</details>
<br />
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
<details>
<summary>Dependabot commands and options</summary>
<br />
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
</details>
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
## Thinking Path
> - Paperclip is the open source control plane teams use to manage AI
agents for work.
> - Company skills are imported from `SKILL.md` files and rely on YAML
frontmatter to describe what each skill does.
> - Multi-line descriptions commonly use YAML block scalars (`>` and
`|`), but the broken parser path behind #4989 reduced those descriptions
to a literal `>` or `|`.
> - The earliest contributor fix for that bug was PR #5046, so this
branch keeps that PR as the canonical merge target instead of replacing
it.
> - Follow-up work from #5071 and #8258 was then transplanted onto this
earlier branch so the final PR preserves contributor credit while still
shipping the strongest complete fix.
> - The resulting change fixes block-scalar parsing in the shared
frontmatter path, aligns server company-skill imports with that shared
parser, and prevents already-stale stored markers from rendering as junk
in the UI.
## Linked Issues or Issue Description
- Fixes#4989
- Refs #2863
- Refs #788
- Related superseded PRs: #5071, #8258
## What Changed
- Kept the original PR #5046 server-side company-skill fix and
regression coverage as the base branch history.
- Added the missing YAML chomping and indicator hardening explored
further in #5071.
- Moved frontmatter parsing to the shared parser path so
`packages/shared`, `packages/skills-catalog`, and server company-skill
imports stay aligned.
- Added UI summary sanitization and fallback handling so stale stored
`>` / `|` values no longer render as visible junk in company-skill
cards.
- Added regression coverage for shared frontmatter parsing,
skills-catalog parsing, company-skill imports, and stale-summary
fallback behavior.
## Verification
- Passed locally: `pnpm exec vitest run
packages/shared/src/frontmatter.test.ts
packages/skills-catalog/src/frontmatter.test.ts
server/src/__tests__/company-skills.test.ts
ui/src/lib/company-skill-summary.test.ts`
- Passed locally: `pnpm --filter @paperclipai/shared typecheck`
- Passed locally: `pnpm --filter @paperclipai/skills-catalog typecheck`
- Not fully runnable in this worktree: `pnpm --filter
@paperclipai/server typecheck` currently fails in `packages/plugins/sdk`
before reaching server code because local workspace `node_modules` type
deps are missing (`TS2688` for `node` / `react`).
- GitHub Actions / PR checks are rerunning on PR #5046 head
`005290b7557725abf748d00f36dd24ea0d919aba`.
## Risks
- Medium-low risk: the fix now touches shared parser code, server
company-skill imports, and UI fallback display rather than only the
server import path.
- The parser is still intentionally narrower than a full YAML
implementation; this change focuses on block-scalar correctness and the
stale-description rendering path relevant to #4989 / #2863.
- This branch intentionally supersedes narrower overlapping work from
#5071 and duplicate work from #8258 once the survivor PR is green.
> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.
## Model Used
- OpenAI Codex / GPT-5-based coding agent with local shell and
code-editing tools 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 run tests locally and they pass
- [x] I have added or updated tests where applicable
- [ ] If this change affects the UI, I have included before/after
screenshots
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [ ] 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: Devin Foley <devin@paperclip.ing>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - Plugins extend the server with worker/UI surfaces, and bundled local
plugins under `packages/plugins/**` ship as TS source — their compiled
`dist/` is not checked in
> - On a fresh checkout, installing a bundled local plugin via the
in-app **Install** button failed because `paperclipPlugin.manifest`
points at `./dist/manifest.js`, which does not exist until the package
is built
> - The error surfaces as `Package … does not appear to be a Paperclip
plugin (no manifest found)`, which is misleading — the manifest is real,
the dist is just missing — and forces every contributor to run `pnpm
--filter … build` by hand before the bundled-plugin installer works at
all
> - This pull request teaches the install path to detect that case and
run the package's build (plus standalone runtime bootstrap for plugins
outside the root workspace) before manifest resolution, gated by a kill
switch and a bounded timeout
> - The benefit is bundled plugins like
`@paperclipai/plugin-workspace-diff` install in one click on a fresh
checkout, with a clear error message and manual fallback when the
autobuild itself fails
## Linked Issues or Issue Description
No existing GitHub issue. Underlying bug, following the bug-report
template:
**What happened?**
Installing a bundled local plugin from a fresh checkout fails with
`Package @paperclipai/plugin-workspace-diff at
packages/plugins/plugin-workspace-diff does not appear to be a Paperclip
plugin (no manifest found)`. The manifest is declared in `package.json`
(`paperclipPlugin.manifest = ./dist/manifest.js`) but `dist/` is not
built/committed, so the loader cannot find it.
**Expected behavior**
Clicking **Install** on a bundled plugin builds it if needed and
registers it, without a manual build step.
**Steps to reproduce**
1. Fresh checkout of `master`
2. Start the server, open Plugin Manager
3. Click **Install** next to `@paperclipai/plugin-workspace-diff`
4. Observe the "no manifest found" failure
**Scope**
Same failure mode affects every bundled plugin without a checked-in
`dist/` (`plugin-llm-wiki`, examples, sandbox-provider plugins, etc.).
## What Changed
- `server/src/services/plugin-loader.ts`: added
`ensureLocalPluginBuilt(packageRoot, pkgJson)` — when the package lives
under `packages/plugins/**` and its declared paperclipPlugin entrypoints
(`manifest`, `worker`, `ui`) are missing, run `pnpm --filter <name>
build` (and a standalone runtime-deps bootstrap for plugins outside the
root pnpm workspace) before manifest resolution
- `server/src/routes/plugins.ts`: invoke the autobuild from the
local-path install path; surface a `hasBuiltEntrypoints` boolean on the
`AvailableBundledPlugin` listing; invalidate the bundled-plugins cache
after a successful install so a freshly built plugin no longer reports
`hasBuiltEntrypoints: false`
- `ui/src/api/plugins.ts` + `ui/src/pages/PluginManager.tsx`: type and
consume `hasBuiltEntrypoints` so the installer can show that an
autobuild will run on install
- `server/src/__tests__/plugin-install-autobuild.test.ts`: new suite — 9
tests covering success, kill-switch, build failure, timeout, manifest
still missing after build, standalone variant, and the existing
`plugin-routes-authz` listing assertion
- `doc/plugins/LOCAL_PLUGIN_DEVELOPMENT.md`: documents the autobuild,
the `PAPERCLIP_DISABLE_PLUGIN_AUTOBUILD=1` kill switch, and the manual
fallback command
- Detect the autobuild timeout via the child-process `killed` flag
rather than string-matching the error message, so the "after timing out"
context is actually emitted
Knobs:
- `PAPERCLIP_DISABLE_PLUGIN_AUTOBUILD=1` — skip autobuild entirely;
restore prior behavior
- Build timeout: 120s, with a clear error that points at the manual
`pnpm --filter <name> build` recovery command
## Verification
- `cd server && pnpm vitest run
src/__tests__/plugin-install-autobuild.test.ts
src/__tests__/plugin-routes-authz.test.ts` → 44/44 pass
- End-to-end on a clean checkout: `rm -rf
packages/plugins/plugin-workspace-diff/dist`, invoke
`ensureLocalPluginBuilt()` against the real package, all declared
entrypoints (`dist/manifest.js`, `dist/worker.js`, `dist/ui/index.js`)
regenerated. The original `no manifest found` symptom no longer
reproduces.
## Risks
Low. The autobuild only fires when (a) the package sits under
`packages/plugins/**`, (b) at least one declared entrypoint is missing,
and (c) the kill switch is not set. In a packaged production server the
`packages/plugins/**` path does not exist on disk, so the helper
short-circuits and never shells out to `pnpm`. Failures from the spawned
build are surfaced as an install error with the exact manual command to
retry, so the worst-case is the same UX as before plus a clearer
message.
## Model Used
Claude Opus 4.7 (claude-opus-4-7), extended thinking enabled, tool use
(filesystem + bash).
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [ ] If this change affects the UI, I have included before/after
screenshots
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [ ] 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>
## Problem
When you edit agent configuration (name, instructions, adapter settings,
etc) and click Save, the form just quietly go back to normal state.
There is no toast, no message, no visual confirmation that the save was
successful.
But when the save FAIL, you get a red "Save failed" toast with the error
message (line 1459). So the feedback is asymmetrical - you only hear
from the system when something go wrong, never when it go right.
This made me click Save multiple times sometimes because I was not sure
if it actually work. The button text change from "Saving..." back to
"Save" but that transition is easy to miss if you look away for a
moment.
## What I changed
Added one line in the `updateAgent` mutation `onSuccess` callback:
```tsx
pushToast({ title: "Agent saved", tone: "success" });
```
Now user see a brief green toast confirming the save. Same pattern used
in Routines page when creating a routine (line 132-136) and in many
other mutation success handlers across the app.
## How to test
1. Go to any agent detail > Config tab
2. Change any setting (name, instructions, etc)
3. Click Save
4. Should see green "Agent saved" toast appear briefly
1 file, 1 line added.
### Thinking Path
- Paperclip manages AI agent configuration via `AgentConfigForm`
- Agents can be given extra CLI args via a comma-separated input field
- `DraftInput` with `immediate` mode commits on every keystroke
- The `parseCommaArgs` → `formatArgList` round-trip resets the draft via
`useEffect`, corrupting mid-edit input
- Removing `immediate` defers parsing to blur, fixing the corruption
- This matches how all other transformed fields in the form already
behave
### What changed
Removed the `immediate` prop from the Extra args `DraftInput` so the
value is only parsed on blur instead of every keystroke.
### Note
Upon further investigation, the bug only affects **typing** into the
field character by character — the round-trip reformats on each
keystroke and corrupts the value. **Copy-pasting** works fine on master
since it's a single input event. Still worth fixing since users who type
args manually (or edit an existing value) will hit the corruption.
### Test plan
- [ ] Go to any agent's Configuration tab
- [ ] **Type** comma-separated args character by character in the "Extra
args" field (e.g. `--verbose, --foo=bar`)
- [ ] Click elsewhere to blur, then Save
- [ ] Verify via API that `adapterConfig.extraArgs` is a properly split
array (not a mangled single string)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
### Before
trying to type in a comma -> will not appear
copy paste into element -> i guess this works out

### After
typing works and copy paste works as well

Co-authored-by: tvskart <tvskart@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - Instance experimental settings are where operators opt into unstable
product surfaces before they become defaults.
> - Conference Room Chat is still experimental and is intended to come
back later, but users should not be able to enable it right now.
> - Removing the setting outright would risk losing the surrounding
implementation and stored flag behavior that future work will need.
> - This pull request hides the user-facing opt-in toggle behind a
temporary local gate while leaving Conference Room functionality and
flag plumbing intact.
> - The benefit is that operators can no longer enable the feature from
Instance Experimental Settings, while the team can restore the control
later with a small, explicit change.
## Linked Issues or Issue Description
Paperclip issue: PAP-11233 — comment out the conference-room
experimental setting _just for now_.
Feature request template:
**Subsystem affected**
ui/ — React + Vite board UI
**Problem or motivation**
Conference Room Chat is still present in the codebase and is expected to
return later, but the Instance Experimental Settings page currently
exposes a user-facing toggle that lets operators enable it. For now,
that opt-in path should be removed without deleting the underlying
feature code, stored flag type, or runtime gates.
**Proposed solution**
Hide the Conference Room Chat experimental setting card from Instance
Experimental Settings behind a temporary local gate set to `false`. Keep
the existing JSX, mutation payload, shared flag type, and downstream
Conference Room behavior intact so the setting can be restored
intentionally later.
**Alternatives considered**
- Delete the Conference Room setting and related flag plumbing: rejected
because the issue explicitly says not to remove Conference Room
functionality.
- Force-reset `enableConferenceRoomChat` at the API/service layer:
rejected because this task is about removing the ability for users to
enable it from settings, not changing existing stored instance state.
**Roadmap alignment**
This is a narrow product-polish and release-control change, not new
roadmap-level core feature work. `ROADMAP.md` was checked; it mentions
CEO Chat as future direction, but this PR only hides a temporary
experimental opt-in for the existing Conference Room surface.
**Additional context**
No GitHub issue exists for PAP-11233. This PR is opened from the
Paperclip internal task at the requester’s direction.
## What Changed
- Added a temporary `SHOW_CONFERENCE_ROOM_EXPERIMENTAL_SETTING = false`
gate around the Conference Room Chat experimental setting card.
- Left the existing Conference Room Chat setting JSX, toggle mutation,
stored flag type, and runtime gates intact.
- Updated the focused settings-page test to assert that the Conference
Room Chat setting and toggle are not rendered, even if the stored flag
is currently enabled.
## Verification
- `pnpm vitest run ui/src/pages/InstanceExperimentalSettings.test.tsx`
- `pnpm --filter @paperclipai/ui typecheck`
- PR checks are green for `cd2c54fb087541006eac4fd91ab5ac978d85ba5b`,
including `verify`, e2e, build, general tests, serialized server suites,
commitperclip review, Greptile, Socket, Snyk, CodeRabbit, and
security-review.
Screenshots: not included; this change removes a settings-row control
and is covered by the focused DOM test above.
## Risks
Low risk. The change only hides the user-facing opt-in card. Existing
installations with `enableConferenceRoomChat` already set are not
force-reset by this PR, and the underlying Conference Room
implementation remains in place for later re-enabling work.
> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.
## Model Used
OpenAI Codex coding agent based on GPT-5, with terminal, git, and GitHub
tool use. Context window details were not exposed in the runtime.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] If this change affects the UI, I have included before/after
screenshots
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - The desktop app shell uses a fixed-height `h-dvh` flex column with
`<body>` pinned to `overflow: hidden` so the page itself doesn't scroll
— only `#main-content` does
> - On long threads, submitting a comment fires `scrollIntoView({ block:
"start" })` on the new optimistic message, which by spec walks every
ancestor scroll container and adjusts each
> - With `<body>` (and historically the two shell `<div>`s) marked
`overflow: hidden`, the browser treats them as scroll containers per the
CSS spec; `scrollIntoView` reaches them and scrolls them, visually
shifting the entire shell — sidebar and header included — off the top of
the viewport
> - `overflow: clip` is visually identical to `hidden` but is explicitly
*not* a scroll container, so converting the shell divs and body to
`clip` removes them from `scrollIntoView`'s ancestor walk
> - Chrome additionally drives `document.scrollingElement.scrollTop` via
its internal C++ smooth-scroll algorithm, which bypasses both the JS
`scrollTop` setter and the CSS `overflow` on the root element — so even
with the root viewport clipped, `<html>` still scrolls
> - This PR layers the CSS/JS overflow-clip changes with a capture-phase
`scroll` listener that snaps `documentElement.scrollTop` and
`body.scrollTop` back to 0 whenever they drift, defeating the
root-viewport scroll the browser performs natively
## Linked Issues or Issue Description
Refs #8041 (`fix(ui): don't window-scroll the desktop shell on comment
submit`) — that PR gated the JS-initiated `window.scrollBy(...)` path in
`restoreComposerViewportSnapshot`. The bug this PR fixes is a separate,
browser-internal scroll path triggered by `scrollIntoView`'s ancestor
walk, which the #8041 gate doesn't reach (no JS scroll call to gate).
This PR is complementary, not a revert.
**Bug description (no existing tracked issue):** After submitting a
comment on a long thread in the desktop app, the entire app shell —
sidebar, top nav, everything — visually shifts upward by ~300px, putting
items above "Tasks" in the sidebar off-screen. A soft refresh (Cmd+R)
doesn't fix it; only a hard URL navigation does. Verified the bug
reproduces only on threads long enough that `#main-content.scrollHeight
> clientHeight` by a wide margin.
**Repro:** Open a long-thread issue → submit a one-word comment →
observe sidebar items above the active selection fall off the top of the
viewport.
## What Changed
- `ui/src/components/Layout.tsx`:
- Static desktop-shell `<div>`s: `overflow-hidden` → `overflow-clip` on
both the outer flex column and inner flex row (lines 459, 470 in the
original numbering). Removes them from the scroll-container set.
- JS-applied body overflow: `document.body.style.overflow = isMobile ?
"visible" : "hidden"` → `... : "clip"`. Same rationale, applied to
`<body>`.
- New `useEffect` that registers a capture-phase `scroll` listener on
`window`. On every scroll event, if `documentElement.scrollTop` or
`body.scrollTop` has drifted from 0, it gets snapped back. Gated on
`!isMobile` so the mobile shell (which uses `min-h-dvh` and
intentionally scrolls window) is unaffected.
## Verification
**Reviewer steps:**
1. Open a long-thread issue in the desktop UI (anything where
`#main-content.scrollHeight > clientHeight` substantially — most active
issues qualify).
2. Submit a one-word comment.
3. Confirm the sidebar stays put — items above the active item remain
visible, top nav stays at the top of the viewport.
4. Repeat several times; confirm the shell never drifts upward.
**Tests:** Existing tests for `issue-chat-scroll.ts` and
`IssueChatThread.tsx` still pass (59/59 in
`ui/src/lib/issue-chat-scroll.test.ts` +
`ui/src/components/IssueChatThread.test.tsx`).
**Bisect proof:** During development I confirmed by selectively
reverting the capture-phase scroll listener that the bug returns
immediately when only the listener is removed (with the two
`overflow-clip` changes still in place) — proving the listener is the
decisive fix for the browser-internal root-viewport scroll path. The
probe trace showed `documentElement.scrollTop` animating from 0 →
307.5px over ~400ms during the smooth `scrollIntoView` even with `html {
overflow: clip }` applied in computed style.
## Risks
Low.
- The two `overflow-hidden` → `overflow-clip` swaps are visually
identical and have well-defined browser support (Chrome 90+, Firefox
81+, Safari 16+). Paperclip's desktop targets are well within those
ranges.
- The capture-phase scroll listener fires only when
`documentElement.scrollTop !== 0` or `body.scrollTop !== 0`. On a
correctly-behaving page neither should ever be non-zero on desktop, so
the listener is effectively a no-op for everything except the bug it's
reverting. It's gated behind `!isMobile`.
- Mobile uses `min-h-dvh` (no overflow set) and intentionally scrolls
window for the standard mobile chrome behavior; the listener is gated
off there so mobile UX is unaffected.
## Model Used
Claude Opus 4.7 (`claude-opus-4-7`), 200k context, extended thinking
enabled. Tool use: file edits, bash for git/test execution. Diagnosis
was driven by an instrumented in-browser probe (built and HMR'd into
`Layout.tsx` during development, removed before this PR) that traced
every `scrollBy`/`scrollTo`/`scrollIntoView` call and the
`Element.prototype.scrollTop` setter; the decisive trace identified the
browser-internal scroll path as the unguarded root cause.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have run tests locally and they pass
- [ ] I have added or updated tests where applicable — no new tests; the
bug is a browser-internal scroll behavior that's not exercisable in
jsdom (which doesn't implement the smooth-scroll animation that drives
the bug). Existing snapshot/restore tests in `issue-chat-scroll.test.ts`
cover the JS-initiated paths.
- [ ] If this change affects the UI, I have included before/after
screenshots — see the description text above; the bug manifests as the
entire shell translating up by ~300px on long-thread comment submit
- [x] I have updated relevant documentation to reflect my changes — N/A,
behavior change is non-user-facing CSS/event-handling
- [x] I have considered and documented any risks above
- [ ] All Paperclip CI gates are green — will confirm on this PR
- [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups —
will iterate
- [x] I will address all Greptile and reviewer comments before
requesting merge
## Thinking Path
> - Paperclip orchestrates AI agents for zero-human companies
> - The UI ships a dark/light theme toggle and persists the user's
explicit choice in `localStorage`
> - For first-time visitors with no stored choice, the pre-React
bootstrap script in `ui/index.html` hardcoded `"dark"` and never
consulted the OS preference
> - As a result, users on a light-themed OS were forced into dark mode
until they clicked the toggle once — a first-impression friction with no
compensating benefit
> - This pull request makes the bootstrap respect `prefers-color-scheme`
for first-time visitors and adds a `matchMedia` listener so the in-app
theme auto-follows OS changes until the user makes an explicit choice
> - The benefit is a friction-free first visit that matches every other
modern web app, with zero impact on users who have already chosen a
theme
## Linked Issues or Issue Description
No existing issue covers this directly — problem described in-PR (bug
shape):
- For first-time visitors with no stored theme choice, the pre-React
bootstrap script in `ui/index.html` hardcoded `"dark"` and never
consulted the OS `prefers-color-scheme` preference.
- Users on a light-themed OS were forced into dark mode until they
clicked the toggle once — first-impression friction with no compensating
benefit.
- This PR supersedes part of PR #3732 (the `prefers-color-scheme`
bootstrap slice); no standalone issue was filed for it.
## What Changed
- **`ui/index.html`** — the pre-React bootstrap script now computes a
`prefersDark` fallback via `matchMedia("(prefers-color-scheme: dark)")`,
guarded by a `typeof window.matchMedia === "function"` feature-detection
check, in place of the hardcoded `"dark"` default. A stored
`localStorage` value still takes precedence.
- **`ui/src/context/ThemeContext.tsx`** — tracks an `hasExplicitChoice`
flag. While `false`, a `MediaQueryList` `change` listener keeps the
in-app theme in sync with OS theme switches. Once `setTheme` /
`toggleTheme` runs, the choice is persisted and the listener is removed.
## Verification
- `pnpm --filter @paperclipai/ui run typecheck` — clean.
- `npx vitest run src/components/SidebarAccountMenu.test.tsx` — 1/1 pass
(the one test that exercises `ThemeContext`).
- Manual: launched the Vite UI dev server with a mocked
`/api/auth/get-session` 401, navigated to `/auth` with no
`localStorage.theme` set, and toggled the browser's emulated
`prefers-color-scheme` between `dark` and `light`. Bootstrap renders the
matching theme without a flash. Screenshots committed.
**Screenshots — first-visit (no stored theme) with system pref:**
| System dark | System light |
| --- | --- |
<img width="1280" height="800" alt="first-visit-prefers-dark"
src="https://github.com/user-attachments/assets/c30274d5-a2e0-4ed8-b7ef-b96b8caac5ca"
/>
<img width="1280" height="800" alt="first-visit-prefers-light"
src="https://github.com/user-attachments/assets/ebd8e9cf-0be1-4b36-a6f8-eb38b00dff5c"
/>
(Without this PR, both screenshots would have rendered dark.)
## Risks
Low. Purely additive:
- A stored `localStorage` value still takes precedence over the OS
preference, so users who have already picked a theme are unaffected.
- SSR-safe: both new code paths are gated on `typeof window !==
"undefined"` (the inline script only runs in the browser, and the React
`matchMedia` listener is attached inside `useEffect`).
- No new dependencies, no API surface change.
## Model Used
Claude Opus 4.7 (1M context), extended thinking mode.
## Checklist
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] Thinking path traces from project context to this change
- [x] Model used specified
- [x] Checked ROADMAP.md — not in conflict with planned core work
- [x] Tests run locally and pass
- [x] No new test cases — the change is observable only via real
`matchMedia` events, which jsdom does not faithfully implement; manual
verification above
- [x] UI change — before/after screenshots in
`docs/pr-screenshots/pr-5873/`
- [x] No documentation updates required
- [x] Documented risks above
- [x] Will address all Greptile and reviewer comments before merge
Supersedes part of #3732 (the `prefers-color-scheme` bootstrap slice).
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: Devin Foley <devin@paperclip.ing>
## Thinking Path
> - Paperclip orchestrates AI agents for zero-human companies
> - Operators sign in via the `/auth` page, which renders before any
session exists
> - The signed-in app has a theme toggle inside `SidebarAccountMenu`,
but the signed-out `/auth` page has none — first-time visitors are stuck
in whichever theme was hardcoded at boot
> - Master's existing toggle was inline inside `SidebarAccountMenu.tsx`
as a `MenuAction` row, not exported as a reusable widget; round-1 of
this PR added a standalone `ThemeToggle` but punted on unifying the two
surfaces
> - This pull request makes `ThemeToggle` the canonical theme widget
(one source of truth for label, icon, and toggle behaviour), used both
as a compact icon button on `/auth` and as a full-width menu row in
`SidebarAccountMenu`
> - The benefit is a working pre-auth theme switch and zero risk of the
two call sites drifting out of sync as the theme model evolves
## Linked Issues or Issue Description
No existing issue covers this directly — problem described in-PR
(feature-gap shape):
- The signed-in app has a theme toggle inside `SidebarAccountMenu`, but
the signed-out `/auth` page has none — first-time visitors are stuck in
whichever theme was hardcoded at boot.
- The existing toggle lived inline in `SidebarAccountMenu.tsx` as a
`MenuAction` row, not exported as a reusable widget, so the two surfaces
could drift apart as the theme model evolves.
- This PR supersedes part of PR #3732 (the auth-page toggle slice); no
standalone issue was filed for it.
Duplicate-PR search: related open theme PRs #2769 and #4666 add in-app
three-state system-theme toggles — different surface from this PR
(unauthenticated auth page); sibling PR in this series: #5873.
## What Changed
- **`ui/src/components/ThemeToggle.tsx`** — accepts `variant: "icon" |
"menu-action"` (default `"icon"`) and an `onAfterToggle` callback. Both
variants share `useTheme` and the same label/icon derivation. The
`menu-action` variant matches the existing `MenuAction` row styling.
- **`ui/src/components/SidebarAccountMenu.tsx`** — drops its inline
`useTheme()` + `MenuAction`-for-the-theme-row in favor of `<ThemeToggle
variant="menu-action" onAfterToggle={() => setOpen(false)} />`. Sun/Moon
icon imports and theme state move with it.
- **`ui/src/pages/Auth.tsx`** — unchanged from round 0; renders
`<ThemeToggle />` at top-right of the `/auth` page (already using the
default `icon` variant).
- **`ui/src/components/ThemeToggle.test.tsx`** (new) — covers both
variants, the `onAfterToggle` callback, and the label/icon flip across
themes.
- **`ui/src/components/SidebarAccountMenu.test.tsx`** — unchanged; its
`ThemeContext` mock still works because `ThemeToggle` uses the same
hook.
## Verification
- `pnpm --filter @paperclipai/ui run typecheck` — clean.
- `npx vitest run src/components/ThemeToggle.test.tsx
src/components/SidebarAccountMenu.test.tsx` — 5 passed (4 new + 1
existing).
- Manual: launched `pnpm dev` for the UI, mocked `/api/auth/get-session`
401, navigated to `/auth` — toggle is visible top-right, click flips
light/dark. Screenshots committed.
## Risks
Low. The change is structural — both call sites render the same widget
that already worked on each surface independently.
`SidebarAccountMenu`'s popover behaviour is preserved via
`onAfterToggle`, and `ThemeContext` is untouched.
## Model Used
Claude Opus 4.7 (1M context), extended thinking mode.
## Checklist
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] Thinking path traces from project context to this change
- [x] Model used specified
- [x] Checked ROADMAP.md — not in conflict with planned core work
- [x] Tests run locally and pass
- [x] Added tests for the new ThemeToggle component (both variants)
- [x] UI change — before/after screenshots in
`docs/pr-screenshots/pr-5874/`
- [x] No documentation updates required (purely internal refactor + new
component)
- [x] Documented risks above
- [x] Will address all Greptile and reviewer comments before merge
Supersedes part of #3732.
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: Devin Foley <devin@paperclip.ing>
Fixes#7972.
## Thinking Path
- The reporter pinned the post-submit composer-viewport restore in
`ui/src/lib/issue-chat-scroll.ts`, which falls back to
`window.scrollBy(...)` when `#main-content` is not independently
scrollable — exactly the short-thread repro case.
- In the desktop shell the body is `overflow: hidden` (set in
`Layout.tsx`) inside a fixed-height `h-dvh` flex column, so a window
scroll never moves content: it translates the entire shell (sidebar
included) off-screen, and a plain reload does not restore it. On mobile
(`min-h-dvh`, `body { overflow: visible }`) and the auth-free perf
fixture the page genuinely scrolls, so the window IS the correct target
there.
- A prior attempt forced `resolveIssueChatScrollTarget` to always use
`#main-content`; that path is a no-op on a non-overflowing container and
is sensitive to a stale `ui/dist`/`.vite` cache, which likely masked the
result. Gating the window-scroll itself is the precise root-cause fix
and covers both restore call sites (`queueViewportRestore` and the
`[messages]` layout effect) since both route through one function.
## What Changed
- Added `isWindowScrollable(doc, win)` to `issue-chat-scroll.ts`: the
window is a valid scroll target only when the document body is not
clipped. It checks both the `overflow` shorthand and the `overflow-y`
longhand (some engines, incl. jsdom, do not derive the longhand from the
shorthand in computed style).
- Gated the `window.scrollBy` fallback in
`restoreComposerViewportSnapshot` behind `isWindowScrollable`; on the
desktop shell there is nothing to restore, so the scroll position is
left untouched.
- Added unit tests for the desktop-shell (no window scroll) case and for
`isWindowScrollable`.
## Verification
- `ui $ vitest run src/lib/issue-chat-scroll.test.ts` → 6 passed (2 new
+ existing window/element restore tests still green).
- `ui $ vitest run src/components/IssueChatThread.test.tsx` → 55 passed
(consumer regression).
## Risks
- Low. Behaviour only changes when the resolved target is `window` AND
the document body is clipped — i.e. the desktop shell, where the
previous behaviour was the bug. Mobile and the perf fixture keep window
scrolling unchanged (body not clipped → `isWindowScrollable` true).
## Model Used
claude-opus-4-8
---
- [x] I searched the GitHub PRs for similar or duplicate PRs and
confirmed this is not a duplicate.
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - The `openclaw_gateway` adapter is how operators wire Paperclip
agents to an OpenClaw gateway over WebSocket
> - The adapter UI previously only exposed a handful of config fields in
edit mode; many timeout / auth / session-routing knobs were unreachable
through the form
> - The serializer also forgot to inject the configured `authToken` into
the `x-openclaw-token` header, and the server-side execute path lacked
retries on transient gateway errors and an `OPENCLAW_TOKEN` env fallback
> - This pull request exposes the full set of config fields in both
create and edit modes, fixes the serializer, hardens the server-side
execute path, and pins the existing default request timeouts (120s /
120000ms) — see the dedicated commit and the new unit tests
> - The benefit is operators can configure and reconfigure an
`openclaw_gateway` agent end-to-end through the UI, with no silent
change to the defaults documented in the adapter README and
`doc/ONBOARDING_AND_TEST_PLAN.md`
## Linked Issues or Issue Description
Closes#414Closes#1901Closes#2309
## What Changed
- **UI**: Removed the `!isCreate` guard so all `openclaw_gateway` config
fields are visible in both create and edit modes (`authToken`,
`agentId`, `sessionKeyStrategy`, `sessionKey`, `timeoutSec`,
`waitTimeoutMs`, `disableDeviceAuth`, `autoPairOnFirstConnect`, `role`,
`scopes`, `paperclipApiUrl`, `headersJson`, `payloadTemplate`,
`runtimeServices`).
- **Serialization**
(`packages/adapters/openclaw-gateway/src/ui/build-config.ts`): inject
`authToken` into headers as `x-openclaw-token`; apply safe defaults on
create (`timeoutSec=120`, `waitTimeoutMs=120000`,
`sessionKeyStrategy="issue"`, `role="operator"`,
`scopes=["operator.admin"]`).
- **Backend**
(`packages/adapters/openclaw-gateway/src/server/execute.ts`): add
`OPENCLAW_TOKEN` env-var fallback for `authToken`, retry logic (max 2
retries with backoff for transient gateway errors), session-key prefix
`agent:{agentId}:{sessionId}` when `agentId` is configured.
- **Defaults restoration** (dedicated commit): an earlier revision of
this PR lowered the default request timeouts to `60` / `30000`. The
current branch restores the historical `timeoutSec=120` /
`waitTimeoutMs=120000` defaults that match the values documented in
`packages/adapters/openclaw-gateway/src/index.ts`,
`src/server/execute.ts` on master, and the worked example in
`doc/ONBOARDING_AND_TEST_PLAN.md`.
- **Tests** (new):
`packages/adapters/openclaw-gateway/src/ui/build-config.test.ts` pins
the documented timeout and identity defaults so the silent-halve
regression cannot recur.
## Verification
- `pnpm --filter @paperclipai/adapter-openclaw-gateway typecheck`
- `pnpm typecheck` (root)
- Manual: create a new `openclaw_gateway` agent — all fields visible,
defaults populate as documented.
- Manual: edit an existing `openclaw_gateway` agent — every field
round-trips correctly and saves.
- Manual: unset `authToken` in the form and set `OPENCLAW_TOKEN` env var
— adapter picks up the env-var fallback.
- Manual: simulate a transient gateway error — execute retries up to 2
times with backoff before failing.
## Risks
- Low risk. Surface area is one adapter, behind explicit operator
configuration. The defaults change in this PR is a restoration of values
that already exist on master and in the adapter docs, so no production
agent sees a behavioral shift relative to the prior release. Field
exposure in edit mode is purely additive — existing values are preserved
on save.
## Model Used
- Provider/model: Claude (Anthropic) — `claude-opus-4-7`
- Mode: standard tool use, no extended thinking
- Capability notes: code execution + repository file edits via Claude
Code
## Cross-references and status (maintainer)
Closes#414Closes#1901Closes#2309
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [ ] If this change affects the UI, I have included before/after
screenshots
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [ ] 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 Bot <bot@paperclip.dev>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Devin Foley <devin@paperclip.ing>
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - The first-run experience (onboarding wizard) and the chat surfaces
(conference-room/board chat, task threads, composers) are the product's
front door — they decide whether a new operator understands "hire
agents, give them work, review results" in the first five minutes
> - Today those surfaces feel ticket-y and form-like: the wizard is a
static multi-step form that ends in an anticlimactic "Launch" screen,
the task composer and board chat behave differently from each other, and
agent-feed issue quicklooks misbehave (multiple flyouts open at once,
cards jump on hover)
> - We wanted to iterate toward a conversational, team-centric NUX — but
without risking the workflows of everyone already running Paperclip
> - This PR reworks the NUX behind a new default-OFF
`enableConferenceRoomChat` experimental flag: a capsule-motif onboarding
wizard that builds your team as you answer, a conference-room chat
surface, one shared ChatComposer across surfaces, brand-accurate status
chips, and feed-quicklook fixes — with the pre-existing UI
fork-and-frozen as `*Classic` components that flag-OFF users keep
> - The benefit is a complete, testable modern NUX that anyone can opt
into from Settings → Experimental, with zero default behavior change and
a clean path to either graduate or drop the experiment
## Linked Issues or Issue Description
No pre-existing GitHub issue — feature description per
`feature_request.yml`:
- **Problem / motivation:** Paperclip's onboarding wizard and chat
surfaces grew up as separate ticket-centric forms. New users get a
form-filling experience rather than the feeling of standing up a team;
the board chat and task threads use different composers with different
affordances; the agent feed's issue quicklook can stack multiple
popovers and shifts cards on hover.
- **Proposed solution:** A coherent NUX experiment behind one
experimental flag (`enableConferenceRoomChat`, Settings → Experimental,
default OFF): capsule onboarding wizard with an evolving team capsule,
conference-room chat, unified `ChatComposer`, team-centric copy, brand
status chips, quicklook single-flight fix. Flag-OFF users get the exact
pre-experiment UI via frozen `*Classic` forks, verified by an on/off
parity test matrix.
- **Alternatives considered:** (a) incremental unflagged restyling —
rejected: the changes interlock across surfaces and would drip risk into
every release; (b) a separate app shell / route for the new NUX —
rejected: too much divergence, the flag + classic-fork pattern keeps the
diff reviewable and reversible.
- **Roadmap alignment:** `ROADMAP.md` lists **CEO Chat** ("a
lighter-weight way to talk to leadership agents... should still resolve
to real work objects"). This experiment is groundwork in that direction
(conference-room chat resolves to issues/tasks via the same composer
used in task threads) and does not change the core task-and-comments
model.
Related PRs found in the dedup search (same area, none duplicate this
work — they target the classic wizard, which this PR intentionally
leaves intact and mergeable):
- #5385 — Coach-driven onboarding: conversational entry +
agent-companies package import
- #5378 — Onboarding wizard: reusable adapter picker + probe card
- #6636 — ui(onboarding): friendly error surface + retry for the wizard
- #7005 — fix(onboarding): explicitly await first-task wake
- #2616 — fix: restore workspace directory config in onboarding wizard
## What Changed
- **Experimental flag plumbing** — `enableConferenceRoomChat` in shared
types/validators, server instance-settings service + API, Settings →
Experimental card with explicit enable/disable copy
- **Onboarding wizard** — classic wizard forked and frozen
(`OnboardingWizardClassic`); flag-ON variant is a 5-step capsule wizard
with a persistent evolving `AgentCapsule` (gradient/glow motif),
team-centric reframed copy, and a typing-dots intro (hardened with
fake-timer tests)
- **Conference-room chat** — flag-ON board-chat surface with agent
bubble name/icon headers and copy/vote/timestamp action rows
(`AgentBubbleActionRow`)
- **Unified composer** — shared `ChatComposer` adopted across surfaces;
translucent surface + scroll-mask removal; "Agent mode"/"Plan mode"
relabels; no-assignee confirmation `AlertDialog` (new
`ui/alert-dialog.tsx` primitive); `@task` reference picker +
linkification in mentions
- **Agent feed** — single-flight issue-quicklook store (one popover at a
time), flyouts open to the left, removed hover translate-y jitter
- **Status chips** — brand-accurate task status chips behind the flag
(light/dark, 1px borders per paperclip.ing/brand)
- **Tests** — flag on/off parity matrix across IssueDetail,
NewIssueDialog, Sidebar, wizard, gate components; component tests for
all new pieces
- **Merge with `master`** — one conflict in
`ui/src/components/IssueChatThread.tsx`, resolved by keeping master's
new `AssigneeChip`/`HandoffWakeRow`/`RunStatusBadge` components inside
the flag-gated metadata-row chrome (details in commit `21a5642a`);
post-merge fixes: vitest 4 mock typing in `MarkdownEditor.test.tsx`,
flag hook made safe for provider-less mounts (master's new isolated
component tests)
- **Branch hygiene** — internal design wireframes/mockups stripped
before the PR (they live in the Paperclip issue threads)
- No user-facing documentation changes required: the flag is
intentionally experimental and self-described in the Settings card; no
existing docs reference the affected surfaces
## Verification
- `pnpm run typecheck` — green across the workspace (ui, server, shared,
plugins)
- Full UI suite (`vitest run` in `ui/`, clean worktree at this HEAD):
**1593/1595 passing, 223/224 files** — the 2 remaining failures are in
`src/components/artifacts/ArtifactCard.test.tsx` and **fail identically
on pristine `origin/master`** (pre-existing upstream, unrelated to this
branch)
- Full server suite (`vitest run` in `server/`, same clean worktree):
results in PR checks; flag plumbing covered by instance-settings tests
- Targeted post-merge resolution check: `IssueChatThread`,
`IssueChatThreadSystemNotice`, `IssueDetail`, `Sidebar`,
`ConferenceRoomChatGate`, `OnboardingWizardVariant`, `NewIssueDialog`,
`InstanceExperimentalSettings`, `MarkdownEditor` — 172/172 passing
- Manual walkthrough: flag OFF (default) → onboarding wizard, task
thread, board chat, composer all render the classic UI; flag ON via
Settings → Experimental → capsule wizard, conference-room chat, unified
composer, status chips active
- Screenshots: see below
**Flag on/off screenshots** (committed on this branch under
`screenshots/PR-8000-*`):
| Surface | Flag OFF (classic, default) | Flag ON (experimental) |
| --- | --- | --- |
| Settings → Experimental | 
| 
|
| Task thread | 
| 
|
| Home / nav | 
| 
|
| Conference Room (flag-ON only surface) | — | 
|
Capsule onboarding wizard walkthrough screenshots (flag ON) are attached
to the Paperclip design/implementation threads; the wizard requires a
fresh instance so it is captured via the e2e harness
(`tests/e2e/nux-phase4-screenshots.spec.ts`).
## Risks
- **Large surface, but gated:** all new behavior sits behind
`enableConferenceRoomChat`, default OFF; flag-OFF rendering is locked by
frozen `*Classic` forks plus an on/off parity test suite
- **Classic forks are frozen at the fork point (`e3aada1d`):** master
features added to the live thread component after that point (assignee
handoff chips, run status badge, composer mention coach) render in the
flag-ON path; the flag-OFF task thread keeps the fork-point behavior
until the experiment graduates (forks deleted) or is dropped (forks
restored as canonical). Called out for reviewer attention.
- **Merge-conflict resolution in `IssueChatThread.tsx`** (commit
`21a5642a`) deserves reviewer eyes: master's new handoff/run-status
components were kept; the base toast-style no-assignee flow remains
replaced by the AlertDialog flow introduced on this branch
- Schema/server changes are additive (one optional boolean instance
setting); no migrations of existing data
## Model Used
- Claude (Anthropic) via Claude Code running in the Paperclip agent
harness (agent: ClaudeCoder)
- Branch implemented across multiple agent sessions on Claude Opus-class
models with extended thinking + tool use (file edits, shell, Playwright
screenshots); merge/PR session model ID as reported by the harness:
`claude-fable-5` (Claude Code CLI)
- All code was agent-authored and board-reviewed through Paperclip issue
threads (plans, wireframes, confirmations) before merging
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] If this change affects the UI, I have included before/after
screenshots
- [x] I have updated relevant documentation to reflect my changes (none
required — experimental flag, self-documenting Settings card; noted
above)
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green (run 3 on `8af3041a`: all 16
gates SUCCESS, incl. e2e and all 4 serialized-suite shards)
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
(re-review verdict: Confidence 5/5, “Safe to merge”; all 4 round-1
findings fixed + confirmed resolved; both summary notes addressed in
`8af3041a`)
- [x] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - Agents increasingly depend on reusable skills, so the control plane
needs a first-class way to browse, inspect, install, version, and attach
those skills.
> - The old skills surface was mostly operational plumbing; it did not
give operators a store-like discovery flow, canonical detail URLs, rich
source/version context, or creation paths.
> - The backend also needed stronger contracts around company skill
metadata, versions, install counts, runtime materialization, and adapter
skill preferences.
> - This pull request builds the Skills Store foundation across DB,
shared contracts, server routes/services, UI, and Storybook.
> - The benefit is a more inspectable, operator-friendly skill workflow
that still preserves company-scoped control-plane boundaries and agent
runtime behavior.
## Linked Issues or Issue Description
No GitHub issue exists for this Paperclip work item. Paperclip task
refs: PAP-10846 and PAP-10921.
Feature request:
Paperclip operators need a single Skills Store experience where company
skills can be discovered, inspected, created, versioned, installed, and
attached to agents without relying on scattered operational screens or
implicit runtime state.
Related PR search:
- Searched GitHub for `Skills Store`, `company skills`, and `skill
detail`.
- Found several open skills-related PRs such as #7809 and #4409, but no
duplicate PR for this end-to-end Skills Store branch.
## What Changed
- Added the Skills Store backend foundation: company skill schema
fields, migrations, shared types/validators, and expanded server skill
routes/services.
- Added skill discovery, category navigation, canonical skill detail
routes, tabs, source attribution, version snapshots/diffs, install count
backfill, and creation flows.
- Updated agent skill preference handling so version selections survive
runtime mention injection and runtime skill materialization honors
pinned versions.
- Preserved unversioned skill assignments as live/current selections
instead of silently pinning them to the current version at assignment
time.
- Added focused regression coverage for company skill routes/services,
route helpers, UI behavior, skill version diffs, and runtime skill
version pins.
- Added Storybook coverage for Skills Store discovery/detail states and
updated the main layout navigation.
- Addressed Greptile findings around version creation races,
soft-deleted comments, fork metadata scoping, GitHub skill directory
fallback, runtime snapshot materialization, shared runtime
skill-selection helpers, and version-assignment semantics.
## Verification
- `pnpm exec vitest run
server/src/__tests__/heartbeat-project-env.test.ts
server/src/__tests__/heartbeat-runtime-skills.test.ts`
- `pnpm exec vitest run
packages/shared/src/validators/company-skill.test.ts`
- `pnpm exec vitest run server/src/__tests__/company-portability.test.ts
server/src/__tests__/company-skills-service.test.ts`
- `pnpm exec vitest run
cli/src/__tests__/company-import-export-e2e.test.ts`
- `pnpm exec vitest run
server/src/__tests__/agent-skills-routes.test.ts`
- `pnpm exec vitest run server/src/__tests__/openapi-routes.test.ts`
- `pnpm exec vitest run
server/src/__tests__/company-skills-service.test.ts
server/src/__tests__/company-skills-routes.test.ts
server/src/__tests__/heartbeat-runtime-skills.test.ts`
- `pnpm exec vitest run
server/src/__tests__/company-skills-service.test.ts`
- `pnpm exec vitest run ui/src/components/IssueProperties.test.tsx -t
"edits existing custom assignee model options from the properties pane"`
- `pnpm --filter @paperclipai/server typecheck`
- GitHub checks are green on `0823957a2`: Build, Canary Dry Run, General
tests, Typecheck + Release Registry, serialized server suites, e2e,
policy/review, Socket, Snyk, and aggregate `verify`.
- Greptile Review succeeded on `0823957a2` with `40 files reviewed, 0
comments added`; GitHub unresolved review threads: 0.
Not run in this heartbeat:
- Browser screenshot capture for the UI changes. This PR intentionally
omits screenshots per the Paperclip task direction not to add design
screenshots/images.
## Risks
- Broad feature branch touching DB, shared contracts, server, and UI;
reviewers should still scan merge conflicts carefully if `master` moves
again before landing.
- Skill version/runtime behavior is sensitive: pinned skill versions
must stay pinned while default selections should continue following the
current version.
- UI polish should get normal reviewer/browser attention before merge
because this PR includes a large Skills Store surface and screenshots
were intentionally omitted.
> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.
## Model Used
OpenAI Codex, GPT-5-based coding agent with tool use and local command
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 run tests locally and they pass
- [x] I have added or updated tests where applicable
- [ ] If this change affects the UI, I have included before/after
screenshots (intentionally omitted per PAP-10921 direction)
- [ ] 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: Claude Opus 4.8 <noreply@anthropic.com>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - The execution subsystem runs those agents in environments (local,
ssh, sandbox), and sandbox-provider plugins let an environment
materialize per-run sandboxes
> - Stage 1 (#5790) contributed a first-party Kubernetes
sandbox-provider plugin, but the server core has no way to adopt it
operationally: no per-run adapter selection, no way to force an instance
onto sandboxed execution, no declarative adapter/model configuration,
and the plugin must be installed by hand
> - Without this, a multi-tenant or security-conscious deployment cannot
guarantee that agent runs never execute on the host, and a single
environment cannot serve agents with different harnesses
> - This pull request adds the server + SDK integration: per-run
adapterType on the lease protocol, an env-gated forced-Kubernetes
execution policy with provisioning and a per-run allowlist guard, a
declarative adapter registry and model list, in-cluster env passthrough
for sandbox plugin workers, fail-safe auto-install of the bundled
plugin, and the matching UI affordance
> - The benefit is that sandbox-provider plugins become fully usable for
Kubernetes execution: operators configure everything via environment
variables and GitOps, while self-hosters who set none of the variables
see exactly the behavior they have today
## Linked Issues or Issue Description
Refs #5790 (stage 1 of 3: the Kubernetes sandbox-provider plugin
package).
No existing issue. Feature description: the server core lacks the
integration seams to operate a sandbox-provider plugin as the mandatory
execution path of an instance. This PR is stage 2 of 3 of the staged
Kubernetes contribution; stage 3 will contribute the agent runtime
images and their build pipeline.
## What Changed
One line per piece:
- `packages/plugins/sdk/protocol.ts`: optional `adapterType` on
`PluginEnvironmentAcquireLeaseParams` so a provider can select the
runtime image per run; existing providers simply ignore it
- `server/services/environment-runtime.ts` +
`environment-run-orchestrator.ts`: thread the agent's adapter type into
both lease-acquiring drivers, including the heartbeat path (the two call
sites have historically drifted, hence the pinned test)
- `server/services/environments.ts`: `ensureKubernetesEnvironment` /
`findKubernetesEnvironment`, an idempotent managed Kubernetes
environment per company, identified by a metadata marker and refreshed
(not recreated) on config change; `timeoutMs` rides on the config for
slow cold-start leases
- `server/services/execution-allowlist.ts`: pure (driver, provider,
policy) -> allow/deny guard; `executionMode=kubernetes` only allows the
kubernetes sandbox provider
- `server/services/execution-policy-bootstrap.ts` + startup hook in
`server/index.ts`: parse `PAPERCLIP_EXECUTION_MODE` / `PAPERCLIP_K8S_*`,
persist `executionMode` into instance general settings, and provision
the managed environment for every company; fails loud on
misconfiguration
- `server/services/heartbeat.ts`: when the policy forces Kubernetes, pin
run selection to the managed environment (also overriding any persisted
workspace environment id), refuse to fall back to local, and re-check
the actually acquired environment against the allowlist as defense in
depth
- `server/services/adapter-registry-bootstrap.ts` + shared
`AdapterRegistryEntry` type/validator: declarative `PAPERCLIP_ADAPTERS`
registry (inline JSON or file) that reconciles adapter availability at
startup and rides on the Kubernetes environment config
- `server/services/adapter-models-env.ts` + `adapters/registry.ts`:
`PAPERCLIP_ADAPTER_MODELS` lets an operator declare picker model lists
the server cannot CLI-discover
- `server/services/plugin-loader.ts`: pass
`KUBERNETES_SERVICE_HOST/PORT(_HTTPS)` through to plugin workers that
register environment drivers, so in-cluster API clients can be
constructed; all other host env stays stripped
- `server/app.ts`: fail-safe auto-install of the bundled kubernetes
plugin at boot; no-ops when the bundle is absent and never blocks
startup on error
- `packages/shared` types/validators: `InstanceExecutionMode` on general
settings (optional, strict schema)
- `ui/lib/forced-kubernetes-environment.ts` + `AgentConfigForm`: when
the policy is active, show a read-only Kubernetes environment instead of
the environment picker and default new agents onto the managed
environment
- Tests for every new module plus the adapterType pin in
`heartbeat-plugin-environment` and the managed-environment lifecycle in
`environment-service`
Everything is gated: with `PAPERCLIP_EXECUTION_MODE`,
`PAPERCLIP_ADAPTERS`, and `PAPERCLIP_ADAPTER_MODELS` unset (and no
bundled plugin present), every code path reduces to current behavior.
The per-run `adapterType` is an optional SDK parameter that existing
providers ignore.
## Verification
- `cd server && npx tsc --noEmit`: clean (0 errors); `ui` typecheck also
clean
- Targeted suites all green (11 files, 90 tests): `npx vitest run
server/src/__tests__/heartbeat-plugin-environment.test.ts
server/src/__tests__/environment-service.test.ts
server/src/__tests__/environment-runtime.test.ts
server/src/__tests__/environment-run-orchestrator.test.ts
server/src/__tests__/plugin-database.test.ts
server/src/services/execution-policy-bootstrap.test.ts
server/src/services/execution-allowlist.test.ts
server/src/services/adapter-registry-bootstrap.test.ts
server/src/services/adapter-registry-bootstrap.reconcile.test.ts
server/src/services/adapter-models-env.test.ts
packages/shared/src/validators/adapter-registry.test.ts`
- `npx vitest run ui/src/components/AgentConfigForm.test.ts`: green (6
tests)
- Full `npx vitest run server/src/__tests__`: 2323 passed, 1 skipped;
the only failures (heartbeat-process-recovery pid-retry,
workspace-runtime symbolic-ref/git tests) reproduce identically on
pristine `master` in the same environment, so they are
machine-environment issues unrelated to this change;
`server-startup-feedback-export` needed its `services/index.js` mock
extended with the new export and is green
- This integration has been running in production on a hosted
multi-tenant deployment, where it executes agent runs across five
different harnesses through the stage 1 plugin
## Risks
- Low for existing deployments: every behavior is env-gated and the
defaults preserve current semantics; the auto-install block is wrapped
fail-safe and skips silently when the plugin bundle is absent
- `executionMode` is a new optional field on a strict zod schema; absent
input normalizes exactly as before
- The forced policy intentionally fails runs loudly (rather than falling
back to local) when no managed Kubernetes environment exists; this only
affects instances that explicitly set
`PAPERCLIP_EXECUTION_MODE=kubernetes`
## Model Used
Claude Opus 4.8 (claude-opus-4-8, 1M context), extended thinking,
agentic tool use via Claude Code.
## UI screenshots
The UI change is a new read-only "Execution" section in
`AgentConfigForm`, shown only when the instance execution policy forces
Kubernetes (`executionMode=kubernetes`); there is no "before" state for
it (the section did not exist, and instances without the forced policy
render the existing picker unchanged). Captured from the new Storybook
stories added in this PR (`Product/Agent Management`):
Managed Kubernetes environment present (read-only display, no local/SSH
picker):

No managed environment available yet (warning notice, no silent local
fallback):

## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] If this change affects the UI, I have included before/after
screenshots
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [ ] All Paperclip CI gates are green
- [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
## Thinking Path
> - Paperclip orchestrates AI agents through pluggable local adapters;
codex_local wraps OpenAI's `codex` CLI.
> - The codex_local adapter declares a hard-coded
`DEFAULT_CODEX_LOCAL_MODEL = "gpt-5.3-codex"` and multiple Paperclip
consumers (UI build-config, server route, OnboardingWizard, NewAgent
form, AgentConfigForm) fall back to it when the operator doesn't pick a
model.
> - That model — and every `*-codex` model plus the older
`gpt-5/5.1/5.2` lines — is API-key-only. Codex CLI rejects them on
ChatGPT subscription auth with "The 'gpt-5.3-codex' model is not
supported when using Codex with a ChatGPT account."
> - Every codex_local agent created through the default onboarding path
inherits this pin and breaks on its first heartbeat for any user authed
via `codex login` (ChatGPT).
> - claude_local already takes the right shape: its build-config only
sets `adapterConfig.model` when the operator actually picked one, and
falls through to whatever default `claude` CLI uses.
> - Codex CLI's own default is auth-mode-aware. ChatGPT-subscription
accounts get `gpt-5.5`; API-key accounts get the codex-tuned default. A
Paperclip-side pin masks this and downgrades whichever group it wasn't
built for.
> - This PR makes codex_local match claude_local's shape: omit
`adapterConfig.model` when the user picks "default," and let the CLI
choose. Subscription users stop breaking; API-key users stop getting
downgraded.
> - The benefit is auth-mode-correct defaults with no Paperclip-side
hard pin, plus future-proofing: when OpenAI bumps the CLI default we
inherit it for free.
## What Changed
- `packages/adapters/codex-local/src/ui/build-config.ts` — only set
`adapterConfig.model` when the operator picked one (parity with
`packages/adapters/claude-local/src/ui/build-config.ts`).
- `server/src/routes/agents.ts` — drop the codex_local-specific
`next.model = DEFAULT_CODEX_LOCAL_MODEL` fallback in
`applyCreateDefaultsByAdapterType`. Bypass-sandbox default is left in
place (security posture, not a model choice).
- `ui/src/pages/NewAgent.tsx`, `ui/src/components/AgentConfigForm.tsx`,
`ui/src/components/OnboardingWizard.tsx` — stop pre-populating the model
field with `DEFAULT_CODEX_LOCAL_MODEL` when the user selects the Codex
adapter. Other adapters' defaults (gemini_local, cursor, opencode_local)
are unchanged.
- `DEFAULT_CODEX_LOCAL_MODEL` is preserved as an exported constant for
downstream consumers / plugin authors who want to opt in to a pin; we
just stop forcing it on operators who didn't ask for one.
- Test: assert `buildCodexLocalConfig` omits `model` when input is
blank.
## Verification
- `pnpm exec vitest run
packages/adapters/codex-local/src/ui/build-config.test.ts
packages/adapters/codex-local/src/server/codex-args.test.ts
server/src/__tests__/adapter-registry.test.ts
server/src/__tests__/heartbeat-model-profile.test.ts
server/src/__tests__/agent-permissions-routes.test.ts` → 74/74 passing
- `pnpm exec vitest run ui/src/lib/duplicate-agent-payload.test.ts
ui/src/lib/acpx-model-filter.test.ts` → passing
- `pnpm tsc --noEmit -p .` → clean
- Live: I separately verified live during initial investigation that on
ChatGPT-subscription auth, `gpt-5.3-codex` is rejected and `gpt-5.5` is
what Codex CLI picks by default. Omitting model lets the CLI handle
that.
## Risks
- Telemetry: any sink that reads `adapterConfig.model` for cost
attribution will now see the empty/omitted case more often. The CLI
emits the actually-used model in its event stream; downstream telemetry
should already read from there for accuracy, but worth a check.
- Operator UX: "default" now means "whatever the CLI picks" instead of a
Paperclip-known model. The selectable catalog still includes `gpt-5.5`,
`gpt-5.4`, `gpt-5.3-codex`, etc. for operators who want to pin
explicitly.
- Existing agents are unaffected — their `adapterConfig.model` is
already set; this only changes the *new-agent* default flow.
## Related work
- Depends on: an open catalog-add PR adding `gpt-5.5` to the selectable
model list and to `CODEX_LOCAL_FAST_MODE_SUPPORTED_MODELS`. Operators
who want to switch to `gpt-5.5` explicitly need that PR merged first;
this PR is the structural change that makes "default" mean "let the CLI
choose."
- Closes#5371 — codex_local default model selection persists
`gpt-5.3-codex` instead of adapter default (this PR is the exact fix
#5371 proposes).
- Related: #5132 (opencode-local: hire-time default model fails on
ChatGPT-OAuth accounts) — same problem shape on a sibling adapter; not
fixed here but worth tracking for a parallel.
- Related: #5939 (codex_local adapter hardcodes `gpt-5.3-codex-spark`
validation, fails on ChatGPT OAuth accounts regardless of configured
model) — separate validation-path bug; not fixed here.
## Model Used
Claude (Sonnet-class), running inside Paperclip as a claude_local
executor.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] If this change affects the UI, I have included before/after
screenshots
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - The issue thread is the operator surface where comments, assignee
changes, pauses, resumes, and wakeups turn human intent into agent
execution.
> - Interrupting a live run and handing work to another assignee needs
clear semantics so the product does not accidentally keep work alive,
wake the wrong participant, or hide why an agent stopped.
> - Comment-driven wakes also need strict boundaries so closed, blocked,
and dependency-driven work only resumes when there is real actionable
input.
> - This pull request codifies the interrupt handoff contract,
implements backend scheduling behavior, and gives the UI clearer
handoff/pause language.
> - The benefit is a more inspectable and predictable task lifecycle for
both operators and agents.
## Linked Issues or Issue Description
Paperclip issue: `PAP-10664` / `PAP-10751`.
Problem: interrupting or reassigning live agent work could be ambiguous
in the UI and backend. Operators needed clearer feedback about whether a
handoff wakes an agent, what pause/cancel affects, and when comments
should revive execution. The backend also needed stronger tests around
comment wake boundaries, retry supersession, and structured agent
mention dispatch.
Related GitHub PR search found broad workflow-adjacent PRs #5082, #6359,
and #4083, but no exact duplicate for this head branch or
interrupt-handoff scope.
## What Changed
- Added an interrupt handoff semantics document covering destination
behavior, wake expectations, and live-run interruption states.
- Implemented backend interrupt handoff behavior and comment wake/reopen
handling in issue routes/services and heartbeat scheduling.
- Hardened structured agent mention dispatch so mentions resolve through
the intended dispatch path.
- Added UI helpers and components for handoff chips, wake rows,
interrupt banners, pause-affects summaries, and composer guidance.
- Updated the issue properties assignee picker and issue chat/composer
surfaces to make interrupt/reassign behavior clearer.
- Added backend, UI utility, component, and Storybook coverage for the
new behavior.
- Stabilized the new UI component tests with a local `flushSync`-backed
act helper matching existing repo practice in this dependency set.
- Addressed Greptile feedback by threading historical run `errorCode`
through issue-run data and operator-interrupted chat labels.
- Addressed Greptile's cancel ordering concern by terminating/deleting
in-memory heartbeat processes before cancellation status persistence,
with regression coverage for DB update failure.
## Verification
- `git diff --check $(git merge-base HEAD origin/master)..HEAD`
- `pnpm --filter @paperclipai/ui exec vitest run
src/lib/interrupt-handoff.test.ts src/lib/issue-chat-messages.test.ts
src/components/IssueProperties.test.tsx
src/components/interrupt-handoff/InterruptHandoffViews.test.tsx
--no-file-parallelism --maxWorkers=1` — 4 files / 91 tests passed before
the Greptile follow-ups.
- `pnpm run preflight:workspace-links && pnpm exec vitest run
server/src/__tests__/heartbeat-process-recovery.test.ts
server/src/__tests__/heartbeat-retry-scheduling.test.ts
server/src/__tests__/issue-comment-reopen-routes.test.ts
server/src/__tests__/issue-tree-control-service.test.ts
server/src/__tests__/issue-update-comment-wakeup-routes.test.ts
server/src/__tests__/issues-service.test.ts --no-file-parallelism
--maxWorkers=1` — 6 files / 191 tests passed before the Greptile
follow-ups.
- `pnpm --filter @paperclipai/ui exec vitest run
src/lib/issue-chat-messages.test.ts --no-file-parallelism
--maxWorkers=1` — 1 file / 24 tests passed after the historical
`errorCode` follow-up.
- `pnpm exec vitest run server/src/__tests__/activity-service.test.ts
server/src/__tests__/activity-routes.test.ts --no-file-parallelism
--maxWorkers=1` — 2 files / 11 tests passed after the historical
`errorCode` follow-up.
- `pnpm exec vitest run
server/src/__tests__/heartbeat-process-recovery.test.ts
--no-file-parallelism --maxWorkers=1` — 1 file / 52 tests passed after
the cancel ordering follow-up.
- Greptile is green for head `272647636287d034bab8d981eaf5305865aa0f96`;
the old inline P2 is resolved/outdated.
- GitHub Actions, Socket, security-review, and Greptile checks are green
for head `272647636287d034bab8d981eaf5305865aa0f96`. The external
`security/snyk (cryppadotta)` status was still pending at
`https://app.snyk.io/org/cryppadotta/pr-checks/85b3e8f4-04e1-4f8e-9362-899c8148c23c`
after a bounded wait.
## Risks
- Medium: changes touch issue comments, wake scheduling, and live-run
interruption semantics, so regressions could affect when agents resume
or stay stopped.
- Medium: UI copy and state grouping for assignee changes may need
reviewer tuning after product review.
- Low migration risk: no database schema migration is included.
- The branch was created before the latest `origin/master` commits;
reviewers should confirm CI merge-base behavior and resolve any merge
conflicts if GitHub reports them.
> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.
## Model Used
OpenAI Codex, GPT-5-based coding agent, tool use and local command
execution enabled. Exact hosted model build and context window were not
exposed by the runtime.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [ ] If this change affects the UI, I have included before/after
screenshots
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
Screenshot note: this PR includes Storybook coverage for the new
interrupt handoff UI states rather than captured before/after browser
screenshots in this PR-creation heartbeat.
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - Routines are the recurring-work surface that lets a company keep
operating without a human manually kicking off every task
> - The base routine detail Variation C shell already landed in #7848,
but the follow-up branch still had polish work for scheduling, section
ergonomics, and the list layout
> - Operators need routine edit screens to explain trigger behavior
clearly, keep long detail pages usable on mobile/touch devices, and make
grouped routine lists easier to scan
> - This pull request rebases the remaining branch work onto current
`master`, drops the duplicate commits already merged through #7848, and
keeps only the new routine UI follow-ups
> - The benefit is a cleaner routines workflow without reopening the
already-merged shell work or carrying unrelated lockfile, workflow, or
screenshot changes
## Linked Issues or Issue Description
Refs #7848
Feature follow-up: polish the routines UI after the Variation C
routine-detail shell landed.
Problem / motivation:
- Routine trigger configuration needs clearer previews for manual,
schedule, API, and webhook execution modes.
- Routine detail sections need better responsive spacing and touch
ergonomics.
- The routines list grouping should scan like grouped records instead of
a table with heavy row dividers.
- The routine tests need a React 19-compatible render helper so the
focused routine suite can run in this workspace.
Proposed solution:
- Add cron-fire preview helpers and routine-run display helpers with
focused tests.
- Expand the routine editable and operate sections with richer trigger,
variable, run, activity, and history presentation.
- Adjust the routine detail shell and sub-sidebar spacing for
mobile/touch layout.
- Update grouped routine list presentation to use bordered group headers
with borderless rows.
- Switch affected routine tests to the repo's `flushSync` render-helper
pattern.
Alternatives considered:
- Leaving the duplicate pre-#7848 commits in the branch would recreate
conflicts and make the PR review much larger than the remaining change.
- Keeping grouped routine rows inside one bordered table was simpler,
but made the grouping hierarchy less legible.
Roadmap alignment:
- ROADMAP.md lists Scheduled Routines as a core shipped capability and
Output/Enforced Outcomes as ongoing priorities. This is polish on that
existing routines capability, not a new roadmap-level feature.
## What Changed
- Added routine scheduling preview helpers and tests for
cron/manual/API/webhook fire-policy display.
- Added routine run display helpers and tests for deduped trigger labels
and run-row subtitles.
- Polished routine detail sections, including trigger summaries, operate
views, and env/variable editing ergonomics.
- Adjusted routine detail page and sub-sidebar spacing so the
title/header area is less pinned and touch layouts center better.
- Reworked the routines list grouped layout so group headers are
bordered cards and routine rows are borderless inside each group.
- Added Storybook coverage for the routines list grouped layout and
updated the existing routine detail story.
- Repaired routine tests to use `flushSync` helpers compatible with the
installed React 19 runtime.
## Verification
- `pnpm exec vitest run ui/src/lib/cron-fires.test.ts
ui/src/lib/routine-run-display.test.ts ui/src/pages/Routines.test.tsx
ui/src/components/RoutineSubSidebar.test.tsx
ui/src/components/RoutineSaveBar.test.tsx`
- Result: 5 test files passed, 37 tests passed.
- Confirmed the rebased PR diff does not include `pnpm-lock.yaml`,
`.github/workflows/*`, or committed screenshots.
- Confirmed `origin/master` is an ancestor of the pushed branch head
after rebase.
## Risks
- Medium UI risk: this touches the routine detail and routine list
surfaces, so visual regressions are possible in edge cases not covered
by the focused tests.
- Low data risk: no schema, migration, server API, or lockfile changes
are included.
- Review note: the branch intentionally force-pushed after rebasing
because the original first three commits were already merged through
#7848.
> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.
## Model Used
OpenAI Codex, GPT-5-based coding agent runtime, with repository
shell/tool access. Exact hosted runtime model identifier and
context-window size were not exposed in the execution 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 run tests locally and they pass
- [x] I have added or updated tests where applicable
- [ ] If this change affects the UI, I have included before/after
screenshots
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - The web UI has a bottom-left account flyout menu where users reach
profile, docs, and the light/dark toggle
> - There was no in-product way for users to send feedback or report
issues — they had to find an external channel
> - We want a low-friction, always-visible entry point for feedback, and
a clean URL we can re-point later without shipping app changes
> - This pull request adds a **Feedback** item (Megaphone icon) to the
account flyout, between Documentation and the theme toggle, that opens
`https://paperclip.ing/feedback` in a new tab
> - `paperclip.ing/feedback` is a stable indirection (added to the
marketing site) that currently 302-redirects to a Google Form, so the
destination can be swapped for a richer solution later with no app
release
> - The benefit is a one-click feedback path for users and a
future-proof link the team controls
## Linked Issues or Issue Description
No public GitHub issue exists (tracked internally as Paperclip PAP-107).
Describing the underlying request inline as a feature, per
CONTRIBUTING.md path (B):
### Problem or motivation
Users have no in-app affordance to give feedback or report issues; that
friction loses signal we'd otherwise act on.
### Proposed solution
Add a Feedback item to the account flyout (Megaphone icon, between
Documentation and the theme toggle) that opens a stable
`paperclip.ing/feedback` URL in a new tab. That URL redirects to a
Google Form for now, keeping the client decoupled from the destination.
### Alternatives considered
Linking the Google Form directly from the app — rejected because it
bakes a throwaway URL into the client; the `/feedback` indirection keeps
the link clean and swappable.
### Roadmap alignment
Small, self-contained UX addition; no overlap with planned core work
(checked ROADMAP.md). The `/feedback` redirect lives in the separate
`paperclip-website` repo (Astro site on Cloudflare Pages), commit
`f65b566`. No duplicate/related PRs found in this repo (searched
feedback/flyout/menu).
## What Changed
- `ui/src/components/SidebarAccountMenu.tsx`: import `Megaphone` from
`lucide-react`; add `FEEDBACK_URL = "https://paperclip.ing/feedback"`
const next to `DOCS_URL`; insert a `Feedback` `MenuAction` between
Documentation and the theme toggle using the `external` prop so it opens
in a new tab (`target="_blank"`, `rel="noreferrer"`) and closes the
popover on click.
- `ui/src/components/SidebarAccountMenu.test.tsx`: assert the Feedback
item renders with the correct `href`, opens in a new tab, and is ordered
after Documentation and before the theme toggle.
- (Separate repo, for context) `paperclip-website` `public/_redirects`:
`/feedback` → 302 → the feedback Google Form.
## Verification
- **Unit tests:** `SidebarAccountMenu` tests pass (item renders, correct
`href`, `target="_blank"`, ordering). Run: `cd ui && npm test --
SidebarAccountMenu`.
- **Manual / canary:** The board previewed the canary build of the menu
item and accepted it. Clicking **Feedback** opens a new tab to
`paperclip.ing/feedback`.
- **Redirect:** After the Cloudflare Pages deploy propagates, `curl -sI
https://paperclip.ing/feedback` returns the Google Form in the
`Location` header.
_Screenshots:_ UI change was validated via the accepted canary preview;
the item reuses the existing `MenuAction` styling, so it visually
matches the Documentation/theme rows.
## Risks
- **Low risk.** Additive, self-contained UI change with no new state or
API calls. The only external dependency is the `paperclip.ing/feedback`
redirect (separate repo, already deployed); if it were missing the link
would 404, but it is in place. No migrations, no breaking changes.
> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR.
## Model Used
- **Claude (Anthropic).** PR authoring/orchestration:
**claude-opus-4-8** (extended thinking + tool use). The implementation
commit `b454a12d` was produced with assistance from
**claude-sonnet-4-6**. All changes reviewed before pushing.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [ ] If this change affects the UI, I have included before/after
screenshots
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [ ] 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 Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - Agent work is issue-centered, and reviewers often need to inspect
files, artifacts, and path references produced during that work.
> - Before this branch, workspace-relative paths and artifact file
references were not first-class inspectable objects in the board UI.
> - Safe file viewing needs shared resource contracts, server-side
workspace boundary checks, and UI that opens files without exposing
arbitrary host paths.
> - The workspace file viewer branch needed to stay as one active PR and
be rebased onto current `paperclipai/paperclip:master` for review.
> - This pull request adds the workspace file resource API, issue-page
file viewer and browser, markdown file-reference links, and artifact
file chips.
> - The benefit is that board users can inspect relevant files from
issue context while preserving workspace boundaries and auditability.
## Linked Issues or Issue Description
No public GitHub issue exists for this branch. Internal Paperclip
issues: `PAP-1953`, `PAP-10539`, `PAP-10733`.
Problem / motivation:
- Board users need to open workspace-relative files mentioned by agents
or attached as work-product metadata without switching to a terminal.
- The UI needs to support both direct file-path opening and workspace
browsing/searching from an issue page.
- The server must enforce company access, workspace boundaries, size
limits, rate limits, and safe audit logging.
Related PR:
- Prior closed attempt: #4442
- Single active PR for this branch: #7681
## What Changed
- Added shared workspace file resource types, validators, and
workspace-file `resourceRef` metadata validation for work products.
- Added server routes/services for resolving, listing, and previewing
workspace-relative files with access checks, scan caps, list-specific
limits, and audit logging.
- Added the issue file viewer provider, sheet, workspace browser,
command-palette action, markdown workspace-file autolinks, and artifact
file chips.
- Updated issue workspace UI and stories/tests for file browsing and
workspace file opening.
- Rebased the branch onto current `paperclipai/paperclip:master` and
updated the existing single PR branch.
- Addressed current-head Greptile follow-ups by applying `offset`
consistently across search/recent/changed file listings, restoring
stopped-service port ownership checks before auto-port reuse, and
stabilizing the workspace browser pagination test.
## Verification
Current local verification after rebase to `public/master`:
- `pnpm exec vitest run packages/shared/src/work-product.test.ts
server/src/__tests__/file-resources.test.ts
server/src/__tests__/instance-settings-routes.test.ts
server/src/__tests__/instance-settings-service.test.ts
server/src/__tests__/workspace-runtime.test.ts
ui/src/components/FileViewerSheet.test.tsx
ui/src/components/FileViewerSheet.copy.test.tsx
ui/src/components/WorkspaceFileBrowser.test.tsx
ui/src/components/WorkspaceFileMarkdownBody.test.tsx
ui/src/context/FileViewerContext.test.ts
ui/src/lib/remark-workspace-file-refs.test.ts
ui/src/lib/workspace-file-parser.test.ts
ui/src/components/IssueWorkspaceCard.test.tsx` - 13 files passed, 197
tests passed.
- `pnpm -r --filter @paperclipai/shared --filter @paperclipai/server
--filter @paperclipai/ui typecheck` - passed.
- `pnpm exec vitest run ui/src/components/WorkspaceFileBrowser.test.tsx`
- 1 file passed, 25 tests passed.
- `pnpm exec vitest run server/src/__tests__/file-resources.test.ts
server/src/__tests__/workspace-runtime.test.ts` - 2 files passed, 90
tests passed.
- `pnpm -r --filter @paperclipai/server typecheck` - passed.
- Confirmed branch is `0` behind and `46` ahead of current
`public/master` after rebase and follow-up commits.
- Confirmed the PR diff does not include `pnpm-lock.yaml`.
- Confirmed the PR diff does not include `.github/workflows` changes.
- Searched GitHub for duplicate or related workspace file viewer
PRs/issues; #4442 is the prior closed attempt and this PR is the single
active PR for the branch.
- No screenshots were committed; the task explicitly asked not to add
design screenshots or images unless they were part of the work.
Current remote verification on head
`a698a7bc10137baf7d25bd5722e1d6e0343387c1`:
- Greptile Review - success, 64 files reviewed, 0 comments added, no
unresolved Greptile review threads.
- PR workflow `verify` - success.
- Typecheck + Release Registry, General tests, workspace test shards,
serialized server suites, Build, Canary Dry Run, e2e, Socket, and Snyk -
success.
- `security-review` - neutral, with output saying a draft advisory was
filed for maintainer review and is not a merge block.
- `commitperclip PR Review / review` - cancelled after the security gate
detected flags and timed out while creating/reviewing the advisory. I
reran it once and it cancelled the same way; no actionable code/test
failure was exposed in the job logs.
## Risks
- This is a broad UI/server feature PR, so review needs to pay attention
to route authorization, workspace boundary handling, and markdown
autolink false positives.
- Workspace browsing intentionally caps list results and scan depth;
very large workspaces may require users to refine search terms.
- Remote workspace preview remains unavailable until remote file-access
support is implemented.
- The neutral commitperclip security-review advisory needs maintainer
review, but the check output says it is not a merge block.
> 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 coding agent in a Paperclip/Codex local tool-use
environment, medium reasoning, with shell/GitHub CLI tool use for branch
inspection, verification, rebase, PR update, Greptile review, and CI
inspection.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [ ] If this change affects the UI, I have included before/after
screenshots
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [ ] 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: Claude Opus 4.7 <noreply@anthropic.com>
## Summary
Rebuilds the routine detail page as **variation C** — a sub-sidebar
shell that splits the page into **ROUTINE** (Overview · Triggers ·
Variables · Secrets · Delivery) and **OPERATE** (Runs · Activity ·
History), per the engineering spec on PAP-10730. Replaces the previous
5-tab `?tab=…` layout in `ui/src/pages/RoutineDetail.tsx`.
Implements PAP-10732. Design source of truth: PAP-10730 `spec` document;
approved direction PAP-10709.
## What changed
- **Routing** (`ui/src/App.tsx`): real sub-routes under
`routines/:routineId/:section`. Bare `/routines/:id` redirects to the
last-viewed section (`localStorage`) or `overview`; old `?tab=…` URLs
redirect to the matching section for back-compat. Every section URL is
bookmarkable.
- **Shell** (`RoutineDetail.tsx`): slim 56px sticky header (title +
managed-by-plugin chip + Run / Active toggle), page-local sub-sidebar,
full-canvas section body, per-section sticky save bar. All routine
state/mutations stay in the shell and flow to sections via a
`RoutineDetailContext`.
- **New components**: `RoutineSubSidebar` (+ mobile `<Select>` picker,
roving keyboard nav), `RoutineSaveBar` (scoped dirty count, ⌘/Ctrl+S
save, Esc-discard confirm, 409 conflict recovery with Reload /
Overwrite), `RadioCard` primitive (Delivery), `RoutineTriggerCard`
(extracted from the inline editor, with human-readable cron),
`RoutineActivityRow` (expandable JSON), `lib/cron-readable`, and the
per-section components.
- **Reuse**: History mounts the existing `RoutineHistoryTab`; Variables
mounts `RoutineVariablesEditor` with a provenance banner; Secrets reuses
`EnvVarEditor` + the one-time reveal banner. No backend or schema
changes.
- **States**: per-section loading/empty/error/save-conflict and
read-only strip scaffolding (§1.6).
## Testing
- New unit tests: sub-sidebar navigation/active/dirty markers, save-bar
dirty + ⌘S + conflict recovery, cron helper.
- Existing routine tests still pass: `Routines.test.tsx`,
`RoutineHistoryTab.test.tsx`, `RoutineRunVariablesDialog.test.tsx`.
- `vitest run` (routine scope): **36 passed**. Production `vite build`:
**green**.
- Screenshots at 1440×900 + 390×844 attached to
[PAP-10732](https://example.invalid) (rendered via a new Storybook story
with fixture data).
## Out of scope (per spec)
- `/routines` list-page redo (follow-up).
- Non-owner secret-value visibility (Open Q6 — CEO escalation; built
with the spec default).
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip is the open source control plane people use to manage AI
agents, work, and company context.
> - The board UI sidebar is the main way operators keep orientation
across companies, projects, agents, issues, and settings.
> - The existing fixed expanded sidebar competes with route-specific
navigation, especially company settings and plugin routes that bring
their own contextual sidebar.
> - A collapsible primary rail preserves global navigation while giving
contextual pages more horizontal room.
> - This pull request adds a persisted collapsed rail, hover/focus peek,
keyboard toggle, and a secondary sidebar takeover model for settings and
plugin `routeSidebar` surfaces.
> - The benefit is a denser board shell that keeps the app rail
available without replacing it when a route needs its own navigation.
## Linked Issues or Issue Description
Paperclip issue: PAP-10638 Create collapsible sidebar branch.
Related GitHub PR found during duplicate search: #3838
(`feat/collapsible-sidebar`) covers a similar sidebar area but is a
different head branch and implementation. This PR intentionally packages
the work from `PAP-10638-collapsable-sidebar` into one reviewable
branch.
Problem description:
The board shell needs a first-class collapsed sidebar mode. Contextual
surfaces such as company settings and plugin route sidebars should not
replace the global app sidebar; they should collapse the app sidebar to
a rail and render their contextual navigation beside it.
## What Changed
- Added desktop collapsed/sidebar-peek state to `SidebarContext`,
including persisted user pins, route collapse requests, and forced
collapse for secondary-sidebar routes.
- Replaced the old resizable sidebar pane with `SidebarShell`, which
supports a fixed 64px rail, persisted expanded width, keyboard/pointer
resizing, and hover/focus peek overlay behavior.
- Updated `Sidebar`, sidebar nav items, project/agent sections, badges,
and account/company menu presentation for expanded, collapsed, and
peeking states.
- Added `RequestCollapsedSidebar` and `SecondarySidebar` so routes and
plugin `routeSidebar` slots can request contextual sidebar layouts
without replacing the primary app sidebar.
- Wired company settings and plugin route sidebars into the
secondary-pane takeover model.
- Added focused Vitest coverage for sidebar state precedence, shell
sizing, nav item rail rendering, keyboard shortcuts, layout takeover
behavior, and route collapse requests.
- Updated plugin authoring docs/spec references for route sidebar
behavior.
## Verification
Targeted local verification passed:
```sh
NODE_ENV=test pnpm run preflight:workspace-links && NODE_ENV=test pnpm exec vitest run ui/src/context/SidebarContext.test.tsx ui/src/components/SidebarShell.test.tsx ui/src/components/Sidebar.test.tsx ui/src/components/Layout.test.tsx ui/src/components/RequestCollapsedSidebar.test.tsx ui/src/components/SidebarNavItem.test.tsx ui/src/components/SidebarAgents.test.tsx ui/src/components/SidebarProjects.test.tsx ui/src/components/KeyboardShortcutsCheatsheet.test.tsx ui/src/hooks/useKeyboardShortcuts.test.tsx
```
Result: 10 test files passed, 88 tests passed.
Additional follow-up verification passed after review fixes:
```sh
NODE_ENV=test pnpm run preflight:workspace-links && NODE_ENV=test pnpm exec vitest run ui/src/components/Layout.test.tsx ui/src/context/SidebarContext.test.tsx && pnpm --filter /ui typecheck
```
Result: 2 test files passed, 28 tests passed, and UI typecheck passed.
Latest PR-head remote checks: Paperclip PR workflow, Snyk, Socket, and
Greptile are green; commitperclip `review` is cancelled in its
security-gate step after filing a non-blocking neutral `security-review`
check.
Notes:
- A direct run without `NODE_ENV=test` loads React's production build in
this workspace, where `act` is unavailable; the command above matches
the repo stable runner's test environment.
- I did not run Playwright/browser e2e or full workspace build/typecheck
in this PR-creation heartbeat.
- QA screenshots are attached in
https://github.com/paperclipai/paperclip/pull/7824#issuecomment-4661968387
for expanded, collapsed rail, hover peek, and settings secondary-sidebar
states.
## Risks
- Medium UI layout risk: this changes the board shell and primary
sidebar composition across many routes.
- Local storage migration risk is low: new collapsed state uses a new
key and existing width storage remains scoped to the sidebar width.
- Plugin route risk: plugin `routeSidebar` slots now render as secondary
panes on desktop, so plugin authors should confirm their route sidebar
content fits a 240px contextual pane.
- Mobile risk appears low because mobile keeps the drawer model and
gates collapsed/peek behavior to desktop.
> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.
## Model Used
OpenAI Codex coding agent based on GPT-5, with local shell/git/GitHub
CLI tool use. Exact service-side model identifier and context window
were not exposed in this runtime.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] If this change affects the UI, I have included before/after
screenshots
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [ ] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - Operators manage both company-scoped configuration and
instance-level runtime/admin settings from the board UI
> - Instance settings previously lived as their own top-level sidebar
area, separate from the company settings context operators already use
> - That split made settings navigation feel heavier and made instance
configuration less discoverable from the settings tab
> - This pull request moves instance settings under company settings
while preserving the existing instance settings routes and plugin/admin
surfaces
> - The benefit is a smaller primary sidebar and a more coherent
settings hierarchy for operators
## Linked Issues or Issue Description
- Refs #338
- Internal: PAP-10491, PAP-10538
## What Changed
- Moved instance settings navigation under the company settings area.
- Added route helpers and sidebar entries for nested instance settings
paths.
- Updated plugin/admin settings routes to use the company settings
instance scope.
- Preserved legacy instance-settings bookmarks through compatibility
redirects that keep the active company prefix.
- Updated focused UI and plugin tests for the new navigation shape.
- Stabilized the process-loss retry test that was failing the serialized
server shard in CI.
- Rebased the branch onto current `paperclipai/paperclip` `master` and
pushed the current head.
## Verification
- `pnpm exec vitest run
ui/src/components/CompanySettingsSidebar.test.tsx
ui/src/components/access/CompanySettingsNav.test.tsx
ui/src/lib/instance-settings.test.ts
ui/src/components/InstanceSidebar.test.tsx
ui/src/components/Layout.test.tsx
ui/src/components/SidebarAccountMenu.test.tsx
ui/src/pages/PluginPage.test.tsx ui/src/plugins/bridge.test.ts
packages/shared/src/validators/plugin.test.ts`
- `pnpm exec vitest run ui/src/lib/instance-settings.test.ts
ui/src/components/CompanySettingsSidebar.test.tsx
ui/src/components/access/CompanySettingsNav.test.tsx
ui/src/components/Layout.test.tsx ui/src/plugins/bridge.test.ts`
- `pnpm exec vitest run
server/src/__tests__/heartbeat-process-recovery.test.ts -t "queues
exactly one retry when the recorded local pid is dead"`
- `pnpm test:run:serialized -- --shard-index 0 --shard-count 4`
- GitHub PR checks are green on head
`fe7b0955169dcae55cbe10889c1876a70ab0b80c`, including `verify`, `General
tests (server)`, all serialized server shards, build, e2e, policy,
security checks, and Greptile.
- Confirmed the PR diff does not include `pnpm-lock.yaml` or
`.github/workflows` changes.
## Risks
- Medium UI/navigation risk: instance settings links are intentionally
moving under company settings, so stale external bookmarks to legacy
paths rely on the compatibility routing in this branch.
- Low test-only risk from the CI stabilization commit: it makes the
recovery assertion select the actual retry run by `retryOfRunId` instead
of whichever non-original run appears first.
- No database migrations.
- No dependency lockfile or workflow changes.
> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.
## Model Used
- OpenAI Codex coding agent based on GPT-5, with shell/tool execution in
a local repository worktree. Exact context window was not exposed by the
runtime.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [ ] If this change affects the UI, I have included before/after
screenshots
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip is the open source control plane people use to manage AI
agents for work.
> - Agent runtime state is surfaced in both the server API and the board
UI so operators can tell whether an agent is idle, running, paused, or
in error.
> - When an agent is already in `error`, the existing pause/resume
action slot is not useful because there is no running work to pause.
> - Operators need a direct, audited recovery path that clears the stale
error state only for agents in the same company.
> - This pull request adds a company-scoped clear-error mutation,
exposes the shared API contract, and wires the board action cluster to
show Clear error in the pause/resume slot for errored agents.
> - The benefit is that operators can recover CEO/CTO-style errored
agents without resorting to database edits or unrelated session reset
actions.
## Linked Issues or Issue Description
Refs #4021
Paperclip issue: PAP-10515 — right now the CEO and CTO agents are in
error state, but there is no way to clear the error; they appear
otherwise fine.
## What Changed
- Added shared constants, API path, and agent status type support for a
company-scoped clear-error action.
- Added the server service and route to clear an agent from `error` back
to `idle`, with company access enforcement and activity logging.
- Added OpenAPI/docs coverage for the clear-error endpoint.
- Added backend coverage for service behavior and cross-tenant
authorization.
- Updated the board agent action cluster to show a red-tinted Clear
error button only when `agent.status === "error"`.
- Updated agent properties to show a red active last-error indicator
only while the agent is currently errored.
- Added UI component tests for the error-state action and the non-error
pause/resume behavior.
## Verification
Local:
- `pnpm exec vitest run
server/src/__tests__/agents-service-clear-error.test.ts
server/src/__tests__/agent-cross-tenant-authz-routes.test.ts
ui/src/components/AgentActionButtons.test.tsx`
- `pnpm --filter @paperclipai/ui typecheck`
- `pnpm exec vitest run server/src/__tests__/openapi-routes.test.ts`
PR checks:
- Main Paperclip workflow is green on
`a7378e584d50594e7bd507a1a02985bfaaa5abf8`.
- Greptile is 5/5 with no files requiring special attention and no new
comments on the latest review.
- `commitperclip PR Review` is still red because its security-gate step
canceled after filing a draft advisory; the linked `security-review`
check is neutral and says the draft advisory is not a merge block.
Visual artifact:
- 
## Risks
Low to medium risk. The mutation is intentionally narrow, but reviewers
should check that clearing `lastError`/`lastRunError` and returning to
`idle` is the desired recovery semantics for every adapter state. The
remaining red check is from the external commitperclip security-review
workflow, not from the code/test workflow for this PR.
> 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 coding model, tool-assisted with local shell,
git, GitHub CLI, and targeted Vitest 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 run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] If this change affects the UI, I have included before/after
screenshots
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [ ] 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
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - Operators supervise live agent runs from the issue detail chat
surface.
> - The existing run menu can pause/stop work, but operators sometimes
need to stop the active run and immediately finalize the task outcome.
> - Doing those as separate actions is slower and easier to leave
half-finished.
> - This pull request adds explicit live-run finalization actions to the
issue chat run menu.
> - The benefit is a clearer operator path for stopping a live run and
marking the task done or cancelled in one ordered flow.
## Linked Issues or Issue Description
No public GitHub issue exists for this internal Paperclip task. Internal
task: PAP-10535.
## Subsystem affected
ui/ — React + Vite board UI.
## Problem or motivation
Operators can stop an active run from the issue detail chat, but
finalizing the issue outcome requires a separate status action after the
run is stopped. That extra step makes live-run finalization slower and
easier to leave incomplete.
## Proposed solution
Add explicit issue chat run-menu actions for `Stop and cancel` and `Stop
and done`, where each action cancels the active heartbeat run before
updating the issue status.
## Alternatives considered
Keep the existing two-step flow of cancelling the run first and then
changing issue status separately. That preserves current behavior but
does not solve the operator workflow gap.
## Roadmap alignment
This is a small targeted UI control-plane improvement for supervising
live agent work. It does not duplicate a planned core roadmap item found
in `ROADMAP.md`.
This PR was split from the local `master` branch on June 7, 2026. It
covers the UI-only live-run finalization action. I searched GitHub for
duplicate/related PRs; the results were broader run-control PRs, not
this exact issue-detail menu action.
## What Changed
- Added optional `runFinalizationActions` support to `IssueChatThread`
assistant message run menus.
- Added `Stop and cancel` and `Stop and done` actions on the issue
detail chat tab.
- Each action cancels the active heartbeat run before updating the issue
status.
- Added focused UI coverage to assert cancellation happens before the
status update.
- Addressed Greptile feedback for partial-failure messaging and
duplicate run-state invalidation.
## Verification
- `git diff --check origin/master..HEAD`
- `git diff --check`
- `NODE_ENV=test pnpm exec vitest run ui/src/pages/IssueDetail.test.tsx`
- Storybook screenshot capture for the live-run menu before and after
the finalization actions.
## Screenshots
Before: existing live-run menu only offered the normal stop action.

After: the live-run menu includes `Stop and cancel` and `Stop and done`.

## Risks
- Medium UI behavior risk: the new actions expose faster finalization
controls from the live-run menu. They are gated through the existing
issue detail management surface and still use the existing run cancel
and issue update APIs.
- Low migration risk: no schema, API contract, or dependency changes.
> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.
## Model Used
OpenAI Codex coding agent based on GPT-5, with shell, git, GitHub CLI,
local test execution, and Playwright browser screenshot capture. Exact
hosted model variant and context-window size were not exposed by the
runtime.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] If this change affects the UI, I have included before/after
screenshots
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip is the open source control plane people use to manage AI
agents for work.
> - Issue-thread interactions are the board/agent handoff surface for
structured decisions and confirmations.
> - Accepted checkbox confirmations can include many selected labels,
and inline selectors must stay usable across desktop and mobile devices.
> - The existing accepted checkbox summary capped labels behind a static
`+N more` chip, so operators could not inspect the hidden selections
from the card.
> - The inline selector also skipped search focus on coarse pointers,
which made mobile selection slower and less predictable.
> - This pull request makes the hidden checkbox selections expandable
and restores search focus when the selector opens.
> - The benefit is a more inspectable, faster interaction UI without
changing the interaction API contract.
## Linked Issues or Issue Description
Internal source work: [PAP-10488](/PAP/issues/PAP-10488), split from
[PAP-10495](/PAP/issues/PAP-10495).
### Subsystem affected
ui/ - React + Vite board UI
### Problem or motivation
Accepted checkbox confirmations hide selected values behind a static
count, and inline selector search does not focus on mobile/coarse
pointer devices. That makes accepted interaction cards less inspectable
and makes mobile selection slower than it needs to be.
### Proposed solution
Make hidden accepted checkbox selections expandable/collapsible directly
in the interaction card, and focus the inline selector search input
whenever the selector opens, including coarse pointer environments.
### Alternatives considered
Leaving the static `+N more` chip avoids UI state, but it keeps selected
values hidden from operators. Only restoring desktop focus keeps the old
mobile/coarse pointer gap, so the focus behavior should be consistent
across pointer types.
### Roadmap alignment
This is a small board UI polish change for the existing issue
interaction surface. It does not add a new core roadmap capability or
change the API contract.
## What Changed
- Converted the accepted checkbox `+N more` chip into an expandable
button with a `Show less` control.
- Restored search input focus whenever `InlineEntitySelector` opens,
including coarse pointer environments.
- Updated focused component tests for the expanded selection summary and
mobile/coarse-pointer focus path.
- Adjusted the interaction card test harness to use the same `flushSync`
act helper style used by nearby focused component tests.
## Verification
- `pnpm exec vitest run
ui/src/components/IssueThreadInteractionCard.test.tsx
ui/src/components/InlineEntitySelector.test.tsx` passed in
`~paperclipai/paperclip/.paperclip/worktrees/PAP-10495-interaction-selector-polish`.
## Risks
- Low risk. The change is limited to UI state and focus behavior in
existing components.
- Restoring focus on coarse pointers may open a virtual keyboard on
mobile, but that is intentional for the faster search workflow requested
by this branch.
> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected - check the roadmap
first. See `CONTRIBUTING.md`.
## Model Used
- OpenAI Codex coding agent based on GPT-5, with local repository
inspection, shell execution, git, and GitHub CLI tool use. Runtime
context window was not exposed by the 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 run tests locally and they pass
- [x] I have added or updated tests where applicable
- [ ] If this change affects the UI, I have included before/after
screenshots
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [ ] 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>
## Thinking Path
> - Paperclip is the open source control plane people use to manage AI
agents for work.
> - The artifacts surface is where operators inspect concrete outputs
from agent work.
> - Video artifacts currently render as HTML video previews, but
browsers often paint a blank first frame until a playable frame is
decoded.
> - That makes generated video work products harder to recognize from
the artifacts grid.
> - This pull request seeks a tiny thumbnail frame after metadata loads
and fades the video preview in once a frame is ready.
> - The benefit is that video artifacts are easier to scan without
changing the artifact data model or download flow.
## Linked Issues or Issue Description
Internal source work: PAP-10477, split from PAP-10495.
### What happened?
Video artifact cards can show a blank or black preview in the artifacts
grid even when the underlying video artifact is valid and playable.
### Expected behavior
Video artifact cards should paint a representative frame so operators
can visually scan generated videos from the artifact grid.
### Steps to reproduce
1. Generate or upload a video artifact whose first decoded frame is not
immediately painted by the browser.
2. Open the artifacts grid or an issue surface that renders artifact
cards.
3. Observe that the video preview can appear blank until playback or
decoding catches up.
### Paperclip version or commit
`3c65f784b640a0012ce4672c1295331d4acd8a0c` before this PR.
### Deployment mode
Board UI component behavior in local dev and hosted deployments; no
database or API behavior changes.
## What Changed
- Video artifact previews now seek to a small timestamp after metadata
loads so a real frame can be painted.
- The preview remains hidden until a frame is ready, while preserving
the existing play overlay and error fallback.
- Added a timeout safety valve so unusual media that never reports seek
completion still becomes visible.
- Added jsdom component tests covering metadata load, seek, frame-ready
behavior, and the silent seek fallback.
## Screenshots
| Before frame ready | After frame ready |
| --- | --- |
| 
| 
|
## Verification
- `pnpm exec vitest run
ui/src/components/artifacts/ArtifactCard.test.tsx` passed in
`.paperclip/worktrees/PAP-10495-artifact-video-thumbnails`.
- `pnpm --filter @paperclipai/ui build` passed in
`.paperclip/worktrees/PAP-10495-artifact-video-thumbnails`.
- `pnpm build` passed in
`.paperclip/worktrees/PAP-10495-artifact-video-thumbnails` before the
Greptile fallback patch; the post-patch UI build covers the changed
TypeScript/Vite surface.
## Risks
- Low risk. The change is limited to UI preview rendering for video
artifacts.
- Some browsers may reject or silently ignore programmatic seeking for
unusual media; the code now falls back to revealing the preview after a
short timeout rather than leaving the video invisible.
> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.
## Model Used
- OpenAI Codex coding agent based on GPT-5, with local repository
inspection, shell execution, git, and GitHub CLI tool use. Runtime
context window was not exposed by the 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 run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] If this change affects the UI, I have included before/after
screenshots
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [ ] 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>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - The control plane owns agent lifecycle, issue assignment, routine
dispatch, heartbeat wakeups, and recovery paths
> - Terminated, paused, pending-approval, or otherwise invalid agents
should not receive new work or new execution attempts
> - The old behavior left eligibility checks spread across routes and
services, so assignment and run paths could drift apart
> - This pull request centralizes agent lifecycle eligibility and
applies it consistently to assignment, invocation, routines, recovery,
and UI affordances
> - The benefit is safer autonomy: terminated agents stay paused,
invalid org-chain agents are surfaced, and active agents keep receiving
valid work
## Linked Issues or Issue Description
Refs #5103
Related: #1864
Bug fix context:
- What happened: agent assignment and heartbeat/run paths did not share
one eligibility contract, so invalid lifecycle states could still be
considered in some paths.
- Expected behavior: terminated agents must never receive new
assignments or heartbeat runs, and paused or otherwise invalid agents
should be treated as non-invokable consistently.
- Steps to reproduce: create or select an agent in an invalid lifecycle
state, then attempt assignment, routine dispatch, or heartbeat/recovery
wake paths.
- Paperclip version/commit: fixed on top of `paperclipai/paperclip`
`master` at the PR base.
- Deployment mode: applies to the server control plane in local and
authenticated deployments.
## What Changed
- Added shared agent lifecycle eligibility helpers and exported the
related shared types.
- Centralized server-side assignability and invokability checks for
issue assignment, agent routes, heartbeat dispatch, routines, recovery,
and liveness logic.
- Hardened issue assignment so invalid assignees are rejected instead of
queued for work.
- Hardened heartbeat/routine/recovery paths so terminated and otherwise
invalid agents are not woken for new runs.
- Updated board UI affordances to disable invalid agent actions and
surface org-chain warnings where relevant.
- Added targeted shared, server, and UI tests for the new eligibility
behavior.
## Verification
- `pnpm exec vitest run packages/shared/src/agent-eligibility.test.ts
server/src/__tests__/agent-invokability.test.ts
server/src/__tests__/heartbeat-archived-company-guard.test.ts
server/src/__tests__/issue-liveness.test.ts
server/src/__tests__/issues-service.test.ts
server/src/__tests__/routines-service.test.ts
ui/src/lib/company-members.test.ts ui/src/pages/Agents.test.tsx` — 8
files, 144 tests passed.
- `pnpm --filter @paperclipai/shared typecheck && pnpm --filter
@paperclipai/server typecheck && pnpm --filter @paperclipai/ui
typecheck` — passed.
- Checked the PR diff does not include `pnpm-lock.yaml` or
`.github/workflows` changes.
- Checked `ROADMAP.md`; this is a targeted control-plane safety fix and
does not duplicate a planned core feature.
- Searched GitHub for duplicate or related PRs/issues; closest related
items are linked above.
- CI and Greptile verification are pending on the opened PR and will be
followed up before requesting merge.
## Risks
Low to moderate risk. The intended behavioral shift is that invalid
agents are refused earlier and more consistently, which could expose
existing data with paused, pending, terminated, or broken org-chain
assignees. The added tests cover the critical assignment, heartbeat,
routine, recovery, shared helper, and UI paths. No database migrations
are included.
> 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 GPT-5 Codex via the Paperclip `codex_local` adapter, with
shell/git/GitHub CLI tool use. Reasoning mode and context window are
managed by the adapter runtime and 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 run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] If this change affects the UI, I have included before/after
screenshots (not applicable: no design screenshots requested; UI
behavior is covered by tests)
- [x] I have updated relevant documentation to reflect my changes (not
applicable: no user-facing command or schema docs changed)
- [x] I have considered and documented any risks above
- [ ] All Paperclip CI gates are green (pending CI)
- [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
(pending Greptile)
- [x] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - Human operators sign in through the main auth page and through
invite acceptance flows.
> - Those forms need to be understandable to assistive technology and
password managers.
> - The login fields did not consistently expose stable names, ids,
autocomplete hints, required state, and error relationships.
> - That made it easier for password managers such as 1Password to miss
the username/password pairing and harder for screen readers to associate
validation errors.
> - This pull request tightens the auth and invite form metadata while
keeping the visible flow unchanged.
> - The benefit is a smoother login and invite-acceptance experience
without changing server-side auth behavior.
## Linked Issues or Issue Description
No matching GitHub issue found after searching `login accessibility
1Password username password` in `paperclipai/paperclip` issues and PRs.
## What happened?
Login and invite auth fields were missing password-manager and
accessibility metadata, including stable field identifiers, autocomplete
hints, required semantics, and error-region relationships. That made it
easier for password managers such as 1Password to miss the
username/password pairing and harder for screen readers to associate
validation errors.
## Expected behavior
Auth fields should be discoverable as username/password fields,
distinguish sign-in and sign-up password autocomplete behavior, and
expose validation errors through an alert region referenced by invalid
inputs.
## Steps to reproduce
1. Open the main `/auth` form or an invite auth form.
2. Inspect the email, password, and sign-up name field attributes.
3. Trigger a validation/auth error and inspect whether invalid inputs
reference the displayed error text.
## Paperclip version or commit
Prior to this PR on `master`.
## Deployment mode
Board UI, all deployments using these forms.
## What Changed
- Added stable `id`, `name`, `required`, `aria-required`,
`aria-invalid`, `aria-describedby`, and autocomplete metadata to the
main auth form.
- Added invite auth field metadata so invite sign-up uses
`new-password`, invite sign-in uses `current-password`, and the email
field is recognized as `username`.
- Added alert regions for auth and invite auth errors so invalid fields
can reference the displayed error text.
- Added focused Vitest coverage for the main auth form and invite auth
flow metadata/error behavior.
## Verification
- `pnpm exec vitest run ui/src/pages/Auth.test.tsx
ui/src/pages/InviteLanding.test.tsx` passes: 2 files, 16 tests.
- `pnpm build` passes locally.
- PR CI is green on head `3531d1900`, including Build, Canary Dry Run,
Typecheck + Release Registry, e2e, general/serialized test shards,
policy, commitperclip review, security checks, and aggregate `verify`.
- Greptile Review is passing on head `3531d1900`; the P2
alert/live-region review thread was fixed and resolved.
- Rebasing onto `public-gh/master` completed cleanly; current base ref
is `e50666e4c`.
- Confirmed the branch diff does not include `pnpm-lock.yaml`,
`.github/workflows`, migrations, or design/image assets.
- No screenshots attached: this is a form metadata/accessibility change,
and the task specifically asked not to add design screenshots or images
unless they are part of the work.
## Risks
Low risk. The change is limited to UI form attributes and error wiring.
Main risk is password-manager/browser interpretation differences,
covered by asserting the emitted DOM metadata rather than a specific
vendor integration.
> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.
## Model Used
OpenAI Codex, GPT-5-based coding model with repository tool use and
shell execution. Exact hosted model ID/context window are not exposed in
this runtime.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] If this change affects the UI, I have included before/after
screenshots
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - The artifacts surface is where board users inspect files, media, and
documents produced by agents.
> - Grouped artifact stacks make that surface easier to scan by task,
but the first pass still made grouping feel secondary to media filters.
> - The follow-up request was to make grouping the default and give the
grouping control the same icon-only outline treatment used on the issues
page.
> - This pull request keeps the existing artifact grouping API/UI, then
polishes the artifacts toolbar state and Storybook review coverage.
> - The benefit is that `/artifacts` now opens in the task-stack view by
default while preserving explicit flat-mode filtering via
`groupBy=none`.
## Linked Issues or Issue Description
No public GitHub issue exists for this internal Paperclip task.
### Subsystem affected
ui/ — React + Vite board UI.
### Problem or motivation
The `/artifacts` grouping affordance was visually placed after the media
filters, rendered as a text button, and defaulted to a flat artifact
list. Internal follow-up `PAP-10465` requested the grouping icon move
left of the filters, become an icon-only outlined button like `/issues`,
and make Task grouping the default.
### Proposed solution
Default `/artifacts` to grouped Task stacks, keep explicit flat mode
available as `groupBy=none`, move the grouping control before the media
chips, and restyle it as the shared icon-only outline button pattern.
### Alternatives considered
Leaving flat mode as the implicit default was rejected because it does
not satisfy the follow-up. Keeping a text label on the grouping trigger
was rejected because `/issues` already established the icon-only outline
pattern for this class of toolbar control.
### Roadmap alignment
This aligns with the `Artifacts & Work Products` roadmap item by making
generated outputs easier to inspect and operate from the board UI.
## What Changed
- Defaulted the `/artifacts` page to `groupBy=task` when no grouping URL
param is present, while keeping explicit flat mode available with
`groupBy=none`.
- Moved the group control before the media filter chips and changed it
to an icon-only outlined button using the shared `Button` pattern.
- Updated artifact page tests to cover default Task grouping, explicit
flat mode, trigger ordering, and icon-only outline metadata.
- Updated the artifact Storybook story so its toolbar mock matches the
production ordering and grouped Task is documented as the default mode.
## Verification
- `pnpm exec vitest run ui/src/pages/Artifacts.test.tsx
ui/src/components/artifacts/ArtifactGroupCard.test.tsx` — passed.
- `pnpm --filter @paperclipai/ui typecheck` — passed.
- `pnpm --filter @paperclipai/server typecheck` — passed.
- `git diff --check` — passed.
- QA visual validation from internal follow-up PAP-10466 passed
desktop/mobile scenarios. Screenshot evidence attached there:
- Desktop default:
http://paperclip-dev:3100/api/attachments/bc81305d-f5de-485c-abeb-9e7c3d9d8539/content
- Desktop toolbar close-up:
http://paperclip-dev:3100/api/attachments/3375a62b-2110-48f3-bafa-ea98c00f99f7/content
- Mobile default:
http://paperclip-dev:3100/api/attachments/bfc5642e-9248-431e-9bac-36284dec1c89/content
- Mobile toolbar close-up:
http://paperclip-dev:3100/api/attachments/ca79401a-5ba8-464d-bc6e-aeffd47fe695/content
- GitHub PR checks on head `431964c8b` — passed, including Greptile 5/5.
## Risks
Low to medium risk. The main behavior shift is intentional: `/artifacts`
now queries grouped Task stacks by default. Existing flat mode remains
available through the grouping menu and explicit `groupBy=none` URLs.
## Model Used
OpenAI Codex, GPT-5.4 class coding model in this Paperclip heartbeat
environment, with shell, git, test, and GitHub CLI tool use. Context
window managed by the Codex runtime.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] If this change affects the UI, I have included before/after
screenshots
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - The board UI is the operator surface where users create, assign,
monitor, and review work items.
> - The product language is moving toward "tasks" for user-facing work
items while the internal API and database still use "issues".
> - PR #7543 bundled this copy migration with broader
information-architecture work, which made the branch too large for
Greptile review.
> - This pull request peels the Issue-to-Task copy migration into a
smaller, independently reviewable change.
> - The benefit is clearer user-facing terminology, less agent confusion
via the Paperclip skill note, and a smaller PR that Greptile can review.
## Linked Issues or Issue Description
Refs #7645
Refs #7543
Refs PAP-10430
This PR was split out of #7543 so the Issue-to-Task copy migration can
be reviewed separately and the remaining IA PR can fall under Greptile's
file limit.
## What Changed
- Preserves Scott Tong's original `PAP-57` copy-only commit, with author
and co-author credit intact, to rename user-facing "Issues" copy to
"Tasks" across the UI while keeping routes/API/internal symbols as
`issue`.
- Updates onboarding and release-smoke browser selectors from `Create &
Open Issue` to `Create & Open Task`.
- Adds a terminology note to `skills/paperclip/SKILL.md` clarifying that
task and issue refer to the same Paperclip work item.
- Resolves the only cherry-pick conflict by keeping current search
artifacts support and changing visible search copy to "tasks".
## Verification
- `pnpm --filter @paperclipai/ui build` passed.
- `NODE_ENV=test pnpm exec vitest run
ui/src/components/IssuesList.test.tsx ui/src/components/Sidebar.test.tsx
ui/src/components/NewIssueDialog.test.tsx
ui/src/pages/IssueDetail.test.tsx` passed: 4 files, 66 tests.
- `git diff --check origin/master...HEAD` passed.
- Diff is 80 files, below Greptile's 100-file limit.
- Before/after UI copy examples: "Issues" -> "Tasks", "New Issue" ->
"New Task", "Create & Open Issue" -> "Create & Open Task".
## Risks
- Medium copy-risk: this intentionally changes user-facing terminology
broadly while keeping internal issue identifiers and routes unchanged.
- Some docs and APIs still say `issue`; the skill note clarifies this so
agents do not treat task and issue as separate entities.
- Browser-level visual validation is expected from CI because this local
container is missing usable browser dependencies.
> 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
Scott Tong authored the original `PAP-57` copy migration, assisted by
Claude Opus 4.8 and Paperclip agents per the preserved commit metadata.
Codex / GPT-5-class coding agent with shell, GitHub CLI, and repository
access performed the PR split, conflict resolution, skill note, and
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 run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] If this change affects the UI, I have included before/after
screenshots
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: scotttong <scott.tong@gmail.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - The affected subsystem is the heartbeat execution path that turns
issue assignment into adapter-backed work in a selected workspace.
> - PAP-10409 and sibling follow-ups failed before useful adapter output
because project/workspace identity became incoherent.
> - A project-workspace-linked child issue could keep
`projectWorkspaceId` / execution workspace state while losing
`projectId`, then a git-sensitive local adapter could fall through
toward an invalid fallback cwd.
> - Paperclip needs to treat coherent workspace identity as part of the
live-path contract, not only as post-failure cleanup.
> - This pull request documents that rule, repairs issue inheritance,
and blocks git-sensitive adapter launch before it can run from the wrong
cwd.
> - The benefit is a bounded recovery path: affected issues are repaired
explicitly, future malformed workspaces fail fast with a clear recovery
action, and the UI surfaces that reason.
## Linked Issues or Issue Description
Refs #7646
Bug report fields:
- Summary: adapter-backed follow-up issues can fail before doing work
when issue creation/inheritance preserves workspace ids but drops
project identity.
- Affected issues: internal Paperclip issues PAP-10408 through
PAP-10412, especially PAP-10409.
- Steps to reproduce: create a project-scoped parent/follow-up tree
where a child issue keeps `projectWorkspaceId` or an inherited execution
workspace but has `projectId: null`, then launch a git-sensitive local
adapter such as `codex_local`.
- Expected behavior: Paperclip derives or preserves coherent project
identity during issue creation, and heartbeat refuses malformed
git-sensitive workspace launches with one clear recovery action.
- Actual behavior before this PR: the run could reach adapter bootstrap
with an incoherent workspace context and fail with git errors such as
`fatal: not a git repository (or any parent up to mount point /srv)`.
- Root cause: child/follow-up issue inheritance preserved workspace
execution context without coherent project context. That let heartbeat
workspace resolution/adapter launch reach a fallback cwd instead of
refusing the malformed workspace state up front.
## What Changed
- Documented the adapter workspace-coherence live-path precondition in
`doc/execution-semantics.md`.
- Updated issue creation/inheritance so workspace-inheriting issues
preserve or derive project identity, while existing mismatch validation
still rejects incoherent project/workspace combinations.
- Added a heartbeat preflight guard for git-sensitive local adapters
that validates effective cwd, persisted workspace identity, project
workspace identity, and required git metadata before launch.
- Added `workspace_validation` recovery actions for this failure class
and ensured the source issue gets a visible, idempotent recovery
comment.
- Surfaced workspace-validation recovery state in issue rows, blocked
notices, and recovery action cards, including the manual-repair wake
policy label.
- Added focused regression coverage for issue inheritance, all heartbeat
workspace-validation guard branches, recovery display helpers, and UI
recovery components.
## Verification
- `pnpm exec vitest run
server/src/__tests__/heartbeat-workspace-session.test.ts`
- Result: 1 test file passed, 68 tests passed.
- `pnpm exec vitest run
ui/src/components/IssueRecoveryActionCard.test.tsx`
- Result: 1 test file passed, 12 tests passed.
- `pnpm exec vitest run ui/src/components/IssueBlockedNotice.test.tsx
ui/src/components/IssueRecoveryActionCard.test.tsx`
- Result: 2 test files passed, 18 tests passed.
- `pnpm --filter @paperclipai/ui typecheck`
- Result: passed.
- `pnpm exec vitest run
server/src/__tests__/heartbeat-plugin-environment.test.ts
server/src/__tests__/issues-service.test.ts
server/src/__tests__/heartbeat-workspace-session.test.ts
server/src/__tests__/heartbeat-process-recovery.test.ts
ui/src/components/IssueBlockedNotice.test.tsx
ui/src/components/IssueRecoveryActionCard.test.tsx
ui/src/lib/recovery-display.test.ts`
- Result: 7 test files passed, 200 tests passed before the final
guard-branch additions; the changed server file was re-run above.
- UI coverage: `ui/storybook/stories/source-issue-recovery.stories.tsx`
contains rendered scenarios for the generic recovery chip,
workspace-validation recovery chip, blocked notice indicator, recovery
action card, and issue-row chip.
- Screenshot capture attempt: Storybook started successfully on
`http://127.0.0.1:6016/`, but screenshots could not be captured in this
runner because `agent-browser` launched an unusable Chrome binary and
Playwright Chromium failed on missing system library `libatk-1.0.so.0`;
the runner is non-root and lacks passwordless sudo for installing
browser dependencies.
- Hosted CI on final commit `969594e7` is green, including `verify`,
`Build`, `Typecheck + Release Registry`, `General tests (server)`,
workspace suites, serialized server suites, `Canary Dry Run`, and `e2e`.
- Roadmap checked: no duplicate roadmap item; this is a tightly scoped
reliability fix for existing heartbeat/workspace behavior.
- Duplicate PR search checked: no open PR matched `workspace coherence
adapter cwd`.
## Risks
- Medium risk: heartbeat launch is stricter for git-sensitive local
adapters and can now block malformed workspace states before adapter
execution.
- Mitigation: the guard is limited to local git-sensitive adapters and
records a source-scoped recovery action with structured evidence instead
of retrying indefinitely.
- Compatibility: valid project/workspace execution paths continue
normally; explicit project/workspace mismatches remain rejected.
> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.
## Model Used
OpenAI Codex, GPT-5-based `codex_local` coding agent with terminal/tool
use. Work was produced through Paperclip issue execution with focused
local test runs.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [ ] If this change affects the UI, I have included before/after
screenshots
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - The board UI is the control surface for issues, projects, agents,
goals, workspaces, and operator settings.
> - The existing navigation and list surfaces make several
high-frequency workflows feel harder to scan than they should,
especially around projects and agents.
> - The product direction is to improve those surfaces without breaking
the existing route model or forcing a new IA on every operator at once.
> - This pull request now keeps the dependent IA, project identity, and
agent-list visual refresh work together while the Issue-to-Task copy
migration is split into #7651.
> - The benefit is a clearer left nav, better project identity, denser
agent/project list rows, and brand-aligned status treatment while
preserving the classic default experience behind a flag.
## Linked Issues or Issue Description
Refs #7645
Refs #7651
Internal planning/work references: PAP-53, PAP-56, PAP-58, PAP-59,
PAP-60, PAP-61, PAP-68, PAP-69, PAP-70, PAP-71, PAP-72, PAP-75, PAP-76,
PAP-80, PAP-85, PAP-86, PAP-87, PAP-88, PAP-89.
## What Changed
- Adds `enableStreamlinedLeftNavigation`, defaulting off, and gates
sidebar presentation so classic navigation remains the default.
- Adds project icon persistence, validation, portability, picker UI, and
`ProjectTile` rendering while defaulting new projects to neutral gray.
- Adds projects-list task-count and budget summary data with focused
server/shared/UI coverage.
- Refreshes agent list rows, row actions, active/recent sidebar
behavior, and status capsule/chip styling for the approved brand state
system.
- Removes the placeholder Conference room and Artifacts nav/routes from
the finalized experimental nav direction.
- Removes `pnpm-lock.yaml` and the Issue-to-Task copy migration from
this PR diff; the copy migration now lives in #7651.
## Verification
- Existing branch verification from the authored commits: UI typecheck,
targeted unit tests, and light/dark visual checks for `/agents`, agent
detail, and design-guide status states.
- Maintainer cleanup verification on `75e34e5`: `git diff --check
origin/master...HEAD` passed, the `design/` diff is empty, and the PR
diff is 61 files, below Greptile's 100-file review limit.
- `pnpm --filter @paperclipai/ui build` passed.
- `NODE_ENV=test pnpm exec vitest run
ui/src/components/Sidebar.test.tsx` passed: 1 file, 8 tests.
- CI and Greptile should rerun on the latest push.
## Risks
- Broad UI surface area: the experimental flag keeps the classic nav
default, but changed shared components such as `EntityRow`,
`ProjectTile`, and agent status badges could affect multiple pages.
- Database migration: `projects.icon` is additive and nullable, but
migration ordering and portability import/export must stay aligned.
- The Issue-to-Task copy migration is now separated into #7651, so
reviewers should evaluate this PR as IA/project/agent presentation work
only.
- Visual regressions are possible across smaller widths because the PR
intentionally changes dense list-row layouts.
## Model Used
Claude Opus 4.8 assisted the original feature commits.
Paperclip-Paperclip agents assisted some planning/design commits. Codex
/ GPT-5-class coding agent with shell, GitHub CLI, and repository access
performed this PR-readiness cleanup and split.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] If this change affects the UI, I have included before/after
screenshots
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Dotta <bippadotta@protonmail.com>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - Agent work is coordinated through issues, comments, interactions,
and approval-style handoffs.
> - Existing issue-thread interactions could ask questions, suggest
tasks, and request confirmation, but they did not support a structured
checkbox confirmation payload for choosing one or more options.
> - That gap made board/user confirmations harder to validate
consistently across API callers, plugin helpers, CLI tooling, and the
UI.
> - This pull request adds the shared checkbox confirmation contract,
server handling, client helpers, and issue-thread UI needed to render
and submit structured selections.
> - The benefit is that agents can request bounded multi-select
confirmations in the same audited issue-thread flow as other Paperclip
interactions.
## Linked Issues or Issue Description
- No public GitHub issue found for this exact branch. Internal Paperclip
issue: PAP-10415 / PAP-10441 requested creating this PR for the checkbox
confirmation issue-thread UI component work.
- GitHub duplicate search performed for checkbox confirmation /
issue-thread interaction PRs; no matching open PR was found.
- Related issue search result `#7497` was unrelated company file cleanup
work, so it is not linked as a related issue.
## What Changed
- Added shared types, validators, constants, and tests for
`request_checkbox_confirmation` interactions.
- Extended server issue-thread interaction service and routes for
checkbox confirmation creation, validation, expiration, and response
handling.
- Added CLI, MCP, and plugin SDK helper coverage so external callers can
create the new interaction shape consistently.
- Updated the issue-thread interaction UI to render checkbox
confirmations with min/max bounds, selection summaries, stale-target
states, and accept/decline flows.
- Documented the checkbox confirmation interaction contract in the
Paperclip skill/API reference.
## Verification
- Rebased cleanly on `paperclipai/paperclip` `master` fetched into
`public-gh/master` at `a4fa0eaf5`.
- Confirmed the PR diff does not include `pnpm-lock.yaml` or
`.github/workflows` changes.
- Ran focused tests with `NODE_ENV=test`:
```sh
NODE_ENV=test pnpm run preflight:workspace-links
NODE_ENV=test pnpm exec vitest run packages/shared/src/issue-thread-interactions.test.ts server/src/__tests__/issue-thread-interaction-routes.test.ts server/src/__tests__/issue-thread-interactions-service.test.ts ui/src/components/IssueThreadInteractionCard.test.tsx ui/src/lib/issue-thread-interactions.test.ts cli/src/__tests__/issue-subresources.test.ts cli/src/__tests__/project-goal.test.ts packages/mcp-server/src/tools.test.ts packages/plugins/sdk/tests/testing-actions.test.ts
```
Result: 8 test files passed, 78 tests passed.
- CI on latest head `63b9e55` is green.
- Greptile Review passed on latest head; GraphQL review-thread check
shows all Greptile threads resolved.
## Risks
- Medium surface area because the interaction contract touches shared
validators, server routes/services, UI rendering, CLI, MCP, plugin SDK
helpers, and docs.
- No database migrations are included.
- `pnpm-lock.yaml` is intentionally excluded per repository lockfile
policy.
- UI screenshots are not attached because the task explicitly requested
not to add design screenshots or images unless they were part of the
work; component tests cover the new rendering and interaction states.
> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.
## Model Used
- OpenAI Codex coding agent based on GPT-5, with repository file access,
shell command execution, git/GitHub CLI tooling, and Paperclip
control-plane API access. Exact hosted model ID/context-window metadata
is not exposed inside this runtime.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [ ] If this change affects the UI, I have included before/after
screenshots
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip orchestrates AI agents for zero-human companies
> - Test infrastructure across server, ui, packages/* runs on Vitest
> - Dependabot opened a narrow bump (3.2.4 → 3.2.6), but the wider
workspace is on 3.2.4 and the major-version bridge to v4 needs a
coordinated change set across configs and tests
> - Staying on 3.x indefinitely leaves us behind on Vitest 4 (perf,
pool, and config improvements) and forces repeated patch-only dependabot
churn
> - This pull request upgrades Vitest to 4.1.8 across the workspace,
updates `server/vitest.config.ts` and `scripts/run-vitest-stable.mjs`
for the new API, and adjusts two UI tests for the new assertion
semantics
> - The benefit is a single, coherent Vitest 4 upgrade that supersedes
#7570 and gets us on the supported major line
## What Changed
- Bump `vitest` from `3.2.4` to `4.1.8` across root, `server`, `ui`, and
all `packages/*` (including plugin examples and sandbox providers)
- Update `server/vitest.config.ts` for Vitest 4 config surface
- Update `scripts/run-vitest-stable.mjs` to match the new runner
behavior
- Adjust `ui/src/components/CommentThread.test.tsx` and
`ui/src/components/MarkdownEditor.test.tsx` for Vitest 4 matcher/timing
semantics
- Refresh `pnpm-lock.yaml`
## Verification
- `pnpm install` resolves cleanly with the new lockfile
- `pnpm -w -r test` (server, ui, packages) runs under Vitest 4.1.8
## Risks
- Major-version Vitest bump: behavioral changes in pools, fake timers,
and matcher strictness can surface flake. Test config and the two UI
tests were updated to match v4 semantics; broader test runs should be
watched on CI before merge.
- Supersedes dependabot PR #7570 (3.2.4 → 3.2.6); that PR should be
closed.
## Model Used
- Claude (Anthropic) — `claude-opus-4-7`, extended thinking, tool use
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
- [ ] I have run tests locally and they pass
- [ ] I have added or updated tests where applicable
- [ ] If this change affects the UI, I have included before/after
screenshots
- [ ] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] I will address all Greptile and reviewer comments before
requesting merge
Closes#7570
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - Operators need a way to inspect files and work products created by
agents across a company without opening each issue one by one.
> - The existing issue detail surfaces already show attachments and
outputs, but there was no company-level artifacts index or search-result
affordance for artifact-like records.
> - The backend needed a company-scoped artifacts projection API that
preserves issue/run attribution and safe links back to source records.
> - The UI needed a first-class Artifacts page, sidebar entry, reusable
artifact cards, and deep-link handling that keeps company prefixes
intact.
> - This pull request adds the company artifacts API and page, then
wires artifacts into search and issue output surfaces.
> - The benefit is a single place to browse, filter, and open generated
work products and attachments while preserving company boundaries.
## Linked Issues or Issue Description
Fixes#7622.
Feature request fields:
- Problem/motivation: company operators need a consolidated artifacts
surface for attachments and work products produced by agents.
- Proposed solution: add a company-scoped artifacts projection endpoint,
a board Artifacts route, reusable cards, sidebar navigation, and
artifact search integration.
- Alternatives considered: keep artifact discovery only on individual
issue pages; that forces operators to know the source issue before
finding generated outputs.
- Roadmap alignment: checked `ROADMAP.md`; this is a focused board
UI/API improvement and does not duplicate a listed roadmap item.
## What Changed
- Added shared artifact types and validators.
- Added a company-scoped artifact projection service/API with tests for
attachment/work-product attribution.
- Added Artifacts board UI route, API client, sidebar link, cards,
filters, and storybook coverage.
- Added artifact result handling to company search and issue
output/deep-link flows.
- Rebased the branch onto the latest `public-gh/master` state and
resolved the route-test conflict by preserving both upstream
team-catalog coverage and artifact route coverage.
- Fixed a local Sidebar test helper so it no longer depends on a
runtime-undefined `React.act` export in this dependency install.
## Verification
- `pnpm --filter @paperclipai/ui exec vitest run
src/components/artifacts/ArtifactCard.test.tsx src/api/artifacts.test.ts
src/lib/company-routes.test.ts`
- `pnpm --filter @paperclipai/ui exec vitest run
src/pages/Artifacts.test.tsx src/pages/Search.test.tsx
src/components/Sidebar.test.tsx`
- `pnpm exec vitest run
server/src/__tests__/company-artifacts-service.test.ts
server/src/__tests__/company-search-service.test.ts
server/src/__tests__/company-search-rate-limit-routes.test.ts
server/src/__tests__/issue-agent-mutation-ownership-routes.test.ts`
- Confirmed the PR diff does not include `pnpm-lock.yaml` or
`.github/workflows/*`.
- Duplicate search: no open PRs or issues found for `artifact page
ArtifactCard` in `paperclipai/paperclip`.
Screenshots are intentionally omitted per the internal task instruction
not to add design screenshots or images to this PR unless they are
specifically part of the work. I also attempted browser capture in this
runner, but `agent-browser` failed to launch Chrome and Playwright
Chromium is missing `libatk-1.0.so.0`.
## Risks
- Low-to-medium risk: this adds a new API projection and UI surface, so
attribution/link regressions could affect artifact navigation.
- Company scoping is covered in the new service/API tests.
- No database migrations are included.
- No lockfile or workflow changes are included.
> 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 coding agent with tool use and local command
execution. Exact hosted model identifier is not exposed in this runtime.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [ ] If this change affects the UI, I have included before/after
screenshots (intentionally omitted per task instruction; browser capture
unavailable in this runner)
- [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: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip is a control plane for AI-agent companies, so execution
policy and trust boundaries are part of the product's safety contract.
> - Low-trust review work needs narrower authority than normal
same-company agents because hostile PRs, comments, attachments, and
generated output can carry prompt-injection payloads.
> - The current V1 shape gives trusted workers broad company context,
which is useful for normal execution but too permissive for a reviewer
assigned to hostile content.
> - This branch adds a `low_trust_review` preset, source-trust tagging,
route-level containment, and quarantine handling so low-trust output
does not automatically flow into higher-trust wake context.
> - The branch has been rebased onto current `origin/master`, and the
low-trust migration was renumbered to `0097_low_trust_source_trust.sql`
to avoid collisions with existing `0091` through `0096` migrations.
> - Greptile feedback was addressed by tightening low-trust detection,
preserving project-level trust policy checks, fixing issue-kind
promotion lookup, removing duplicate post-lease isolation assertion,
documenting fail-closed source-trust behavior, bounding ancestry checks,
enforcing runtime issue context for CEOs, awaiting accepted-plan monitor
authorization, and making low-trust issue source-trust tagging atomic.
> - The benefit is a first production slice of deny-by-default review
containment with regression coverage for the main control-plane pivot
surfaces.
Fixes#7531.
## What Changed
- Added shared trust-policy types and validators, plus
database/source-trust fields for issues, comments, documents, and work
products.
- Implemented server enforcement for low-trust issue scope, agent
self-view redaction, secret/plugin/runtime denial paths, promotion
checks, and quarantined continuation/wake context.
- Added focused low-trust regression tests for resolver behavior, source
trust, route authorization, heartbeat preflight ordering, runtime
containment, and quarantine redaction.
- Added board UI affordances for selecting/reviewing the low-trust
preset and surfacing source-trust badges in relevant issue views.
- Added `doc/LOW-TRUST-PRESETS.md`, updated
`doc/SPEC-implementation.md`, and committed the low-trust review
contract plan under `doc/plans/`.
- Rebasing note: the original `0097_low_trust_source_trust.sql`
migration was renamed to `0097_low_trust_source_trust.sql`; the SQL uses
`ADD COLUMN IF NOT EXISTS` so users who already applied the old-numbered
migration are not broken by the renumbered migration.
## Verification
- Rebased branch onto current `origin/master` and force-pushed with
lease to `origin/PAP-10211-low-trust-agent` at head `2719f31e3`.
- Confirmed the PR diff does not include `pnpm-lock.yaml` or
`.github/workflows` changes.
- Resolved upstream UI/comment conflicts by preserving deleted-comment
tombstone behavior and low-trust source-trust badges/metadata.
- Renumbered the low-trust source-trust migration to
`0097_low_trust_source_trust.sql`; the SQL uses `ADD COLUMN IF NOT
EXISTS` so users who already applied an old-numbered copy are not
broken.
- `pnpm exec vitest run ui/src/lib/issue-chat-messages.test.ts
server/src/__tests__/heartbeat-workspace-session.test.ts`
- `pnpm exec vitest run server/src/__tests__/source-trust.test.ts
server/src/__tests__/workspace-runtime-service-authz.test.ts
ui/src/lib/trust-policy-ui.test.ts
ui/src/components/TrustPresetSection.test.tsx`
- `pnpm run typecheck:build-gaps`
- `git diff --check`
- GitHub checks pass on head `2719f31e3`: build, typecheck/release
registry, general tests, serialized server suites, e2e, canary, verify,
policy/review, Socket, and Snyk.
- Greptile Review passes with Confidence Score 5/5 and zero unresolved
Greptile review threads.
- No design screenshots/images were added because the task explicitly
says not to add them unless they are specifically part of the work.
## Risks
- Medium risk: this touches shared trust-policy contracts, server
authorization paths, heartbeat context generation, migration metadata,
and UI preset controls.
- Low-trust containment is intentionally deny-by-default; legitimate
future review workflows may need explicit allowlisted exceptions.
- Plugin/runtime/security surfaces are broad, so regression tests cover
the current known routes but future integrations must route through the
same containment layer.
- The PR is ready for review; GitHub checks are green and Greptile is
5/5.
> 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 coding agent, tool-enabled shell and GitHub CLI
workflow.
## 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 run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] UI changes are covered by focused tests; no screenshots were added
per task instruction not to add design images unless specifically
required
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - Issue detail pages promote certain artifact work products into the
dedicated Output surface while also listing raw attachments below
> - Document-like artifacts such as plan markdown can currently be
promoted like binary outputs, which makes the same work product story
look like both an output and a document/attachment
> - The output surface should stay focused on inspectable generated
media, archives, PDFs, WebAssembly, SVG/images, and true binary
deliverables while document-like artifacts remain in the document or
attachment flow
> - This pull request filters document-like artifact metadata out of the
Output section and avoids duplicating the attachments that back promoted
outputs
> - The benefit is a cleaner issue detail page where plans and markdown
reports do not appear as binary outputs, while real output files still
get highlighted
## Linked Issues or Issue Description
Fixes#7609
Refs PAP-10354
Refs PAP-10369
## What Changed
- Added output MIME-type normalization and eligibility checks for issue
artifact work products.
- Filtered markdown, text, JSON, XML, CSV, YAML, source-like files, and
generic binary artifacts with document-like filenames out of promoted
issue outputs.
- Kept video, image including SVG, PDF, ZIP, WebAssembly, and true
binary artifacts eligible for the Output section.
- Hid attachments that back promoted outputs while leaving filtered
document-like artifact attachments visible.
- Preserved the full image attachment set for chat image gallery lookup
even when promoted image outputs are hidden from the attachment list.
- Added focused tests for output eligibility, glyph labeling, output
promotion, attachment filtering, gallery image preservation, and the
output section render behavior.
## Verification
- `pnpm vitest run ui/src/lib/issue-output.test.ts
ui/src/pages/IssueDetail.test.tsx
ui/src/components/issue-output/IssueOutputSection.test.tsx`
- GitHub PR checks are green on
`7d1b80f9702f20ab86cc502bffce599b13f1b088`.
- Greptile confidence score is 5/5 and both Greptile review threads are
resolved.
## Risks
- Low risk. The change only affects UI classification of Paperclip
artifact work products. The main behavioral risk is an uncommon
text-like generated artifact no longer appearing in the Output section;
it remains available through the normal attachment/document surfaces.
> 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 GPT-5 Codex via Paperclip CodexCoder, with repository tool use
and local command 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 run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] If this change affects the UI, I have included before/after
screenshots
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
Fixes#7551
## Thinking Path
> - Paperclip is the control plane for AI-agent companies, and reusable
company/team setup is part of making those companies faster to launch.
> - The teams catalog work introduces app-shipped team templates that
can be browsed, previewed, and installed into a company.
> - Catalog installation crosses several contracts: bundled package
contents, shared API types, server import/install behavior, CLI
workflows, and the board UI.
> - Agents also need a safe path through catalog installs: scoped
company selection, explicit source policy, approval fallback for agent
creation, and preserved catalog provenance.
> - This pull request extracts the completed teams catalog branch into
one reviewable PR on top of `public-gh/master`.
> - The benefit is a reusable teams catalog foundation with server, CLI,
package, docs, and hidden UI surfaces kept in sync.
## What Changed
- Added the `@paperclipai/teams-catalog` package with bundled/optional
team definitions, generated manifest, validators, catalog builder tests,
and migration notes.
- Added shared teams catalog types/validators plus server routes and
services for listing, previewing, and installing catalog teams.
- Integrated catalog install with company portability, skill/source
policy checks, provenance metadata, origin hashes, target-manager
reparenting, and installed/out-of-date detection.
- Added CLI `teams` commands and agent-safe company selection behavior,
including `company current` and approval fallback for forbidden
agent-run installs.
- Added hidden Team Catalog UI/API/query surfaces, Storybook fixtures,
and targeted UI tests while keeping the UI route out of primary
navigation.
- Added docs for CLI/company/teams catalog behavior and removed
generated screenshot artifacts from the PR diff.
## Verification
- `pnpm exec vitest run cli/src/__tests__/company.test.ts
cli/src/__tests__/teams.test.ts
packages/teams-catalog/src/catalog-builder.test.ts
packages/teams-catalog/src/shipped-catalog.test.ts
server/src/__tests__/agent-permissions-service.test.ts
server/src/__tests__/company-portability.test.ts
server/src/__tests__/company-skills-service.test.ts
server/src/__tests__/teams-catalog-routes.test.ts
server/src/__tests__/teams-catalog-service.test.ts
server/src/__tests__/teams-catalog-install-no-overrides.test.ts
ui/src/lib/company-routes.test.ts ui/src/pages/TeamCard.test.tsx
ui/src/pages/TeamCatalog.test.tsx
ui/src/pages/useInstallTeamCatalogEntry.test.tsx`
- `pnpm --filter @paperclipai/shared typecheck && pnpm --filter
@paperclipai/teams-catalog typecheck && pnpm --filter paperclipai
typecheck && pnpm --filter @paperclipai/server typecheck && pnpm
--filter @paperclipai/ui typecheck`
- Confirmed branch is rebased onto `public-gh/master` (`78dc3625a`) and
`public-gh/master` is an ancestor of `HEAD`.
- Confirmed PR diff excludes `pnpm-lock.yaml`, `.github/workflows/*`,
generated screenshot images, and screenshot helper scripts.
## Risks
- Medium review surface: this crosses package generation, shared
contracts, server install behavior, CLI, docs, and hidden UI code.
- Catalog install behavior creates agents/projects/tasks/skills and must
keep company scoping, permissions, source policy, and provenance checks
strict.
- `pnpm-lock.yaml` is intentionally excluded per repo policy;
CI/default-branch automation owns lockfile refresh.
- The Team Catalog UI is included but hidden from primary navigation, so
future enablement should re-check visual QA before exposure.
> 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`.
>
> ROADMAP checked: this aligns with reusable companies/templates and
plugin-adjacent onboarding work. This PR packages work already developed
on the Paperclip task branch for review.
## Model Used
- OpenAI Codex, GPT-5 series coding agent in this Paperclip session;
exact runtime context window was not exposed. Used shell, git, `gh`, and
local test/typecheck tooling.
## 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 run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] If this change affects the UI, I have included before/after
screenshots, or documented why screenshots are intentionally omitted
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Bumps
[@assistant-ui/react](https://github.com/assistant-ui/assistant-ui/tree/HEAD/packages/react)
from 0.14.13 to 0.14.14.
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/assistant-ui/assistant-ui/blob/main/packages/react/CHANGELOG.md">@assistant-ui/react's
changelog</a>.</em></p>
<blockquote>
<h2>0.14.14</h2>
<h3>Patch Changes</h3>
<ul>
<li>
<p><a
href="https://redirect.github.com/assistant-ui/assistant-ui/pull/4212">#4212</a>
<a
href="5fe118d6e6"><code>5fe118d</code></a>
- feat: add MCP server support to generative toolkits (<a
href="https://github.com/Yonom"><code>@Yonom</code></a>)</p>
</li>
<li>
<p><a
href="https://redirect.github.com/assistant-ui/assistant-ui/pull/4213">#4213</a>
<a
href="dcd5897f6d"><code>dcd5897</code></a>
- feat: add provider-executed tool support to generative toolkits (<a
href="https://github.com/Yonom"><code>@Yonom</code></a>)</p>
</li>
<li>
<p><a
href="https://redirect.github.com/assistant-ui/assistant-ui/pull/4127">#4127</a>
<a
href="606c9d41f5"><code>606c9d4</code></a>
- feat(assistant-transport): honour
<code>Aui-Replay-Content-Length</code> to split sync-server replay from
live bytes (<a
href="https://github.com/Yonom"><code>@Yonom</code></a>)</p>
<p><code>useAssistantTransportRuntime</code> now reads the
<code>Aui-Replay-Content-Length</code> response header on resume and
gates the body at that byte boundary. The replay prefix is decoded while
<code>isLoading: true</code> has rendered through React, then the reader
pauses until <code>isLoading: false</code> has rendered before releasing
live bytes. Tool calls in the replayed portion are recorded as
historical and skip <code>streamCall</code> / <code>execute</code>,
while tool calls that begin after the replay boundary fire normally.
Responses without the header behave as today.</p>
</li>
<li>
<p><a
href="https://redirect.github.com/assistant-ui/assistant-ui/pull/4208">#4208</a>
<a
href="0558db2895"><code>0558db2</code></a>
- feat: add <code>updateCustom</code> to thread list runtimes, adapters,
and clients (<a
href="https://github.com/okisdev"><code>@okisdev</code></a>)</p>
</li>
<li>
<p><a
href="https://redirect.github.com/assistant-ui/assistant-ui/pull/4214">#4214</a>
<a
href="69540af906"><code>69540af</code></a>
- feat: add renderText helpers for tool call status text (<a
href="https://github.com/Yonom"><code>@Yonom</code></a>)</p>
</li>
<li>
<p><a
href="https://redirect.github.com/assistant-ui/assistant-ui/pull/4199">#4199</a>
<a
href="d9b3119777"><code>d9b3119</code></a>
- feat: a <code>defineToolkit</code> entry may now be an already-formed
<code>ToolDefinition</code> (carrying its own <code>type</code>), not
only an inline definition whose <code>type</code> the compiler infers.
This is what lets a factory like <code>new JSONGenerativeUI({ library
}).present()</code> be used directly as a tool. (<a
href="https://github.com/Yonom"><code>@Yonom</code></a>)</p>
<p>Renames the authoring types to match <code>defineToolkit</code>:
<code>ToolkitDeclaration</code> → <code>ToolkitDefinition</code>, and
adds <code>ToolkitDefinitionEntry</code> (the union of an inline tool
definition and a pre-formed <code>ToolDefinition</code>). The per-tool
inline type is now an internal <code>ToolkitDefinitionInput</code> and
is no longer exported.</p>
</li>
<li>
<p><a
href="https://redirect.github.com/assistant-ui/assistant-ui/pull/4236">#4236</a>
<a
href="ae54c55c8c"><code>ae54c55</code></a>
- feat: add <code>stubTool()</code> and experimental
<code>useAuiToolOverrides()</code> for locally executed generative
toolkit tools (<a
href="https://github.com/Yonom"><code>@Yonom</code></a>)</p>
</li>
<li>
<p><a
href="https://redirect.github.com/assistant-ui/assistant-ui/pull/4235">#4235</a>
<a
href="7640b319f7"><code>7640b31</code></a>
- Deprecate component tool registration APIs in favor of toolkit
registrations. (<a
href="https://github.com/Yonom"><code>@Yonom</code></a>)</p>
</li>
<li>
<p>Updated dependencies [<a
href="cba2b42c26"><code>cba2b42</code></a>,
<a
href="4145caaa23"><code>4145caa</code></a>,
<a
href="58f80e09b5"><code>58f80e0</code></a>,
<a
href="78ff336028"><code>78ff336</code></a>,
<a
href="5fe118d6e6"><code>5fe118d</code></a>,
<a
href="dcd5897f6d"><code>dcd5897</code></a>,
<a
href="0558db2895"><code>0558db2</code></a>,
<a
href="69540af906"><code>69540af</code></a>,
<a
href="d9b3119777"><code>d9b3119</code></a>,
<a
href="ae54c55c8c"><code>ae54c55</code></a>,
<a
href="7640b319f7"><code>7640b31</code></a>]:</p>
<ul>
<li>assistant-stream@0.3.20</li>
<li><code>@assistant-ui/core</code><a
href="https://github.com/0"><code>@0</code></a>.2.10</li>
<li>assistant-cloud@0.1.31</li>
</ul>
</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li>See full diff in <a
href="https://github.com/assistant-ui/assistant-ui/commits/HEAD/packages/react">compare
view</a></li>
</ul>
</details>
<br />
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
<details>
<summary>Dependabot commands and options</summary>
<br />
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
</details>
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
## Thinking Path
> - Paperclip orchestrates AI agents for zero-human companies
> - Its REST API is the control-plane contract for the board UI, agents,
plugins, and external integrations
> - This branch adds `/api/openapi.json`, which makes the generated
OpenAPI document part of that contract instead of an internal
implementation detail
> - Once the spec is published, it has to match the mounted Express
routes, auth model, and real HTTP behavior closely enough for client
generation and review
> - The existing spec drifted from the live server: it missed mounted
routes, documented a few nonexistent ones, omitted auth semantics, and
normalized response codes too aggressively
> - This pull request makes the generated spec track the real API
surface, exposes security requirements, and adds regression coverage so
drift is caught automatically
> - The benefit is that Paperclip's published API description becomes
trustworthy for integrators, SDK generation, and review without changing
runtime auth enforcement
## What Changed
- Added the OpenAPI endpoint wiring under `server/src/routes/openapi.ts`
so `/api/openapi.json` is generated from the current route-backed
OpenAPI builder.
- Replaced generic request/response bodies with typed schemas where
available so the generated document carries useful structure instead of
opaque blobs.
- Expanded the generated spec to cover the mounted route set, including
access/member flows, CLI auth challenge routes, invite acceptance, issue
thread interaction routes, adapter environment testing, budget policy
routes, resource memberships, secret provider routes, cloud upstream
routes, and `/api/openapi.json` itself.
- Corrected documented path mismatches such as `skills/scan` vs
`skills/scan-projects`, and other route-name/path drift.
- Added security schemes plus operation-level security metadata so
public, authenticated, board-only, and instance-admin endpoints are
distinguishable in the generated contract.
- Fixed reviewed response-code mismatches for create/accept flows and
authz failures, including `201`, `202`, and `403` cases that were
previously flattened away.
- Added `server/src/__tests__/openapi-routes.test.ts` to diff the
generated spec against mounted server routes and assert key
auth/response invariants.
- Hardened the route-drift test after review feedback: it now handles
single/double/template route literals, fails on unlisted route files
that declare router methods, and filters OpenAPI path-item keys to HTTP
methods only.
## Verification
- `pnpm exec vitest run server/src/__tests__/openapi-routes.test.ts`
- `pnpm --filter @paperclipai/ui exec vitest run
src/pages/Inbox.test.tsx`
- `pnpm -r typecheck`
- `pnpm test:run`
- `pnpm build`
Manual notes:
- Confirmed the generated spec now matches the mounted route set in the
focused regression test.
- Confirmed `/api/plugins/install` is marked privileged in the generated
security metadata.
- Confirmed `POST /api/invites/{token}/accept` documents `202`.
- Addressed the Greptile route coverage comments and reran the focused
OpenAPI test, typecheck, and build successfully.
## Risks
- Medium-low risk. The main risk is ongoing spec drift if new routes are
added without updating the OpenAPI builder, but the regression test now
fails on unknown route files that declare router methods.
- The auth metadata is descriptive only; it does not change runtime
enforcement. If reviewers assume this PR hardens server auth behavior,
that would be an incorrect expectation.
- This change increases the amount of hand-maintained OpenAPI mapping in
`server/src/routes/openapi.ts`, so future API additions still need
discipline.
> 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. See `CONTRIBUTING.md`.
## Model Used
- OpenAI Codex, GPT-5-based coding agent in Codex desktop. Exact
internal model variant/version and context-window size are not exposed
in this environment. Tool-enabled coding workflow with terminal
execution, git, and GitHub integration.
## 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 run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] If this change affects the UI, I have included before/after
screenshots or confirmed screenshots are not applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Fixes#7514 — the prefix-validation piece of the #5456 auto-linker
404-storm umbrella. The remaining pieces (404 retry guard, word-boundary
tightening, comment edit/soft-delete) stay open under #5456.
Part of #5456.
## Thinking Path
> - Paperclip orchestrates AI agents for zero-human companies
> - Agents and humans cross-reference work in markdown — issue
descriptions, comments, documents — which the UI renders through a
shared `MarkdownBody`
> - That renderer auto-links any `IDENT-123`-shaped token to an internal
`/issues/IDENT-123` link
> - But foreign tracker keys share that exact shape: a Jira `TREE-604`
(or any external `ORG-123`) mentioned in prose becomes a link to a
Paperclip issue that does not exist — it 404s, and the renderer also
fires a wasted issue-fetch for the bogus identifier
> - The set of real issue prefixes is already in the browser: every
company carries an `issuePrefix`, exposed via `CompanyContext`
> - This PR gates bare-token auto-linking to known company prefixes,
leaving explicit `issue://` / `/issues/` references and real markdown
links untouched
> - The benefit is no dead internal links from foreign keys, with no new
query, cache, or server change — and zero regression when prefixes
aren't yet known
## What Changed
- **`ui/src/lib/issue-reference.ts`** — `parseIssueReferenceFromHref`
takes an optional `knownPrefixes` set and rejects a bare `IDENT-123`
token whose prefix isn't in it; threaded through
`remarkLinkIssueReferences(options)` → tree rewrite → text and
inline-code paths. An omitted/empty set keeps the legacy permissive
behavior. Explicit `issue://` scheme and `/issues/` path forms are never
gated.
- **`ui/src/context/CompanyContext.tsx`** — adds `useOptionalCompany()`,
a non-throwing variant of `useCompany()` (returns `null` outside a
provider).
- **`ui/src/components/MarkdownBody.tsx`** — reads company prefixes via
`useOptionalCompany()` and passes them to the linkifier. The
non-throwing read keeps `MarkdownBody` renderable in provider-less
surfaces (e.g. standalone/exported markdown).
- Tests extended in `issue-reference.test.ts` (gating + remark-plugin
cases) and `MarkdownBody.test.tsx` (gating, empty-companies permissive,
explicit-path bypass).
## Verification
- `pnpm --filter @paperclipai/ui exec vitest run
src/lib/issue-reference.test.ts src/components/MarkdownBody.test.tsx` —
green (17 + 40 tests).
- Full UI suite: `pnpm --filter @paperclipai/ui exec vitest run` —
**1161 passed / 183 files**; pre-existing `MarkdownBody` link tests pass
unmodified (they hit the permissive `null`-context path), confirming no
regression.
- `pnpm --filter @paperclipai/ui run typecheck` — clean.
- _Screenshots pending — opening as draft; before/after images to follow
before marking ready._
- Manual (before/after): in an issue description containing both a real
Paperclip identifier and a Jira key — _before_ both render as
`/issues/...` links (the Jira one dead); _after_ only the real
identifier links and the Jira key is plain text.
## Risks
- **Low risk.** No server/API/migration change; pure client rendering
logic.
- A referenced issue whose company isn't in the viewer's `companies`
list stops auto-linking — acceptable, since that internal link wouldn't
resolve for that viewer anyway; explicit `/issues/IDENT` references
still render.
- During initial load (companies not yet fetched) behavior is identical
to today (permissive), so no new flicker.
## Model Used
- **Anthropic Claude Opus 4.8** (`claude-opus-4-8`), 1M-token context,
extended thinking + tool use. Plan authored and implemented with the
model; all decisions reviewed by the human contributor.
## 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 run tests locally and they pass
- [x] I have added or updated tests where applicable
- [ ] If this change affects the UI, I have included before/after
screenshots
- [x] I have updated relevant documentation to reflect my changes (no
doc changes required — behavior gated, no public API/doc surface
affected)
- [x] I have considered and documented any risks above
- [x] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Bumps
[@assistant-ui/react](https://github.com/assistant-ui/assistant-ui/tree/HEAD/packages/react)
from 0.12.23 to 0.14.13.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/assistant-ui/assistant-ui/releases">@assistant-ui/react's
releases</a>.</em></p>
<blockquote>
<h2><code>@assistant-ui/react</code><a
href="https://github.com/0"><code>@0</code></a>.14.13</h2>
<h3>Patch Changes</h3>
<ul>
<li>
<p><a
href="https://redirect.github.com/assistant-ui/assistant-ui/pull/4176">#4176</a>
<a
href="27ae936dec"><code>27ae936</code></a>
- feat: add the <code>ToolkitDeclaration</code> /
<code>ToolkitDeclarationDefinition</code> types for authoring a toolkit
permissively (a backend tool may declare
<code>description</code>/<code>parameters</code>/<code>execute</code>);
the canonical <code>Toolkit</code> keeps those fields erased. Author
with <code>defineToolkit()</code> from <code>@assistant-ui/react</code>,
which the <code>"use generative"</code> compiler strips per
build. (<a href="https://github.com/Yonom"><code>@Yonom</code></a>)</p>
</li>
<li>
<p><a
href="https://redirect.github.com/assistant-ui/assistant-ui/pull/4176">#4176</a>
<a
href="27ae936dec"><code>27ae936</code></a>
- feat: move the <code>defineToolkit</code> and <code>hitl</code>
use-generative markers from <code>@assistant-ui/next</code> into
<code>@assistant-ui/core/react</code>, so they ship once from every
distribution (<code>@assistant-ui/react</code>,
<code>@assistant-ui/react-native</code>,
<code>@assistant-ui/react-ink</code>) and stay portable across build
targets. Import them from <code>@assistant-ui/react</code> instead of
<code>@assistant-ui/next</code>; they remain no-op markers stripped at
build time by a <code>"use generative"</code> compiler. (<a
href="https://github.com/Yonom"><code>@Yonom</code></a>)</p>
</li>
<li>
<p>Updated dependencies [<a
href="27ae936dec"><code>27ae936</code></a>,
<a
href="27ae936dec"><code>27ae936</code></a>]:</p>
<ul>
<li>assistant-stream@0.3.19</li>
<li><code>@assistant-ui/core</code><a
href="https://github.com/0"><code>@0</code></a>.2.9</li>
</ul>
</li>
</ul>
<h2><code>@assistant-ui/react-langgraph</code><a
href="https://github.com/0"><code>@0</code></a>.14.4</h2>
<h3>Patch Changes</h3>
<ul>
<li><a
href="https://redirect.github.com/assistant-ui/assistant-ui/pull/4125">#4125</a>
<a
href="e639a11838"><code>e639a11</code></a>
- chore: drop tracker-behaviour explainer comments left behind in
satellite runtimes (<a
href="https://github.com/Yonom"><code>@Yonom</code></a>)</li>
</ul>
<h2><code>@assistant-ui/react</code><a
href="https://github.com/0"><code>@0</code></a>.14.11</h2>
<h3>Patch Changes</h3>
<ul>
<li><a
href="https://redirect.github.com/assistant-ui/assistant-ui/pull/4125">#4125</a>
<a
href="e639a11838"><code>e639a11</code></a>
- chore: drop tracker-behaviour explainer comments left behind in
satellite runtimes (<a
href="https://github.com/Yonom"><code>@Yonom</code></a>)</li>
</ul>
<h2><code>@assistant-ui/react</code><a
href="https://github.com/0"><code>@0</code></a>.14.9</h2>
<h3>Patch Changes</h3>
<ul>
<li>
<p><a
href="https://redirect.github.com/assistant-ui/assistant-ui/pull/4120">#4120</a>
<a
href="372d4f0c53"><code>372d4f0</code></a>
- feat: simplify <code>MessagePrimitive.GroupedParts</code> API and add
<code>groupPartByType</code> helper. (<a
href="https://github.com/Yonom"><code>@Yonom</code></a>)</p>
<ul>
<li>New <code>groupPartByType({ ... })</code> helper builds a
<code>groupBy</code> from a <code>part.type → group-key path</code>
lookup. The map keys are typed against
<code>PartState["type"]</code> (autocomplete + typo
rejection), missing keys leave the part ungrouped, and the returned
function carries an internal memo fingerprint so the tree survives
unrelated re-renders even when reconstructed inline.</li>
<li>Special map key <code>"mcp-app"</code> matches tool-call
parts that point at an assistant-ui MCP app resource
(<code>ui://...</code>). It takes precedence over the
<code>"tool-call"</code> entry for those parts, so MCP apps
can be routed separately (e.g. rendered outside a chain-of-thought
wrapper).</li>
<li><code>groupBy</code> signature simplified from <code>(part, index,
parts) => string | string[] | null | undefined</code> to <code>(part)
=> readonly \</code>group-${string}`[] | null<code>. The 2nd/3rd args
were unused in practice. Arrays are required (no bare-string shorthand);
</code>null<code>is accepted as an alias for</code>[]` to soften the
migration.</li>
<li>Internal memoization now uses the helper's memo fingerprint when
present, otherwise rebuilds the tree per render (O(n), cheap). The
previous "pass a stable reference" advice is dropped — inline
<code>groupBy</code> is fine.</li>
<li>Docs and examples updated to lead with <code>groupPartByType</code>.
The <code>getMcpAppFromToolPart</code> branch in
<code>packages/ui</code> switches to <code>"mcp-app":
[]</code> via the helper.</li>
</ul>
</li>
<li>
<p><a
href="https://redirect.github.com/assistant-ui/assistant-ui/pull/4107">#4107</a>
<a
href="32ae846a91"><code>32ae846</code></a>
- feat: surface AI SDK v6 tool approvals as a first-class
<code>respondToApproval</code> prop on tool components. tool-call parts
in the <code>approval-requested</code> state now carry
<code>part.approval = { id, isAutomatic? }</code>; tool components call
<code>respondToApproval({ approved, reason? })</code> to ack the gate
without threading <code>chatHelpers</code> through application context.
also fixes a transient <code>requires-action</code> flicker for the
<code>approval-responded</code> state and tightens the external-message
converter so interrupt vs pending tool calls are distinguished by an
actual <code>interrupt</code>/<code>approval</code> field rather than by
<code>result === undefined</code>. (<a
href="https://github.com/okisdev"><code>@okisdev</code></a>)</p>
</li>
<li>
<p>Updated dependencies [<a
href="372d4f0c53"><code>372d4f0</code></a>,
<a
href="d4f1db428b"><code>d4f1db4</code></a>,
<a
href="32ae846a91"><code>32ae846</code></a>]:</p>
<ul>
<li><code>@assistant-ui/core</code><a
href="https://github.com/0"><code>@0</code></a>.2.6</li>
<li>assistant-stream@0.3.17</li>
</ul>
</li>
</ul>
<h2><code>@assistant-ui/react-syntax-highlighter</code><a
href="https://github.com/0"><code>@0</code></a>.14.1</h2>
<h3>Patch Changes</h3>
<ul>
<li>
<p><a
href="https://redirect.github.com/assistant-ui/assistant-ui/pull/4085">#4085</a>
<a
href="01244a5602"><code>01244a5</code></a>
- chore: update dependencies (<a
href="https://github.com/Yonom"><code>@Yonom</code></a>)</p>
</li>
<li>
<p>Updated dependencies [<a
href="b02b7012cf"><code>b02b701</code></a>,
<a
href="0a0c306286"><code>0a0c306</code></a>,
<a
href="01244a5602"><code>01244a5</code></a>,
<a
href="f2ec01ce0f"><code>f2ec01c</code></a>]:</p>
<ul>
<li><code>@assistant-ui/react</code><a
href="https://github.com/0"><code>@0</code></a>.14.8</li>
<li><code>@assistant-ui/react-markdown</code><a
href="https://github.com/0"><code>@0</code></a>.14.1</li>
</ul>
</li>
</ul>
<h2><code>@assistant-ui/react-langgraph</code><a
href="https://github.com/0"><code>@0</code></a>.14.2</h2>
<h3>Patch Changes</h3>
<ul>
<li><a
href="https://redirect.github.com/assistant-ui/assistant-ui/pull/3967">#3967</a>
<a
href="0a0c306286"><code>0a0c306</code></a>
- feat(core, react): add <code>MessagePrimitive.GenerativeUI</code>
primitive (<a
href="https://github.com/samdickson22"><code>@samdickson22</code></a>)</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/assistant-ui/assistant-ui/blob/main/packages/react/CHANGELOG.md">@assistant-ui/react's
changelog</a>.</em></p>
<blockquote>
<h2>0.14.13</h2>
<h3>Patch Changes</h3>
<ul>
<li>
<p><a
href="https://redirect.github.com/assistant-ui/assistant-ui/pull/4176">#4176</a>
<a
href="27ae936dec"><code>27ae936</code></a>
- feat: add the <code>ToolkitDeclaration</code> /
<code>ToolkitDeclarationDefinition</code> types for authoring a toolkit
permissively (a backend tool may declare
<code>description</code>/<code>parameters</code>/<code>execute</code>);
the canonical <code>Toolkit</code> keeps those fields erased. Author
with <code>defineToolkit()</code> from <code>@assistant-ui/react</code>,
which the <code>"use generative"</code> compiler strips per
build. (<a href="https://github.com/Yonom"><code>@Yonom</code></a>)</p>
</li>
<li>
<p><a
href="https://redirect.github.com/assistant-ui/assistant-ui/pull/4176">#4176</a>
<a
href="27ae936dec"><code>27ae936</code></a>
- feat: move the <code>defineToolkit</code> and <code>hitl</code>
use-generative markers from <code>@assistant-ui/next</code> into
<code>@assistant-ui/core/react</code>, so they ship once from every
distribution (<code>@assistant-ui/react</code>,
<code>@assistant-ui/react-native</code>,
<code>@assistant-ui/react-ink</code>) and stay portable across build
targets. Import them from <code>@assistant-ui/react</code> instead of
<code>@assistant-ui/next</code>; they remain no-op markers stripped at
build time by a <code>"use generative"</code> compiler. (<a
href="https://github.com/Yonom"><code>@Yonom</code></a>)</p>
</li>
<li>
<p>Updated dependencies [<a
href="27ae936dec"><code>27ae936</code></a>,
<a
href="27ae936dec"><code>27ae936</code></a>]:</p>
<ul>
<li>assistant-stream@0.3.19</li>
<li><code>@assistant-ui/core</code><a
href="https://github.com/0"><code>@0</code></a>.2.9</li>
</ul>
</li>
</ul>
<h2>0.14.12</h2>
<h3>Patch Changes</h3>
<ul>
<li>
<p><a
href="https://redirect.github.com/assistant-ui/assistant-ui/pull/4172">#4172</a>
<a
href="13157895e4"><code>1315789</code></a>
- feat: add the <code>ToolkitDeclaration</code> /
<code>ToolkitDeclarationDefinition</code> types for authoring a toolkit
permissively (a backend tool may declare
<code>description</code>/<code>parameters</code>/<code>execute</code>);
the canonical <code>Toolkit</code> keeps those fields erased. Author
with <code>defineToolkit()</code> from <code>@assistant-ui/next</code>,
which the <code>"use generative"</code> compiler strips per
build. (<a href="https://github.com/Yonom"><code>@Yonom</code></a>)</p>
</li>
<li>
<p><a
href="https://redirect.github.com/assistant-ui/assistant-ui/pull/4151">#4151</a>
<a
href="299d4488c8"><code>299d448</code></a>
- chore: drop stale <code>biome-ignore</code> pragmas now that the repo
lints with oxlint (<a
href="https://github.com/okisdev"><code>@okisdev</code></a>)</p>
</li>
<li>
<p><a
href="https://redirect.github.com/assistant-ui/assistant-ui/pull/4136">#4136</a>
<a
href="4429aa32f6"><code>4429aa3</code></a>
- centralize thread-level shared options forwarding across runtime
wrapper hooks. follow-up to <a
href="https://redirect.github.com/assistant-ui/assistant-ui/issues/4135">#4135</a>.
(<a href="https://github.com/okisdev"><code>@okisdev</code></a>)</p>
<p>new public exports from <code>@assistant-ui/core</code> (re-exported
from <code>@assistant-ui/react</code>):</p>
<ul>
<li><code>ExternalStoreSharedOptions</code>, a typed <code>Pick</code>
over <code>ExternalStoreAdapter</code> covering the four thread-level
optional fields every wrapper forwards: <code>isDisabled</code>,
<code>isSendDisabled</code>, <code>unstable_capabilities</code>,
<code>suggestions</code>.</li>
<li><code>pickExternalStoreSharedOptions(options)</code>, plucks those
four fields from a wider options object. the body uses <code>satisfies
Required<...></code> so adding a key to the type without copying
it in the function is a compile error rather than a silent missing-field
bug.</li>
<li><code>useExternalStoreSharedOptions(options)</code> (from
<code>@assistant-ui/core/react</code>), a memoized variant for wrappers
that wrap their store in <code>useMemo</code>. lets the wrapper list a
single stable <code>shared</code> reference as a dep instead of
enumerating the four fields. same <code>satisfies</code> guard
internally so the destructure stays in sync with the type.</li>
</ul>
<p>internal: every runtime wrapper hook (<code>useChatRuntime</code>,
<code>useAISDKRuntime</code>, <code>useLangGraphRuntime</code>,
<code>useA2ARuntime</code>, <code>useAgUiRuntime</code>,
<code>useAdkRuntime</code>, <code>useStreamRuntime</code>,
<code>useOpenCodeRuntime</code>) now uses these helpers instead of
inlining the conditional spreads added in <a
href="https://redirect.github.com/assistant-ui/assistant-ui/issues/4135">#4135</a>.
each wrapper sheds 20 to 40 lines of duplicated declarations and
conditional spreads; future additions to the shared option set propagate
through a single edit in <code>pickExternalStoreSharedOptions</code>
instead of touching every wrapper. no user-facing behavior change.</p>
</li>
<li>
<p><a
href="https://redirect.github.com/assistant-ui/assistant-ui/pull/4141">#4141</a>
<a
href="0b999594ff"><code>0b99959</code></a>
- fix(react): stop subtree mutations from snapping a scrolled-up
viewport back to bottom; loosen at-bottom threshold for high-DPR
displays (<a
href="https://github.com/vaniyokk"><code>@vaniyokk</code></a>)</p>
<p><code>useThreadViewportAutoScroll</code> had two related bugs
surfaced on Chrome macOS at <code>devicePixelRatio: 2</code>:</p>
<ol>
<li>subtree mutations snapped the viewport back to bottom after the user
scrolled away. <code>scrollingToBottomBehaviorRef</code> was planted on
<code>thread.runStart</code> / <code>useOnScrollToBottom</code> /
initialize / thread switch and only cleared in <code>handleScroll</code>
once <code>newIsAtBottom</code> became true. while the ref stayed set,
every non-style subtree mutation (a Radix <code>data-state</code> flip,
a markdown re-render, an image lazy-load, an attribute toggle on a
child) re-entered <code>useOnResizeContent</code>'s callback and called
<code>scrollToBottom(scrollBehavior)</code>, locking the viewport to the
bottom until reload.</li>
<li><code>isAtBottom</code> never registered as <code>true</code> on
high-DPR displays. <code>Math.abs(scrollHeight - scrollTop -
clientHeight) < 1</code> is strict-less-than, and Chrome macOS at
<code>devicePixelRatio: 2</code> clips <code>scrollTop</code> one pixel
short of <code>scrollHeight - clientHeight</code> (<code>Math.abs(1)
< 1 === false</code>), so the store never updated and
<code>ScrollToBottom</code> never moved into its disabled state.</li>
</ol>
<p>the fix combines two layers. <code>handleScroll</code> now tracks
<code>lastScrollHeight</code> alongside <code>lastScrollTop</code> and
releases the auto-stick intent when the user scrolls up with content
size unchanged, ruling out content-driven scrollTop shifts. the resize
callback also bails when neither <code>scrollHeight</code> nor
<code>clientHeight</code> has changed since the last fire, so mutations
that don't move layout never re-enter the snap path. at-bottom
auto-follow during streaming is preserved (verified by appending a
synthetic 600px child while scrolled to bottom; viewport follows to new
bottom). the threshold becomes <code><= 1</code> to absorb the 1px
sub-pixel clip.</p>
<p>closes <a
href="https://redirect.github.com/assistant-ui/assistant-ui/issues/4140">#4140</a>.</p>
</li>
<li>
<p><a
href="https://redirect.github.com/assistant-ui/assistant-ui/pull/4160">#4160</a>
<a
href="e76611fcb8"><code>e76611f</code></a>
- feat: add <code>indicator</code> support to
<code>MessagePrimitive.GroupedParts</code>. (<a
href="https://github.com/Yonom"><code>@Yonom</code></a>)</p>
<p>Restores loading-state handling that was dropped from the grouped
renderer. <code>GroupedParts</code> now emits a synthetic <code>{ part:
{ type: "indicator" } }</code> render call you handle with
<code>case "indicator"</code> in your <code>switch
(part.type)</code> — render a "thinking…" dot or any loading
affordance.</p>
<ul>
<li>The indicator is only ever emitted while the message is
<strong>running</strong>, so its presence alone means "render
loading UI here" — there's no <code>status</code> to branch
on.</li>
<li>New <code>indicator</code> prop restricts which running states
qualify: <code>"never"</code>, <code>"empty"</code>
(no parts yet), <code>"no-text"</code> (default — last part
isn't <code>text</code>/<code>reasoning</code>, e.g. the model ended on
a tool call), or <code>"always"</code> (any running
state).</li>
</ul>
</li>
<li>
<p><a
href="https://redirect.github.com/assistant-ui/assistant-ui/pull/4162">#4162</a>
<a
href="eef724efe4"><code>eef724e</code></a>
- fix: drop phantom sibling messages when an external store swaps an
optimistic message id mid-run (<a
href="https://redirect.github.com/assistant-ui/assistant-ui/issues/4037">#4037</a>).
(<a href="https://github.com/Yonom"><code>@Yonom</code></a>)</p>
<p>Messages can now be flagged <code>metadata.isOptimistic</code>.
Optimistic messages are treated as ephemeral: they only ever live on the
current head branch (the repository evicts off-branch optimistic
messages whenever the head moves) and they are never written to
persisted state (<code>export()</code> omits them). The AI SDK v6
adapter flags the streaming assistant message as optimistic, so when its
client-generated id is replaced by a server-provided one mid-run, the
stale placeholder no longer lingers as a phantom branch (e.g.
<code>BranchPicker</code> showing <code>2/2</code> on a turn the user
never branched). Unlike the reverted blanket id-diff (<a
href="https://redirect.github.com/assistant-ui/assistant-ui/issues/4040">#4040</a>),
only explicitly-optimistic messages are affected, so legitimate
<code>onEdit</code> / <code>onReload</code> /
<code>switchToBranch</code> branches are preserved.</p>
</li>
<li>
<p><a
href="https://redirect.github.com/assistant-ui/assistant-ui/pull/4175">#4175</a>
<a
href="2dec3aeba0"><code>2dec3ae</code></a>
- chore: update dependencies (<a
href="https://github.com/Yonom"><code>@Yonom</code></a>)</p>
</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="426c4acd6e"><code>426c4ac</code></a>
chore: update versions (<a
href="https://github.com/assistant-ui/assistant-ui/tree/HEAD/packages/react/issues/4177">#4177</a>)</li>
<li><a
href="27ae936dec"><code>27ae936</code></a>
feat: move use-generative markers to core/react, extract the compiler,
add <a href="https://github.com/a"><code>@a</code></a>...</li>
<li><a
href="275aef6774"><code>275aef6</code></a>
chore: update versions (<a
href="https://github.com/assistant-ui/assistant-ui/tree/HEAD/packages/react/issues/4129">#4129</a>)</li>
<li><a
href="2dec3aeba0"><code>2dec3ae</code></a>
chore: update dependencies (<a
href="https://github.com/assistant-ui/assistant-ui/tree/HEAD/packages/react/issues/4175">#4175</a>)</li>
<li><a
href="eef724efe4"><code>eef724e</code></a>
fix: drop phantom siblings on optimistic message id swap (<a
href="https://github.com/assistant-ui/assistant-ui/tree/HEAD/packages/react/issues/4037">#4037</a>)
(<a
href="https://github.com/assistant-ui/assistant-ui/tree/HEAD/packages/react/issues/4162">#4162</a>)</li>
<li><a
href="13157895e4"><code>1315789</code></a>
feat: add <code>@assistant-ui/next</code> + "use generative"
toolkit support (<a
href="https://github.com/assistant-ui/assistant-ui/tree/HEAD/packages/react/issues/4172">#4172</a>)</li>
<li><a
href="fcb6baf161"><code>fcb6baf</code></a>
feat(core): tool <code>display</code> hint +
<code>standalone-tool-call</code> grouping (<a
href="https://github.com/assistant-ui/assistant-ui/tree/HEAD/packages/react/issues/4167">#4167</a>)</li>
<li><a
href="299d4488c8"><code>299d448</code></a>
chore: drop stale biome-ignore pragmas after migrating to oxlint (<a
href="https://github.com/assistant-ui/assistant-ui/tree/HEAD/packages/react/issues/4151">#4151</a>)</li>
<li><a
href="0b999594ff"><code>0b99959</code></a>
fix(react): release auto-stick intent on user scroll-up; fix retina
at-bottom...</li>
<li><a
href="4429aa32f6"><code>4429aa3</code></a>
refactor: centralize thread-level shared options helper (<a
href="https://github.com/assistant-ui/assistant-ui/tree/HEAD/packages/react/issues/4136">#4136</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/assistant-ui/assistant-ui/commits/@assistant-ui/react@0.14.13/packages/react">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>
## Thinking Path
> - Paperclip orchestrates AI agents for zero-human companies
> - Each agent has a heartbeat scheduler that wakes it on timers and on
events; every wake spawns an adapter (Claude / Codex / …) run that bills
the operator's subscription
> - When an operator archives a company, the agents inside it remain in
invokable states — the heartbeat scheduler never consults company status
— so timers keep firing and event-driven wakes (comments, mentions,
blockers-resolved, etc.) keep cascading
> - On real deployments this silently drains the operator's
subscription: idle archived companies wake their CEOs hourly, plus any
cross-company event cascade
> - This pull request enforces "archived ⇒ never spawns a run" as a
structural invariant by guarding the wake path AND cascading agent state
on archive/reactivate
> - The benefit is that archived companies stop billing the operator,
and the UI/queue stays consistent with the invariant
## What Changed
- `server/src/services/heartbeat.ts`:
- `enqueueWakeup()` loads the company and short-circuits when status is
not `active`. Background sources (timer, automation, events) write a
`company.inactive` skipped wake and return `null`; explicit user invokes
throw a `conflict` so the UI surfaces the real reason.
- `tickTimers()` joins agents to active companies so the scheduler does
not iterate archived-company agents at all (no skip-row noise).
- `server/src/services/companies.ts`:
- `archive(id, actor?)` pauses runnable agents with `pauseReason =
"company_archived"` inside the transaction (preserving
`pending_approval`, `terminated`, and agents paused for unrelated
reasons), then cancels `queued`/`running` heartbeat runs after the
transaction commits.
- `update(id, data, actor?)` reverses the cascade only for agents whose
`pauseReason === "company_archived"` on the `archived → active`
transition; manually-paused agents stay paused.
- Both methods emit activity-log entries (`company.archived` with
`agentsPaused` + `runsCancelled`, `company.reactivated` with
`agentsRestored`) so the audit trail fires regardless of caller.
- `packages/shared/src/constants.ts` + `server/src/services/budgets.ts`:
add `company_archived` to the legal `PauseReason` union so the
restorable marker is a first-class value.
-
`packages/db/src/migrations/0094_backfill_archived_company_agent_pauses.sql`:
backfill so existing archived-company agents become `paused /
company_archived` (excludes `pending_approval`).
- `ui/src/lib/activity-format.ts`: add the `company.reactivated` label.
## Verification
- `npx vitest run src/__tests__/companies-service.test.ts` — archive
cascade, reactivate cascade, and activity-log entries (with counts) all
pass.
- `npx vitest run
src/__tests__/heartbeat-archived-company-guard.test.ts` — timer +
on-demand + event-wake paths all blocked for archived companies;
`company.inactive` skipped-wake row written; user-initiated wakes throw
`conflict`.
- `pnpm typecheck` — clean.
- Manual repro from the bug description: archive a company, wait an
interval / post a comment on one of its issues, observe zero new
heartbeat runs.
## Risks
- Migration `0094` is a single bulk UPDATE on `agents` joined to
archived `companies`. On large deployments it briefly holds row locks on
archived-company agent rows; should be quick because the predicate is
narrow (`status NOT IN (paused, terminated, pending_approval)` and
`companies.status = 'archived'`).
- New `pauseReason` value (`company_archived`) is opaque to older
clients that only know the previous union. Acceptable because the union
is read as plain text and the contract is sync'd in the same change.
- Behavior change for users: invoking an agent in an archived company
now fails with a conflict instead of silently spawning a run. Intended.
## Model Used
- Claude (Anthropic) — model `claude-opus-4-7` ("Opus 4.7"), Claude Code
CLI, with tool use (Read/Edit/Bash/Grep). No extended thinking mode.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [ ] If this change affects the UI, I have included before/after
screenshots — N/A, no UI changes beyond an activity-log label string
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] I will address all Greptile and reviewer comments before
requesting merge
## Related Work
Fixes#1348 (`[Bug] Archived companies still running heartbeats and
consuming tokens`).
Prior attempts and parallel work in this area:
- #1365 and #1429 by @hungdqdesign (March 2026) — both closed without
merging. Same three-layer shape (`tickTimers` / `enqueueWakeup` /
`resumeQueuedRuns` + archive-route cancellation) targeting #1348. Credit
for first publicly proposing the wake-path-guard approach.
- #5865 by @stubbi (May 2026, open) — adds the same `companies.status !=
'archived'` joins to `tickTimers`, `enqueueWakeup`, `resumeQueuedRuns`,
**and** routines `tickScheduledTriggers`, bundled with plugin-table
tenant isolation (`plugin_entities` / `plugin_job_runs` / `plugin_logs`
/ `plugin_webhook_deliveries` get a `companyId` FK with `ON DELETE
CASCADE`). This PR is narrower — it does not touch routines or plugin
tables — but adds the **archive cascade** (pause agents with
`pauseReason = "company_archived"`), the **reactivate reverse**
(un-pause only that subset), the **`company_archived` pause-reason
marker**, and a **backfill migration** for pre-existing archived
companies, which #5865 does not include. Happy to coordinate sequencing
or rebase if #5865 lands first.
Surface attachment-backed artifact work products as a first-class
Output section on the issue detail page so cloud users can watch and
download agent-generated videos without host filesystem access.
- ui/src/lib/issue-output.ts: formatBytes/formatDuration/getOutputFileGlyph
helpers + getIssueOutputs selector that validates the Phase-2 attachment
artifact metadata contract and tolerates malformed metadata (degraded).
- issue-output components: IssueOutputSection, OutputPrimaryCard (native
<video>/image/generic), OutputRow, OutputVideoPlayer, OutputFileTile.
- IssueDetail: fetch work products and render the Output section between
Documents and Attachments; reuse formatBytes in the attachments list.
- DesignGuide: showcase multiple-output, degraded, and empty states.
- Focused tests for video output, empty state, multiple outputs, and
failed attachment metadata (15 tests).
Co-Authored-By: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip orchestrates AI agents and provisions sandboxed execution
environments for them; one of those provisioners is the exe.dev plugin,
which runs each agent inside a long-lived VM reached over SSH.
> - The instance-config form for that plugin is rendered generically by
`JsonSchemaForm` from the plugin's `instanceConfigSchema`, so any UX
problem with the form is split between the shared form component and the
plugin's schema/runtime code.
> - Users coming in cold hit a 12-field flat config they couldn't reason
about (PAPA-407), a form that silently submitted `cpu: 0` for untouched
optional fields (PAPA-407 root cause), a `sshPrivateKey` textarea that
truncated RSA-4096 keys at 4096 chars (PAPA-449), a save flow that
accepted clearly-malformed keys and only blew up at lease time with raw
SSH stderr (PAPA-450, PAPA-451), and a manifest that didn't distinguish
"essential" from "advanced" knobs (PAPA-410 / PAPA-411 — duplicate
sub-issues with identical scope; PAPA-418 reconciliation kept PAPA-410
canonical).
> - These problems all point at the same surface (exe.dev sandbox
config) and are tightly coupled in code — PAPA-449/450/451 patch fields
that PAPA-410/411 introduce — so they get reviewed together.
> - This pull request lands the shared-form changes (advanced-options
disclosure, optional-scalar defaults) and the exe.dev-specific changes
(manifest restructure, longer `maxLength`, stderr translation, save-time
key validation) as five focused commits stacked on `master`.
> - The benefit is a config form that defaults to the two fields a new
user actually needs (API key + SSH private key) with a collapsible
disclosure for the rest, no silent truncation or zero-default
submissions, and SSH key problems surfaced at save time with actionable
messages instead of cryptic post-provision failures.
## What Changed
- **JsonSchemaForm advanced-options disclosure** (PAPA-410, PAPA-411 —
same scope, see note above): adds `x-paperclip-advanced` /
`x-paperclip-group` schema annotations and renders flagged fields behind
a collapsible "Advanced options" disclosure that auto-opens when a
hidden field has a validation error. Exe.dev manifest is restructured to
use the new annotations, so essentials (`apiKey`, `sshPrivateKey`) show
by default while the long tail of optional knobs is grouped under "SSH
access" / "VM resources" / "More options" headings.
- **Omit optional scalar defaults** (PAPA-407): `getDefaultForSchema` no
longer materialises `0` / `""` for optional
`number`/`integer`/`string`/`secret-ref` fields without an explicit
`default`. Object recursion drops properties whose default is
`undefined`. Fields that declare a `default` (e.g. `sshPort: 22`) still
round-trip. Adds a regression test against `getDefaultValues`.
- **Raise `sshPrivateKey` `maxLength`** (PAPA-449): bumps the exe.dev
manifest cap from 4096 to 8192 so RSA-4096 OpenSSH private keys (which
can exceed 4 KB with comments/metadata) aren't silently truncated at
submit.
- **Translate `invalid format` SSH stderr** (PAPA-450):
`formatSshFailure` now recognises `Load key … invalid format` in
combined stderr/stdout and returns a specific message naming the
key-format problem ("isn't an OpenSSH/PEM private key — confirm the
secret starts with `-----BEGIN … PRIVATE KEY-----` and isn't the `.pub`
or a PuTTY `.ppk` export") instead of dumping the raw stderr.
- **Save-time SSH key validation** (PAPA-451):
`onEnvironmentValidateConfig` inline-parses `sshPrivateKey` and rejects
common failure modes — pasted public keys, PuTTY `.ppk` format, missing
`-----END-----` footer, non-base64 body — so the form surfaces an inline
error before any VM is provisioned. Secret-ref bindings (UUIDs) are
still passed through unchanged.
## Verification
CI gates (`pnpm typecheck`, `pnpm test`, the targeted vitest suites
below) all pass.
Run locally:
```bash
# Shared form
pnpm --filter @paperclipai/ui exec vitest run src/components/JsonSchemaForm
# 9 tests pass — includes the new "omits optional scalar fields" regression
# and the three advanced-options-disclosure tests.
# exe.dev plugin
cd packages/plugins/sandbox-providers/exe-dev && pnpm test
# 32 tests pass — includes the new sshPrivateKey-validation cases
# and the new "invalid format" stderr-translation case.
```
Manual smoke (after reinstalling the plugin so the DB manifest
refreshes):
1. Open the exe.dev environment config page. **Default view shows API
Key + SSH Private Key only**, with an "Advanced options" disclosure for
everything else (PAPA-410 / PAPA-411).
2. Paste a `.pub` file's contents into SSH Private Key, click Save.
**Inline error** rejecting the wrong-format key (PAPA-451).
3. Re-paste a valid OpenSSH/PEM private key longer than 4096 bytes —
saves cleanly (PAPA-449).
4. Save the form with everything optional left blank — server no longer
rejects with `"cpu must be greater than 0 when provided"` (PAPA-407).
5. Force a bad key through via a stored secret-ref binding and lease a
VM — failure message names the key-format problem instead of dumping raw
SSH stderr (PAPA-450).
## Risks
- **PAPA-410 / PAPA-411 manifest restructure** is the largest surface
here. Schemas using `x-paperclip-*` extensions are forward-compatible
with stricter JSON Schema validators (extensions are ignored by
default), and the form gracefully renders a flat layout when no field
opts in.
- **PAPA-407** changes form-default behaviour: optional scalar fields
that previously round-tripped as `""` / `0` will now be `undefined` and
absent from the submitted payload. Downstream consumers that expected
the empty-string/zero shape need to treat the field as optional.
Spot-checked the existing exe.dev driver — it already uses
`parseOptionalString` / `parseOptionalInteger`, which treat missing
fields as `null` rather than `0`/`""`.
- **PAPA-451** adds a save-time check, so a
previously-saved-but-malformed `sshPrivateKey` raw value will now fail
to re-save. Bound secret-refs are unaffected, matching how the user
reaches the bad-key state today (via the secrets picker).
- **PAPA-449** simply raises a cap; no semantic risk.
- **PAPA-450** only kicks in on the "invalid format" code path; existing
onboarding-marker branch is untouched.
## Model Used
- Provider: Anthropic
- Model: Claude Opus 4.7 (`claude-opus-4-7`)
- Capabilities used: code reading, code editing, test execution, git/PR
mechanics, Paperclip API for issue coordination
## Checklist
- [x] PR body sections present (Thinking Path, What Changed,
Verification, Risks, Model Used, Checklist)
- [x] Unit tests added for the new behaviours (JsonSchemaForm
default-value omission + advanced disclosure; exe.dev plugin validation
+ stderr translation)
- [x] Existing tests still pass locally (`vitest run` on both packages)
- [x] No raw secrets, IP addresses, or machine-local config in commits
or PR body
- [x] Commits are atomic per linked issue (PAPA-410 / PAPA-411,
PAPA-407, PAPA-449, PAPA-450, PAPA-451)
- [x] Branch is up-to-date with `origin/master`
---------
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip orchestrates AI-agent companies through adapter-backed
local and external runtimes.
> - The agent configuration UI lets operators choose adapter models and
refresh model lists when adapters support live discovery.
> - Codex already had a live refresh path, but Claude Local only exposed
static fallback models and the UI hid the refresh action for Claude.
> - A newly available Claude Opus model should not require a code
release every time the model catalog changes.
> - This pull request adds Anthropic model discovery for Claude Local,
keeps the static fallback current with Claude Opus 4.8, and exposes the
existing refresh button in the Claude Local dropdown.
> - The benefit is that operators can refresh Claude models from the
same model selector flow they already use for Codex.
## What Changed
- Added `claude-opus-4-8` to the Claude Local fallback model list.
- Added Claude model discovery through Anthropic-compatible `GET
/v1/models` when `ANTHROPIC_API_KEY` is available.
- Added normal cache reuse, forced refresh support, a SHA-256-based
API-key fingerprint for cache keys, and warning logging for discovery
errors before fallback.
- Wired `claude_local.refreshModels` into the server adapter registry.
- Enabled the existing `Refresh models` dropdown action for
`claude_local` in `AgentConfigForm`.
- Added tests for Claude fallback, live discovery, API-failure fallback,
forced refresh, and the UI refresh-button gate.
## Verification
- `pnpm exec vitest run server/src/__tests__/adapter-models.test.ts`
- `pnpm exec vitest run ui/src/components/AgentConfigForm.test.ts`
- `pnpm --filter @paperclipai/adapter-claude-local typecheck`
- `pnpm --filter @paperclipai/server typecheck`
- `pnpm --filter @paperclipai/ui typecheck`
- Greptile review reached Confidence Score: 5/5 on commit `b796cf4f1`
with addressed threads resolved.
UI note: the visible change is a conditional action row inside the
existing model dropdown; the regression test covers that `claude_local`
now receives the refresh action.
## Risks
- Low risk. Without `ANTHROPIC_API_KEY`, Claude Local still uses the
static fallback list.
- If Anthropic model discovery fails or times out, Paperclip falls back
to the existing cached or static list.
- Bedrock environments remain on Bedrock-native model IDs.
## Model Used
OpenAI GPT-5 via Codex local coding agent, with repository file access,
shell command execution, git operations, and targeted test/typecheck
verification. Exact context window is not exposed by the runtime.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] If this change affects the UI, I have included before/after
screenshots
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] I will address all Greptile and reviewer comments before
requesting merge
## Thinking Path
> - Paperclip orchestrates AI agents for zero-human companies, so
planning approvals and child-issue fan-out are part of the core
control-plane loop.
> - Accepted plans are supposed to be a safe bridge from planning into
execution, especially when agents wake from review decisions and reuse
isolated workspaces.
> - The duplicate-subtask incident showed that an accepted plan revision
could be interpreted more than once across overlapping runs, which broke
the single-source-of-truth model for issue decomposition.
> - Fixing that required tightening the backend contract first:
accepted-plan decomposition needs an exact-once fingerprint, durable
claim state, and retry-safe child creation.
> - Once that backend behavior existed, the board still needed
visibility into what happened, so the issue detail view needed a
dedicated decomposition section instead of forcing operators to
reconstruct child creation from raw activity.
> - This pull request adds the exact-once decomposition primitive,
hardens wake routing and regressions around the incident, and surfaces
decomposition state in the UI so future incidents are both prevented and
easier to inspect.
## What Changed
- Added accepted-plan decomposition semantics to
`doc/execution-semantics.md`, including the exact-once fingerprint,
durable claim/result expectations, and retry/resume behavior.
- Added persistent accepted-plan decomposition claims in the backend,
including schema, shared types/validators, service logic, and issue
routes for creating and listing decomposition state.
- Hardened heartbeat routing so an accepted-plan continuation stays
scoped to the relevant planning issue instead of opportunistically
re-decomposing another accepted issue on the same assignee.
- Added regression coverage for the original failure modes: concurrent
same-parent retries, cross-issue accepted-plan isolation, and partial
child recreation under the same fingerprint.
- Added the `Plan decomposition` issue-detail section plus supporting
API/query-key/activity formatting updates so operators can see revision
status, owner, child counts, and the linked child issues directly in the
UI.
- Included the small follow-up UI fix so the decomposition section still
renders when the issue work mode is no longer `planning`.
## Verification
- `pnpm --filter @paperclipai/server typecheck`
- `pnpm --filter @paperclipai/ui typecheck`
- `pnpm --filter @paperclipai/db typecheck`
- `pnpm exec vitest run server/src/__tests__/issues-service.test.ts`
- `pnpm exec vitest run server/src/__tests__/issues-service.test.ts -t
"lists persisted decompositions with child issue summaries"`
- `pnpm exec vitest run server/src/__tests__/issues-service.test.ts -t
"accepted plan decomposition"
server/src/__tests__/heartbeat-accepted-plan-workspace-refresh.test.ts
server/src/__tests__/heartbeat-context-summary.test.ts`
- Manual UI path: create a planning issue without an isolated execution
workspace, add a `plan` document, accept the `request_confirmation`, let
Paperclip create child issues, then reopen the parent issue detail page
and confirm the `Plan decomposition` section shows the accepted
revision, status, idempotent-claim badge, and child links.
- Separate follow-up bug noted during manual UI validation: accepting a
plan on an issue whose run never records `workspace_finalize` is tracked
in `PAPA-445` and is not part of this PR’s fix scope.
## Risks
- This adds a new migration and a large Drizzle snapshot update;
reviewers should confirm the schema shape and generated metadata match
the intended decomposition table.
- The exact-once claim changes sit on the accepted-plan fan-out path, so
regressions there could block legitimate child creation or mis-handle
retries if the claim state machine is wrong.
- The new UI only appears when decomposition records exist; reviewers
should use the manual verification path above rather than expecting
existing issues on a stale local instance to show the section
automatically.
- `PAPA-445` remains an open follow-up for the `workspace_finalize`
accept gate when a planning handoff never records finalize; that bug can
interfere with reproducing the UI flow on isolated workspaces but does
not change the correctness of the exact-once decomposition feature
itself.
> Checked `ROADMAP.md`: this PR is a bug fix / control-plane hardening
change for accepted-plan decomposition, not a new uncoordinated roadmap
feature.
## Model Used
- OpenAI Codex via Paperclip `codex_local` (GPT-5-based coding agent;
exact backend model ID/context window not exposed in the run context),
with repository tool use, shell execution, and code-editing
capabilities.
<img width="806" height="1069" alt="Screenshot 2026-05-27 at 11 05
48 PM"
src="https://github.com/user-attachments/assets/5b00b670-96cd-4470-b0a3-581743bcae28"
/>
## 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 run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] If this change affects the UI, I have included before/after
screenshots
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip orchestrates AI agents for zero-human companies through
company-scoped control-plane workflows.
> - Agents need reusable, inspectable skills that can be installed,
reset, audited, exported, and assigned without bespoke local setup.
> - The existing skill truth model needed cleanup so bundled skills,
optional catalog skills, runtime skills, and adapter-provided skills
have clear provenance.
> - Operators also need a practical CLI and board UI for discovering and
managing company skills.
> - This pull request adds the skills CLI, packaged skills catalog,
company skills APIs, and catalog-aware board UI.
> - The benefit is a more reusable Paperclip company setup where skills
are portable, auditable, and easier for operators and agents to manage.
## What Changed
- Added `paperclipai skills` CLI commands and coverage for catalog
listing, installing, resetting, and inspecting company skills.
- Added a packaged `@paperclipai/skills-catalog` workspace with bundled
and optional skill content plus validation/build tests.
- Added shared company-skill types and validators used across CLI,
server, and UI contracts.
- Added server catalog APIs/services for company skill catalog
operations, reset semantics, audit behavior, and portability provenance.
- Updated adapter skill handling so runtime/catalog provenance remains
explicit across local adapters.
- Added board UI support for browsing and managing catalog-backed
company skills.
- Updated docs for the skills CLI/catalog flow and the company skills
Paperclip skill reference.
- Rebased the branch onto current `paperclipai/paperclip:master`; no
`pnpm-lock.yaml`, `.github/workflows`, or migration files are included
in the final PR diff.
## Verification
- Passed: `pnpm run preflight:workspace-links && pnpm exec vitest run
cli/src/__tests__/skills.test.ts
packages/skills-catalog/src/catalog-builder.test.ts
packages/skills-catalog/src/shipped-catalog.test.ts
packages/shared/src/validators/company-skill.test.ts
packages/adapter-utils/src/server-utils.test.ts
packages/plugins/create-paperclip-plugin/src/entrypoints.test.ts
server/src/__tests__/company-skills-catalog-service.test.ts
server/src/__tests__/company-skills-routes.test.ts
server/src/__tests__/company-portability.test.ts`.
- Passed: `pnpm exec vitest run
server/src/__tests__/workspace-runtime.test.ts -t "default
branch|origin/master|symbolic-ref"`.
- Attempted: full `server/src/__tests__/workspace-runtime.test.ts`. Four
provisioning tests failed while seeding an isolated worktree database
from the local Paperclip instance because the local plugin schema dump
contains a duplicate-column foreign key
(`plugin_content_machine_18a7bc327b.content_case_signals`). The
default-branch tests touched by the rebase conflict passed in the
focused run above.
- Checked final diff: no `pnpm-lock.yaml`, no `.github/workflows`, and
no migration-file changes relative to `master`.
## Risks
- Medium: this is a broad skills/catalog change touching CLI, server
APIs, shared contracts, adapter skill sync, and UI.
- Catalog validation and reset semantics need careful reviewer attention
because they affect reusable company setup and portability.
- No database migrations are included in this PR, so there is no
migration ordering/idempotency risk in the final diff.
- No lockfile is included by design; dependency resolution will be
handled by the repository lockfile workflow.
## Model Used
- OpenAI Codex coding agent based on GPT-5, running in Paperclip via the
`codex_local` adapter with shell, git, GitHub CLI, and code-editing tool
access. Exact hosted model build/context-window metadata is not exposed
in this runtime.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have run targeted tests locally and documented the local
workspace-runtime seed failure above
- [x] I have added or updated tests where applicable
- [x] If this change affects the UI, screenshots were intentionally
omitted per PAP-10124 instructions; UI behavior is covered by tests and
reviewer inspection
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip orchestrates AI agents for zero-human companies.
> - Fresh self-hosted deployments need an operator path before any
invite exists.
> - Umbrel installs are private LAN deployments, so a one-time browser
claim is appropriate only when the deployment is private and unclaimed.
> - Public deployments and installs with active invites must keep the
existing invite-only model so admin creation is not exposed broadly.
> - GitHub PR #2927 established the useful direction, but it needed to
be adapted onto current `master` rather than merged as-is.
> - This pull request adds that adapted private-only claim flow across
server, UI, docs, and regression coverage.
> - The benefit is that a fresh private Umbrel-style install can be
claimed from the browser without weakening public deployment access.
## What Changed
- Added a first-admin claim service and access route support for
one-time admin claim eligibility on private unclaimed deployments.
- Updated the bootstrap/access UI so eligible private installs show a
setup claim path, while public and invited deployments keep invite-first
behavior.
- Added a bootstrap-pending setup UX lab covering claim, invite, public,
and signed-in access states.
- Updated deployment and local development docs for authenticated
private/public behavior and the Umbrel-style claim path.
- Added server and UI regression tests for private claim, public
no-claim, active invite fallback, existing board/no-access flows, and
health exposure reporting.
- Stabilized PR handoff verification by serializing the aggregate server
Vitest workspace run, forcing `NODE_ENV=test`, and relaxing the
heartbeat batching test around legitimate recovery follow-up runs.
## Verification
- `pnpm -r typecheck`
- `pnpm build`
- `pnpm vitest --run
server/src/__tests__/heartbeat-comment-wake-batching.test.ts`
- `pnpm vitest --run
server/src/__tests__/health-dev-server-token.test.ts`
- `pnpm test:run`
- QA validation: PAP-10115 passed browser validation with screenshots
for private fresh install claim, active invite versus claim conflict,
public invite-only/claim-absent behavior, existing invite fallback, and
normal board/no-access flows.
- GitHub closeout: issue #2579 and PR #2927 were updated with the
accepted direction: adapt the implementation, do not direct-merge #2927
as-is.
## Risks
- The claim endpoint must remain private-only and one-time; a regression
here could expose admin creation on public deployments.
- Existing invite behavior must remain intact for public deployments and
installs that already have an active invite.
- The stable Vitest harness now serializes the aggregate server
workspace group; this is slower, but it avoids DB-backed suite
collisions under root workspace mode.
> 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`.
>
> ROADMAP.md checked: this is a scoped deployment bootstrap/access fix
and does not duplicate a listed roadmap project.
## Model Used
- OpenAI GPT-5 Codex via Paperclip `codex_local` for product
engineering, implementation, and verification, with tool-enabled local
code execution. Paperclip QA browser validation was performed in
PAP-10115 by the assigned QA agent; exact adapter model metadata for
that QA run is not exposed in this PR context.
## 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 run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] If this change affects the UI, I have included before/after
screenshots
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip orchestrates AI-agent companies through issues, documents,
runs, and durable company-scoped state.
> - Issue documents are where agents and operators capture plans,
handoffs, and work products.
> - Before this change, document collaboration could only happen through
whole-document edits and detached issue comments.
> - Inline document annotations need stable anchors, revision-aware
persistence, and UI affordances that do not break existing document
editing.
> - This pull request adds company-scoped document annotation threads,
comments, anchor snapshots, API routes, and board UI.
> - The benefit is that operators and agents can discuss specific
document passages without losing context as documents evolve.
## What Changed
- Added document annotation tables, schema exports, shared types,
validators, anchor hashing, and text-anchor helpers.
- Added server-side document annotation services and issue routes for
listing, creating, commenting, resolving, and reopening annotation
threads.
- Included annotation summaries in relevant issue document reads and
backup/recovery document workspace behavior.
- Added React UI for inline document highlights, comment panels, mobile
sheet behavior, deep-link focus, and resolved/open filtering.
- Added annotation design artifacts, Storybook coverage, screenshots,
and a screenshot helper script.
- Rebased the branch onto current `paperclipai/paperclip` `master` and
renumbered the annotation migration from `0085_old_swarm` to
`0091_old_swarm`; the SQL uses `IF NOT EXISTS` guards so environments
that previously applied the old migration number can safely apply the
new one.
- Adjusted the new annotation UI tests to use a local async flush helper
because this workspace's React 19.2.4 export does not expose
`React.act`.
## Verification
- `pnpm run preflight:workspace-links && pnpm exec vitest run
packages/shared/src/document-anchors.test.ts
server/src/__tests__/document-annotation-routes.test.ts
server/src/__tests__/document-annotations-service.test.ts
ui/src/components/DocumentAnnotationLayer.test.tsx
ui/src/components/IssueDocumentAnnotations.test.tsx
ui/src/lib/document-annotation-hash.test.ts
ui/src/lib/document-annotation-selection.test.ts`
- Confirmed `git diff --check` passes.
- Confirmed no `pnpm-lock.yaml` or `.github/workflows/*` files are
included in the PR diff.
## Risks
- Medium risk: this adds new persisted annotation tables and routes
across db/shared/server/ui.
- Migration risk is reduced by moving the branch migration to
`0091_old_swarm` after upstream `0090_resource_memberships` and keeping
the SQL idempotent for old `0085_old_swarm` adopters.
- UI risk is mostly around text range anchoring and panel positioning
across long documents, folded content, and mobile layouts; the PR
includes focused unit coverage and design screenshots.
> 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 coding agent, tool-using software engineering
mode. Context window size is not exposed in this Paperclip runtime.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] If this change affects the UI, I have included before/after
screenshots
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip orchestrates AI agents for zero-human companies.
> - The plugin system is how Paperclip exposes optional capabilities and
integrations without bloating the control plane.
> - Operators need the Instance Settings plugin manager to show both
installed external plugins and bundled built-in plugins.
> - Bundled plugins were available in the server/UI surface but were not
represented consistently in the plugin manager list.
> - Workspace runtime reuse also needed to stay pinned to the current
branch/base so the plugin manager can be validated from the intended
checkout.
> - This pull request shows bundled plugins in the manager, marks
experimental bundled plugins clearly, and tightens runtime/worktree
reuse guards.
> - The benefit is that operators can discover bundled plugins from the
same management screen as installed plugins without stale workspace
sessions hiding the latest branch state.
## What Changed
- Lists bundled monorepo plugin packages through the plugin routes API,
including plugin status and install metadata needed by the UI.
- Updates the plugin manager UI/API client to render bundled plugins and
display experimental badges based on installed plugin records.
- Adds server authorization coverage around plugin routes so board and
agent access stay company-scoped.
- Guards execution workspace/runtime reuse against stale base refs and
defaults new worktrees to the fetched target base.
- Expands workspace runtime tests for service reuse, stale workspace
prevention, and controlled runtime stops.
- Addressed Greptile feedback by respecting `origin/HEAD`, using async
cached bundled-plugin discovery, and avoiding duplicated UI experimental
plugin lists.
## Verification
- `pnpm exec vitest run server/src/__tests__/plugin-routes-authz.test.ts
server/src/__tests__/workspace-runtime.test.ts
server/src/__tests__/heartbeat-workspace-session.test.ts`
- `pnpm --filter @paperclipai/ui typecheck`
- `pnpm --filter @paperclipai/plugin-sdk build && pnpm --filter
@paperclipai/server typecheck`
- `pnpm --filter @paperclipai/server typecheck`
- `gh pr checks 6734 --repo paperclipai/paperclip` reports all checks
passing on `10e1ba9e0f505637cd913713fb28c2c99ae92011`.
- Greptile Review reports 5/5 on
`10e1ba9e0f505637cd913713fb28c2c99ae92011`.
- Confirmed the branch is rebased onto `public-gh/master` and the PR
diff does not include `pnpm-lock.yaml` or `.github/workflows` changes.
- UI screenshots were not captured in this PR-creation pass because the
available local board runtime is authenticated; the visible UI path is
covered by the plugin manager code changes and server/API tests above.
## Risks
- Medium risk: this touches shared plugin listing behavior and workspace
runtime reuse, so regressions could affect plugin manager visibility or
service reuse across execution workspaces.
- No database migrations.
- No lockfile or GitHub workflow 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 GPT-5 Codex, coding-agent workflow with shell/tool use in a
local Paperclip worktree. Context window not surfaced by the runtime;
reasoning mode not externally reported.
## 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 run tests locally and they pass
- [x] I have added or updated tests where applicable
- [ ] If this change affects the UI, I have included before/after
screenshots
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip orchestrates AI-agent companies through company-scoped
issues, projects, agents, and board-visible workflows.
> - The board sidebar and project list are the daily navigation surface
for that control plane.
> - Users need to keep all projects and agents accessible while hiding
resources they have intentionally left from their own sidebar.
> - That requires user-scoped resource membership state backed by
company-scoped API and database contracts.
> - The branch also needed to preserve HTTP worktree login sessions and
keep the project list easier to scan after membership grouping.
> - This pull request adds resource membership controls, sidebar leave
actions, grouped/sortable project listings, and focused tests.
> - The benefit is a cleaner personal workspace view without weakening
company-scoped access to the underlying project or agent detail pages.
## What Changed
- Added `project_memberships` and `agent_memberships` tables with
API/shared/server contracts for current-user join/leave state.
- Renumbered the membership migration to `0090_resource_memberships`
after rebasing onto current `master`, and made it idempotent for anyone
who had applied the old branch-local `0087` migration.
- Added project and agent sidebar leave actions, plus list filtering
that waits for membership state before hiding resources.
- Added grouped project listing, project sorting controls, and reserved
row subtitle height for cleaner scanning.
- Fixed HTTP auth cookie security handling so HTTP worktree sessions can
persist.
- Updated focused server and UI tests for the new membership, sidebar,
project list, and auth behavior.
## Verification
- `pnpm exec vitest run server/src/__tests__/better-auth.test.ts
server/src/__tests__/resource-memberships-routes.test.ts
ui/src/pages/Projects.test.tsx
ui/src/components/SidebarProjects.test.tsx
ui/src/components/SidebarAgents.test.tsx
ui/src/components/MembershipAction.test.tsx
ui/src/components/EntityRow.test.tsx`
- Confirmed the branch is rebased on current `origin/master`.
- Confirmed the PR diff does not include `pnpm-lock.yaml` or
`.github/workflows` changes.
## Risks
- Migration safety: low to medium. The migration now uses `IF NOT
EXISTS` / guarded constraints and is numbered after current master
migrations, but it should still get CI coverage against fresh databases.
- UI behavior: low. Left resources are hidden from sidebar only after
membership state loads; direct detail access remains available.
- Auth behavior: low. Cookie security is relaxed only for HTTP/private
local-style origins where secure cookies would prevent login
persistence.
> 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 GPT-5 Codex coding agent, tool-enabled shell/git workflow,
context window not exposed by runtime.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] If this change affects the UI, I have included before/after
screenshots
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] I will address all Greptile and reviewer comments before
requesting merge
Screenshot note: no browser screenshots were captured in this heartbeat;
the UI changes are covered by focused component tests above.
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip orchestrates AI agents for zero-human companies.
> - This branch accumulated multiple already-tested control-plane,
adapter runtime, invite, workspace, plugin, and UI quality fixes on the
primary Paperclip checkout.
> - `origin/master` advanced while those commits were still local, so
the branch needed to be preserved and reconciled before review.
> - Splitting the branch commit-by-commit against the new base produced
overlapping conflicts with recently merged upstream PRs.
> - This pull request keeps the remaining branch as one standalone PR
because the final diff is 38 files after removing screenshot artifacts,
under Greptile's 100-file cap, and can be merged independently after
review.
> - The benefit is that none of the local work is lost, the branch is
now based on current `origin/master`, and reviewers can evaluate the
reconciled changes in one place.
## What Changed
- Merged the local accumulated branch with current `origin/master` and
resolved the invite-flow overlaps from the newer upstream companies
query helper.
- Preserved the local fixes for invite existing-member behavior, invite
link copy fallback, reusable workspace selection, worktree auth, static
SPA fallback, markdown wrapping, plugin slot registration, cloud
upstream UX/server polish, project sorting, and related tests.
- Removed screenshot artifacts from the PR per review request.
- Kept the PR under the requested file limit: 38 files changed, with no
`pnpm-lock.yaml` or `.github/workflows/*` changes.
## Verification
- `NODE_ENV=test pnpm exec vitest run
ui/src/pages/CompanyInvites.test.tsx ui/src/pages/InviteLanding.test.tsx
ui/src/pages/Projects.test.tsx ui/src/plugins/slots.test.ts
ui/src/components/MarkdownBody.test.tsx
server/src/__tests__/invite-accept-existing-member.test.ts
server/src/__tests__/static-index-html.test.ts
server/src/__tests__/execution-workspaces-service.test.ts
server/src/__tests__/better-auth.test.ts
server/src/__tests__/worktree-config.test.ts`
- `NODE_ENV=test pnpm --filter @paperclipai/ui typecheck`
- `NODE_ENV=test pnpm --filter @paperclipai/server typecheck`
- Confirmed `git diff --name-only origin/master...HEAD | wc -l` is `38`.
- Confirmed no PR diff entries match `pnpm-lock.yaml`,
`.github/workflows/*`, or `screenshots/*`.
## Risks
- Medium review risk because this is a bundled rescue PR rather than
several narrow feature PRs.
- Invite flow and company cache behavior overlapped with newer upstream
changes; the merge resolution intentionally keeps the shared
`companiesListQueryOptions` helper while preserving local
existing-member invite behavior.
- Visual review evidence is no longer attached in-repo because
screenshots were removed from this PR per review request.
> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.
## Model Used
- OpenAI Codex, GPT-5-based coding agent, with repository tool access,
terminal execution, and git/GitHub CLI operations.
## 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 run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] UI screenshots were intentionally removed from this PR per review
request
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Co-authored-by: CodexCoder <codexcoder@paperclip.local>
## Thinking Path
> - Paperclip orchestrates AI agents for zero-human companies.
> - Agent creation can happen through local runtimes, managed runtimes,
and external agents that onboard through invites.
> - The old OpenClaw-oriented invite UX lived under company
settings/invites and made a gateway-specific path look like a company
access setting.
> - That hid the broader bring-your-own-agent flow and forced operators
to leave the add-agent modal when adding an external agent.
> - This pull request moves external agent invite generation into the
add-agent modal and makes the copy agent-oriented instead of
OpenClaw-only.
> - The benefit is a clearer agent-first onboarding path while company
invites stay focused on human access.
## What Changed
- Added an external-agent invite branch to the add-agent modal,
including a dedicated prompt result view with Back navigation.
- Added a shared agent onboarding prompt builder and focused modal
coverage for prompt replacement/back navigation.
- Removed the agent invite prompt UI from Company Settings and Company
Invites, leaving Company Invites focused on human access links and
invite history.
- Updated the hidden OpenClaw Gateway runtime hint to direct operators
to the add-agent invite flow instead of presenting it as a blocked
runtime card.
- Updated invite/onboarding docs, storybook coverage, and server-side
onboarding copy toward generic agent language while preserving existing
gateway compatibility.
## Verification
- `pnpm -r typecheck`
- `pnpm build`
- `FAKE_BIN="$(mktemp -d)/bin"; mkdir -p "$FAKE_BIN"; printf
'#!/bin/sh\nexit 1\n' > "$FAKE_BIN/tailscale"; chmod +x
"$FAKE_BIN/tailscale"; PATH="$FAKE_BIN:$PATH" pnpm test:run`
- `pnpm test:run` without the fake `tailscale` shim was also attempted;
it failed only in two pre-existing CLI tailnet fallback tests because
this host has a real Tailscale address (`100.125.202.3`) where those
tests expect no Tailscale.
- Focused confirmation for that host-env issue: `FAKE_BIN=...
PATH="$FAKE_BIN:$PATH" pnpm exec vitest run --project paperclipai
cli/src/__tests__/network-bind.test.ts
cli/src/__tests__/onboard.test.ts`
- Manual UI verification: served UI locally in light mode, opened
add-agent modal, generated external agent prompt, verified the generated
prompt replaces the form and Back returns to the form.
### Screenshots



## Risks
- Existing OpenClaw gateway compatibility remains, but operators now
discover external agent onboarding from the add-agent modal instead of
company settings.
- Agent invites still appear in the invite history table, so that page
may show agent-scoped invite rows even though it no longer creates agent
onboarding prompts.
- Low migration risk: no schema 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 coding agent in Codex desktop; tool-enabled
repository, shell, browser, and GitHub workflow. Context window size was
not exposed by the runtime.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] If this change affects the UI, I have included before/after
screenshots
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] I will address all Greptile and reviewer comments before
requesting merge
## Thinking Path
> - Paperclip orchestrates AI agents for zero-human companies; humans
operate the board through the React UI.
> - The board gates company access via `CompanyProvider`
(CompanyContext) and onboards new humans through the invite landing page
at `/invite/:token`.
> - Reported symptom: opening an invite link and signing in works, but
the page then renders completely blank (black in dark mode).
> - End-to-end browser testing reproduced a client-side crash:
`companiesQuery.data?.some is not a function` and `Cannot read
properties of undefined (reading 'filter')`.
> - Root cause: `CompanyProvider` and `InviteLandingPage` both use the
React Query key `["companies"]` but return **different shapes** — `{
companies, unauthorized }` vs a bare `Company[]` — so they silently
corrupt the shared cache entry; whichever component reads the other's
shape calls `.some()`/`.filter()` on the wrong type and throws,
unmounting the tree.
> - Owners never hit it (they never mount the invite page); only
invitees landing on `/invite/:token` crash.
> - This PR unifies the `["companies"]` query into a single shared
definition so the cache entry always has one shape and the two consumers
can't drift apart again.
> - The benefit is a working invite/onboarding flow and removal of a
whole class of cache-shape bugs on this key.
## What Changed
- Add `ui/src/api/companies-query.ts` exporting a single shared
`companiesListQueryOptions` (and `CompanyListResult`) — one `queryKey` +
one `queryFn` that always returns the wrapped `{ companies, unauthorized
}` shape, documented with the shared-cache contract.
- `ui/src/context/CompanyContext.tsx` now uses
`useQuery(companiesListQueryOptions)` instead of an inline copy of that
query.
- `ui/src/pages/InviteLanding.tsx` uses the same
`companiesListQueryOptions` (with its own `enabled` gate), reads
`companiesQuery.data?.companies` for the membership checks, and uses
`queryClient.fetchQuery(companiesListQueryOptions)` in the post-auth
path — so it reads and writes the identical shape.
## Verification
- `pnpm --filter @paperclipai/ui typecheck` — clean.
- `vitest run src/pages/InviteLanding.test.tsx
src/context/CompanyContext.test.tsx` — 17/17 pass, unchanged.
- Manual end-to-end via a real browser against a LAN-exposed
authenticated instance:
- Owner creates an Owner-role invite.
- New user opens the link and registers — **the "awaiting approval"
screen renders** (previously blank), `POST /api/invites/:token/accept`
returns `202`, no console errors.
- Owner approves at Company Settings → Access (`200`); invitee becomes
an active member.
- Invitee signs in — full board loads; smoke test of dashboard / issues
/ inbox / routines / goals / company settings — all render, zero
`pageerror`s.
- Before: invite page `#root` empty after sign-in (blank/black). After:
awaiting-approval panel renders. (Screenshots available on request.)
## Risks
- Low. `CompanyProvider`'s query behavior is unchanged (same `queryFn`
logic, just extracted into a shared module). `InviteLandingPage` now
reads the same shape it writes. No API, schema, or migration changes.
Existing tests pass unchanged.
## Model Used
- Claude (Anthropic), model ID `claude-opus-4-7` (Opus 4.7), 1M-context,
extended thinking + tool use; driving Claude Code with browser
automation for end-to-end reproduction and 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 run tests locally and they pass
- [ ] I have added or updated tests where applicable (existing
InviteLanding/CompanyContext tests cover the touched code and pass; a
cross-provider regression test that mounts both consumers is a sensible
follow-up)
- [ ] If this change affects the UI, I have included before/after
screenshots (described textually above; this is a crash/blank-page fix,
screenshots available on request)
- [x] I have considered and documented any risks above
- [x] I will address all Greptile and reviewer comments before
requesting merge
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
## Thinking Path
> - Paperclip orchestrates AI agents for zero-human companies.
> - Company creation is the first control-plane object operators create,
and the generated issue prefix becomes part of task identity.
> - The company service already retries when a generated issue prefix
collides with the `companies_issue_prefix_idx` unique constraint.
> - Drizzle 0.45.x wraps PostgreSQL errors in `DrizzleQueryError`,
leaving the real `23505` constraint error on the `.cause` chain.
> - The existing retry detector only inspected the top-level error, so
wrapped prefix collisions surfaced as 500s instead of retrying.
> - This pull request walks the error cause chain for the exact prefix
constraint and verifies the retry path against embedded Postgres.
> - The benefit is company creation no longer fails when generated
prefixes collide under Drizzle 0.45.x wrappers.
## What Changed
- Walk the error `.cause` chain when detecting
`companies_issue_prefix_idx` unique violations, with a cycle guard and
support for `constraint` / `constraint_name` fields.
- Added an embedded Postgres regression test that seeds `ARO`, creates
`Aron & Sharon`, and verifies the retry produces `AROA`.
- Stabilized existing async tests touched by full verification: instance
sidebar plugin rendering now waits for React Query results, and
Tailscale-unavailable CLI tests explicitly hide host `tailscale`
detection.
## Verification
- `pnpm --filter @paperclipai/server exec vitest run
src/__tests__/companies-service.test.ts`
- `pnpm --filter @paperclipai/server exec vitest run
src/__tests__/heartbeat-stale-queue-invalidation.test.ts`
- `pnpm --filter @paperclipai/ui exec vitest run
src/components/InstanceSidebar.test.tsx`
- `pnpm --filter paperclipai exec vitest run
src/__tests__/network-bind.test.ts src/__tests__/onboard.test.ts`
- `pnpm test:run`
- `pnpm -r typecheck`
- `pnpm build`
## Risks
- Low runtime risk: the retry behavior only expands detection for the
existing exact company issue-prefix unique constraint.
- The cause-chain walk is bounded by visited objects to avoid cycles.
- The sidebar and CLI changes are test-only stabilization and do not
change production 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, GPT-5 coding agent in Codex desktop, with local
shell/tool 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 run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] If this change affects the UI, I have included before/after
screenshots (N/A: no UI behavior change)
- [x] I have updated relevant documentation to reflect my changes (N/A:
bug fix with no user-facing docs change)
- [x] I have considered and documented any risks above
- [x] I will address all Greptile and reviewer comments before
requesting merge
Closes#6350
## Thinking Path
> - Paperclip is the board UI and control plane for supervising AI-agent
companies.
> - Operators repeatedly use mobile navigation, issue creation, inbox
scanning, and markdown reading surfaces.
> - Small layout and interaction rough edges add friction to those
high-frequency workflows.
> - The branch included a set of related board UI polish changes that
were too small to review as many separate PRs.
> - This pull request groups the remaining mobile/navigation/markdown
polish into one standalone branch.
> - The benefit is smoother board operation without mixing in unrelated
backend feature work.
## What Changed
- Tightened company settings navigation behavior on mobile.
- Fixed mobile new issue dialog height and moved issue priority into the
overflow controls on small screens.
- Restored browser controls for home-screen app mode.
- Fixed plugin-route sidebar selection on nested page loads.
- Added markdown preformatted-block wrapping controls and coverage.
- Kept updated issue list pages sorted by updated time in the board UI.
## Verification
- `pnpm --filter @paperclipai/plugin-sdk build`
- `NODE_ENV=test pnpm exec vitest run ui/src/components/Layout.test.tsx
ui/src/components/MarkdownBody.test.tsx
ui/src/components/MarkdownBody.wrap.test.tsx
ui/src/components/NewIssueDialog.test.tsx
ui/src/components/access/CompanySettingsNav.test.tsx
ui/src/lib/pwa-install-mode.test.ts ui/src/pages/Inbox.test.tsx`
The targeted UI tests passed. React emitted existing act-wrapping
warnings in a few test files, but there were no test failures.
## Risks
- Medium-low: changes span several UI surfaces, but they are mostly
layout/interaction polish with targeted component tests.
- Visual screenshots are not newly captured in this split PR; follow-up
review should include browser/visual QA before marking ready.
> 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 GPT-5 Codex via `codex_local`, tool-enabled coding session;
exact context window not exposed by this runtime.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] If this change affects the UI, I have included before/after
screenshots
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip is the control plane for AI-agent companies.
> - Operators need a path to move local company state toward Paperclip
Cloud without losing local-first control.
> - The Cloud Upstream flow needs API, persistence, CLI, and board UI
surfaces that agree on the same manifest/run model.
> - The existing branch had the feature work plus UX and error-handling
follow-ups.
> - This pull request packages the remaining Cloud Upstream sync work
into one standalone branch.
> - The benefit is an inspectable local-to-cloud sync workflow with
preview, conflicts, activation, and captured UX review states.
## What Changed
- Added Cloud Upstream shared types, server routes/services, and
persisted run schema/migration.
- Added Paperclip Cloud CLI sync helpers and local connection storage.
- Added the Cloud Upstream board UI, settings entry points, query keys,
and UX lab page.
- Added preview/activation checklist behavior, redirect handling,
manifest-only preview support, friendly errors, in-flight hints, and
entity count summaries.
## Verification
- `pnpm --filter @paperclipai/plugin-sdk build`
- `NODE_ENV=test pnpm exec vitest run cli/src/__tests__/cloud.test.ts
server/src/__tests__/instance-settings-routes.test.ts
server/src/__tests__/instance-settings-service.test.ts
ui/src/pages/CloudUpstream.test.tsx
ui/src/components/CompanySettingsSidebar.test.tsx`
- `NODE_ENV=test pnpm exec vitest run
server/src/__tests__/cloud-upstreams.test.ts`
Worktree setup note: the isolated worktree install skipped native sqlite
build scripts, so I copied the already-built local sqlite binding from
the main checkout before running
`server/src/__tests__/cloud-upstreams.test.ts`. The test then passed.
## Risks
- Medium: this adds a database migration and a broad feature path across
CLI/server/UI.
- Merge order: this is the only PR in this split with a DB migration;
merge it before any future Cloud Upstream migration follow-up.
- Mitigation: the PR is based directly on current `origin/master`, has
targeted route/service/UI tests, and keeps the feature behind existing
experimental Cloud Sync settings.
> 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 GPT-5 Codex via `codex_local`, tool-enabled coding session;
exact context window not exposed by this runtime.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] If this change affects the UI, screenshot artifacts are
intentionally omitted per reviewer request
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] I will address all Greptile and reviewer comments before
requesting merge
## Thinking Path
> - Paperclip orchestrates AI agents for zero-human companies by keeping
task ownership, approvals, and operator control inside one control
plane.
> - Agent permissions and plugin-hosted company settings sit on the
boundary between autonomy and governance.
> - V1 needs scoped task assignment rules, plugin extension points, and
clearer company access surfaces without weakening company boundaries.
> - The branch builds the core authorization service, plugin SDK/host
APIs, and UI simplifications needed to support those controls.
> - Paperclip EE plugin surfaces were intentionally moved out of this
core PR per review direction, so this PR now carries only the public
core/plugin infrastructure work.
> - The latest updates preserve the PAP-9937 branch changes that belong
in this PR, remove the `design/` artifacts, and exclude the experimental
`plugin-briefs` package.
> - Greptile feedback was applied through the authorization/audit paths
and the final cleanup commit was re-reviewed at 5/5 with no unresolved
Greptile threads.
> - The benefit is safer assignment control with extension hooks for
richer permission products while preserving simple defaults for normal
operators.
## What Changed
- Added scoped task-assignment authorization decisions and routed
issue/agent assignment mutations through the authorization service.
- Added plugin SDK and host APIs for company settings slots,
authorization policy/grant management, assignment previews, and bridge
invocation scope propagation.
- Simplified core company access UI and moved advanced controls behind
plugin-provided settings surfaces.
- Added retry-now affordances for blocked issue next-step notices.
- Added protected-assignment enforcement for persisted
agent/project/issue policies, including explicit-grant fallback
behavior.
- Added incremental principal-access compatibility backfill for active
agent memberships and role-default human permission grants.
- Added the Markdown code block wrap action fix from the latest branch
changes.
- Removed `design/` artifacts from the PR and removed
`packages/plugins/plugin-briefs` from the final diff.
- Addressed Greptile feedback for plugin actor sanitization, legacy
membership handling, audit pagination, unknown grant-scope metadata, and
startup test mocks.
## Verification
- `pnpm exec vitest run server/src/__tests__/access-service.test.ts
server/src/__tests__/company-portability.test.ts` -> 2 files passed, 54
tests passed.
- `pnpm exec vitest run
server/src/__tests__/server-startup-feedback-export.test.ts
server/src/__tests__/access-service.test.ts
server/src/__tests__/company-portability.test.ts` -> 3 files passed, 62
tests passed.
- `pnpm exec vitest run
server/src/__tests__/authorization-service.test.ts
server/src/__tests__/plugin-access-authorization-host-services.test.ts
server/src/__tests__/server-startup-feedback-export.test.ts` -> 3 files
passed, 28 tests passed.
- `pnpm --filter @paperclipai/server typecheck` -> passed.
- `git diff --check` -> passed.
- `node ./scripts/check-docker-deps-stage.mjs` -> passed.
- `CI=true pnpm install --frozen-lockfile --ignore-scripts` -> passed
with no lockfile update.
- `pnpm exec vitest run
ui/src/components/MarkdownBody.interaction.test.tsx` -> 1 test passed.
- `git ls-files design packages/plugins/plugin-briefs | wc -l` -> 0.
- GitHub CI on `40cd83b53` -> all checks passed, merge state `CLEAN`.
- Greptile on `40cd83b53` -> 5/5, 102 files reviewed, 0
comments/annotations added, 0 unresolved review threads.
- Confirmed the PR diff contains no `design/`,
`packages/plugins/plugin-briefs`, `pnpm-lock.yaml`, or
`.github/workflows` changes.
## Risks
- Medium: task assignment authorization paths are behaviorally stricter
for protected/private policy data, so existing plugin-authored policies
may block assignment until explicit grants or approval flows are
configured.
- Medium: plugin-host authorization APIs expand the surface area
available to trusted plugins and need careful review for company
scoping.
- Low: startup now performs a principal-access compatibility backfill,
but the migration and runtime backfill use conflict-tolerant inserts.
> 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 coding agent, tool-enabled workflow with shell,
git, and GitHub CLI access.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] If this change affects the UI, I have included before/after
screenshots
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip orchestrates AI agents through a server-side control plane
> - That control plane depends on reliable issue state transitions,
plugin lifecycle behavior, import limits, and startup/shutdown handling
> - Several small runtime fixes had accumulated on the working branch
and were mixed with larger feature work
> - Keeping them separate makes the correctness fixes reviewable and
mergeable without waiting for cloud-sync UI work
> - This pull request groups the server/runtime control-plane fixes into
one standalone branch
> - The benefit is a tighter, safer runtime baseline for retries,
imports, plugin migrations, feedback flushing, and trusted cloud import
handling
## What Changed
- Fixed updated issue list pagination sorting and scheduled retry
comment handling.
- Re-applied pending plugin migrations during hot reload and fixed
plugin-schema worktree seed restore.
- Hardened public tenant DB startup, portable import body limits,
trusted cloud import errors, and trusted cloud tenant import mutation
access.
- Expired stale request confirmations after user comments.
- Added feedback export shutdown hardening so database-unavailable flush
loops stop cleanly.
- Guarded plugin worker `error` event emission when no listener is
registered.
## Verification
- `pnpm install --frozen-lockfile --ignore-scripts`
- `pnpm --filter @paperclipai/plugin-sdk build`
- `npm run install --prefix
node_modules/.pnpm/sqlite3@5.1.7/node_modules/sqlite3`
- `pnpm exec vitest run server/src/__tests__/issues-service.test.ts
server/src/__tests__/plugin-lifecycle-restart.test.ts
server/src/__tests__/server-startup-feedback-export.test.ts
server/src/__tests__/issue-comment-reopen-routes.test.ts
server/src/__tests__/issue-thread-interactions-service.test.ts
server/src/__tests__/issue-thread-interaction-routes.test.ts
server/src/__tests__/body-limits.test.ts
server/src/__tests__/feedback-flush-controller.test.ts
server/src/__tests__/error-handler.test.ts
server/src/__tests__/board-mutation-guard.test.ts
packages/db/src/backup-lib.test.ts` initially exposed local setup issues
and two 5s test timeouts.
- Rerun after local prereq build: `pnpm exec vitest run --testTimeout
15000 server/src/__tests__/issue-comment-reopen-routes.test.ts
server/src/__tests__/issue-thread-interaction-routes.test.ts
server/src/__tests__/feedback-flush-controller.test.ts
server/src/__tests__/server-startup-feedback-export.test.ts` passed.
- Some embedded Postgres-backed tests skipped on this host because local
Postgres init was unavailable.
## Risks
- Runtime-touching branch: startup/shutdown and issue interaction
behavior should be reviewed carefully.
- The feedback export change disables repeated flush attempts only for
database connection-refused failures; other upload failures still log
normally.
- The plugin worker error guard avoids process crashes from unhandled
EventEmitter errors but may hide errors from code paths that expected an
emitted listener.
> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.
## Model Used
- OpenAI Codex, GPT-5-based coding agent with local shell/git/tool use.
Exact hosted model ID and context-window size are not exposed by the
local Paperclip adapter runtime.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] If this change affects the UI, I have included before/after
screenshots
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip operators spend most of their time scanning the board,
inbox, sidebar, and local dev status surfaces
> - Small UI and dev-ops frictions make repeated operator workflows feel
slower than they need to be
> - The working branch contained several independent quality-of-life
improvements mixed with larger cloud work
> - Grouping these smaller UI/dev-ops changes together keeps review
overhead reasonable without merging them into feature PRs
> - This pull request collects the operator-facing QoL polish into one
standalone branch
> - The benefit is a cleaner board navigation and local dev recovery
experience without depending on cloud upstream sync
## What Changed
- Relaxed forced 44px touch targets for small inline widgets.
- Fixed mobile mention menu scrolling and sidebar spacing on
touch/mobile layouts.
- Synced inbox hover state with j/k selection.
- Moved plugin sidebar entries into the Work section.
- Added manual dev-server restart action/banner behavior.
- Logged plugin bridge 502 causes for better diagnosis.
## Verification
- `pnpm install --frozen-lockfile --ignore-scripts`
- `pnpm --filter @paperclipai/plugin-sdk build`
- `pnpm exec vitest run ui/src/components/MarkdownEditor.test.tsx
ui/src/components/Sidebar.test.tsx
ui/src/components/SidebarProjects.test.tsx ui/src/pages/Inbox.test.tsx
ui/src/components/DevRestartBanner.test.tsx
server/src/__tests__/dev-server-status.test.ts
server/src/__tests__/health-dev-server-token.test.ts
server/src/__tests__/plugin-routes-authz.test.ts` initially failed only
because plugin SDK `dist` was not built in the fresh worktree.
- Rerun after build: `pnpm exec vitest run
server/src/__tests__/plugin-routes-authz.test.ts` passed.
- The remaining targeted UI/dev-server tests passed on the first
post-install run.
## Visual Evidence
- Sidebar layout and plugin Work section: 
- Inbox/task row selection and hover-state surface: 
- Dev restart banner desktop: 
- Dev restart banner mobile: 
## Risks
- Mostly UI/dev ergonomics with low data risk.
- Sidebar and inbox changes touch frequently used navigation surfaces,
so visual review on desktop/mobile is still useful.
> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.
## Model Used
- OpenAI Codex, GPT-5-based coding agent with local shell/git/tool use.
Exact hosted model ID and context-window size are not exposed by the
local Paperclip adapter runtime.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] If this change affects the UI, I have included before/after
screenshots
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip orchestrates AI agents that need scoped, auditable access
to secrets
> - Hosted and external deployments need provider vault configuration
without exposing secret values in Paperclip metadata
> - AWS Secrets Manager vault setup previously required too much manual
operator knowledge
> - Provider vault discovery and removal belong together as an
independent secrets-management improvement
> - This pull request adds AWS provider vault discovery/prefill plus
vault removal flows
> - The benefit is a safer operator path for configuring external secret
storage before higher-level cloud workflows depend on it
## What Changed
- Added shared validators/types for AWS provider vault discovery
payloads and safe provider metadata.
- Implemented AWS provider vault discovery preview on the server.
- Added provider vault removal service/route behavior.
- Added Secrets page UI for discovery prefill, removal messaging, and
related rendering coverage.
- Added Storybook provider-vault fixtures and captured screenshots for
the new UX states.
## Verification
- `pnpm install --frozen-lockfile --ignore-scripts`
- `pnpm exec vitest run packages/shared/src/validators/secret.test.ts
server/src/__tests__/aws-secrets-manager-provider.test.ts
server/src/__tests__/secrets-routes.test.ts
server/src/__tests__/secrets-service.test.ts
ui/src/pages/Secrets.render.test.tsx`
- Result: 4 files passed, 1 embedded Postgres-backed file skipped on
this host because local Postgres init was unavailable.
- `pnpm --filter @paperclipai/ui exec vitest run
src/pages/Secrets.render.test.tsx`
- `pnpm --filter @paperclipai/ui typecheck`
- Storybook screenshot capture against `Product/Secrets` on
`http://127.0.0.1:60381/iframe.html?id=product-secrets--secrets-inventory&viewMode=story&globals=theme:dark`
## Screenshots
Provider vaults tab after this change:

AWS discovery candidate flow:

Provider vault removal confirmation:

## Risks
- Secret provider metadata handling must remain non-sensitive;
validators reject credential-bearing Vault URLs and sensitive AWS
discovery keys.
- AWS discovery depends on deployment credentials being configured
correctly outside Paperclip-managed company secrets.
> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.
## Model Used
- OpenAI Codex, GPT-5-based coding agent with local shell/git/tool use.
Exact hosted model ID and context-window size are not exposed by the
local Paperclip adapter runtime.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] If this change affects the UI, I have included before/after
screenshots
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip is the control plane that coordinates AI-agent work
through issues, heartbeats, comments, approvals, and auditable recovery
paths.
> - The affected subsystem is heartbeat/recovery orchestration,
especially the optional cheap model profile used for operational
recovery overhead.
> - Cheap recovery should repair status and liveness, but it must not
become the worker lane that writes deliverables, continues source work,
or propagates cheap execution hints into downstream retries.
> - The gap was that cheap-profile hints could follow recovery wake
contexts and assignment overrides farther than intended, making real
work eligible to run on the cheap model.
> - This pull request separates status-only cheap recovery from normal
source-work continuations, adds route guards for deliverable mutations
during cheap status-only runs, and documents the invariant.
> - The benefit is safer retry/recovery behavior: cheap runs can clean
up control-plane state, while any remaining source work resumes through
a normal/original model path.
## What Changed
- Added recovery model-profile work classes so status-only recovery
carries explicit guard context and normal-model continuations scrub
cheap hints.
- Updated heartbeat, productivity review, liveness continuation, and
recovery service wakeups to request cheap only for bounded status-only
recovery work.
- Blocked cheap status-only recovery runs from writing issue documents,
plans, attachments, work products, or assigning downstream work back to
`modelProfile: "cheap"`.
- Added/updated server tests for cheap profile propagation,
artifact/document guards, route authorization, retry scheduling, and
successful-run handoff behavior.
- Documented the recovery model-profile lane in
`doc/SPEC-implementation.md` and `doc/execution-semantics.md`.
- After rebasing onto current `public-gh/master`, stabilized the new
`InstanceSidebar` plugin-filter tests so the PR check lane stays green.
## Verification
- Local: `pnpm exec vitest run --config vitest.config.ts
src/services/recovery/model-profile-hint.test.ts
src/__tests__/issue-agent-mutation-ownership-routes.test.ts
src/__tests__/issue-document-restore-routes.test.ts` from `server/` - 3
files, 37 tests passed after final edits.
- Local: `pnpm exec vitest run --config vitest.config.ts
src/__tests__/heartbeat-process-recovery.test.ts` from `server/` - 44
tests passed after rerunning the cleanup-sensitive file alone.
- Local: `pnpm --filter @paperclipai/ui exec vitest run
src/components/InstanceSidebar.test.tsx` - 4 tests passed.
- Local: `pnpm --filter @paperclipai/server typecheck` - passed.
- Local: `pnpm --filter @paperclipai/ui typecheck` - passed.
- PR checks on latest head `6f8c3b1380f5bd872c6f49f6f7188ecf3bb6d263` -
all green, including `verify`, build, typecheck,
server/general/serialized tests, e2e, Snyk, and policy.
- Greptile: pass 3 returned Confidence Score 5/5 with zero unresolved
Greptile review threads.
## Risks
- Medium risk: recovery behavior is intentionally stricter, so any path
that incorrectly relies on cheap recovery to keep doing source work will
now need to hand back to a normal-model run.
- Low migration risk: no schema changes.
- No product UI changes; the UI file touched is a test-only
stabilization after rebasing onto current `master`.
> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.
## Model Used
- OpenAI Codex coding agent, GPT-5 model family (`gpt-5`), tool use and
local code execution enabled; context window 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 run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] If this change affects the UI, I have included before/after
screenshots (N/A: no product UI changes)
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] I will address all Greptile and reviewer comments before
requesting merge
## Thinking Path
> - Paperclip orchestrates AI agents for zero-human companies
> - Plugins extend Paperclip with new capabilities; the Instance
Settings sidebar exposes per-plugin settings pages under a "Plugins"
group
> - Some plugins contribute only sandbox-provider drivers (E2B, exe.dev,
Modal). They have no per-plugin settings UI — `PluginSettings` already
redirects sandbox-provider-only plugins to the Environments page
> - As a result, listing them as their own sidebar rows produced
confusing entries that visually nested below the "Adapters" group and
only lead to a stub redirect — there was no value to the user
> - This pull request hides sandbox-provider-only plugins from the
Instance Settings sidebar, and reorders the indented plugin list so it
sits directly under the "Plugins" group it actually belongs to
> - The benefit is a cleaner sidebar that only surfaces plugins with
real per-plugin settings, and removes the visual mis-nesting under
Adapters
## What Changed
- `ui/src/components/InstanceSidebar.tsx`: filter out plugins whose only
contribution is `sandboxProviders` (hybrid plugins that contribute
sandbox providers *plus* something else still get a sidebar entry). Move
the indented plugin list so it renders between the "Plugins" row and the
"Adapters" row instead of after Adapters.
- `ui/src/components/InstanceSidebar.test.tsx`: new test file with 4
cases — sandbox-only plugins hidden, hybrid plugins shown, ordering
(plugin list appears under Plugins and before Adapters), and the
existing non-plugin sidebar items still render.
## Verification
- `pnpm -C ui vitest run src/components/InstanceSidebar.test.tsx` → 4/4
pass.
- `pnpm typecheck` clean on the changed files.
- Manual: visit `/instance/settings/plugins` — "E2B Sandbox Provider"
and "exe.dev Sandbox Provider" rows no longer appear in the sidebar;
remaining plugins are listed directly under the "Plugins" group, not
below Adapters.
**Before:** see the screenshot embedded in the linked issue (`PAPA-375`)
— sandbox-provider plugins show as sidebar rows visually nested under
"Adapters".
**After:** sandbox-provider-only rows are gone; plugin list sits
directly under the "Plugins" group. (A live runtime screenshot was not
captured for this PR because the local server requires an authenticated
browser session not currently available to the agent — happy to add one
on request.)
## Risks
- Low risk. Pure UI filter + reorder, scoped to `InstanceSidebar.tsx`.
No backend or plugin-loader changes. Hybrid plugins that legitimately
need a settings entry are explicitly preserved by the filter. Covered by
4 new unit tests.
## Model Used
- Claude Opus 4.7 (`claude-opus-4-7`), Anthropic, via Claude Code in a
Paperclip heartbeat. Standard tool-use mode (no extended thinking). 200K
context.
## 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 run tests locally and they pass
- [x] I have added or updated tests where applicable
- [ ] If this change affects the UI, I have included before/after
screenshots — before is in the linked issue; after pending live capture
(see Verification)
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] I will address all Greptile and reviewer comments before
requesting merge
Closes PAPA-375.
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip orchestrates AI agents for zero-human companies
> - Plugin authors expose configuration via JSON schemas, including
secret fields marked `format: "secret-ref"`
> - At the same time, Paperclip already has a first-class secrets store,
and `SecretBindingPicker` is the canonical UI for binding to one of
those stored secrets
> - But `JsonSchemaForm`'s `SecretField` rendered only a plain password
input, so configuring an E2B (or Modal / Cloudflare / Daytona) sandbox
required leaving the form, copying a secret UUID, and pasting it back
> - This pull request wires `SecretBindingPicker` into `SecretField` so
every plugin secret-ref field gets the picker plus an optional raw-value
fallback
> - The benefit is that secret reuse becomes one click instead of a tab
switch, and the raw-paste path still works for one-off keys or long
SSH-style secrets
## What Changed
- `ui/src/components/JsonSchemaForm.tsx` `SecretField` now renders
`SecretBindingPicker` above the existing password/textarea input.
UUID-shaped values are treated as bound refs (no raw input shown).
Non-UUID values keep the password/textarea visible (auto-opened) for SSH
keys and other long secrets. Empty fields show the picker plus a small
"Or paste a raw value" toggle.
- Selecting a secret writes the secret UUID to the form value — the
server-side resolution in `server/src/services/environment-config.ts`
(`resolveConfigSecretRefsForRuntime` / `collectEnvironmentSecretRefs`)
is unchanged. The version selector on the picker is suppressed
(`allowVersionSelector={false}`) because plugin secret refs always
resolve at `"latest"`.
- `ui/src/components/JsonSchemaForm.test.tsx` mocks the picker (which
requires `CompanyContext` + `QueryClient` providers) and adds coverage
for: picker render, UUID-bound state hides the raw input, picker
selection writes the UUID through `onChange`, raw text keeps the
password fallback. The original multiline (SSH key) case still asserts a
textarea + no password input.
## Verification
- `pnpm --filter @paperclipai/ui test
src/components/JsonSchemaForm.test.tsx` → 4/4 passing
- `pnpm --filter @paperclipai/ui test src/pages/PluginSettings.test.tsx`
→ 5/5 passing (existing consumer of `JsonSchemaForm`)
- `pnpm --filter @paperclipai/ui exec tsc --noEmit` → clean
- Manual: in the company Environments page, edit an environment with a
sandbox driver that exposes a `secret-ref` field (e.g., E2B `apiKey`).
The field should render the secret dropdown above the raw-value toggle;
selecting an active secret persists its UUID, and saving the form
continues to resolve the secret at runtime.
Before/after screenshots: deferred — change was validated by
[@devinfoley](https://github.com/devinfoley) on the main Paperclip
instance before this PR was opened. Happy to add screenshots if a
reviewer wants them.
## Risks
- Low risk. The change is additive in the SecretField: the raw-value
password/textarea path is preserved and auto-opens whenever the stored
value is not a UUID, so existing SSH-key entries and unsaved raw values
are untouched.
- The new heuristic is "if `value` is a UUID, treat it as a bound
secret". A user who somehow pasted a UUID as a literal value (not as a
secret ref) would now see it rendered as a bound (possibly missing)
secret in the picker. The previous UI already treated UUID values as
opaque secret refs at save time (server converts UUIDs straight
through), so the runtime behavior is unchanged.
- Picker pulls company secrets via the existing `secretsApi.list` query.
No new endpoints, no migrations.
## Model Used
- Provider: Anthropic
- Model: Claude Opus 4.7 (`claude-opus-4-7`)
- Capabilities: tool use, extended reasoning
- Surfaced through: Claude Code via Paperclip heartbeat (issue PAPA-377)
## 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 run tests locally and they pass
- [x] I have added or updated tests where applicable
- [ ] If this change affects the UI, I have included before/after
screenshots — deferred; user validated locally before opening the PR.
Will add if requested.
- [x] I have updated relevant documentation to reflect my changes (no
docs needed — internal behavior of an existing form field)
- [x] I have considered and documented any risks above
- [x] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip orchestrates AI agents for zero-human companies.
> - Human operators create and edit issues through modal-heavy board UI
workflows.
> - The new-issue dialog embeds markdown editors that render
autocomplete menus through body-level portals.
> - Radix Dialog treats those portal clicks as outside-dialog pointer
events and prevents their default behavior.
> - That prevention made completion items hard or impossible to select
from inside the new-issue dialog.
> - This pull request marks the markdown editor floating autocomplete
menu as allowed dialog-external UI and extends the dialog
outside-pointer handler to preserve those interactions.
> - The benefit is that users can click/tap autocomplete completions
while keeping the existing modal behavior intact.
## What Changed
- Added a stable `data-paperclip-floating-ui` marker and explicit
pointer event handling to the markdown editor mention/autocomplete
portal.
- Updated the new issue dialog outside-pointer guard so editor
autocomplete portals are handled like Radix popover portals.
- Added regression coverage for markdown editor portal markup and new
issue dialog completion selection behavior.
## Verification
- `pnpm exec vitest run ui/src/components/MarkdownEditor.test.tsx
ui/src/components/NewIssueDialog.test.tsx` passed: 2 files, 38 tests.
- Confirmed the branch is rebased onto current `public-gh/master` before
opening this PR.
- Confirmed the diff does not include `pnpm-lock.yaml` or
`.github/workflows` changes.
## Risks
- Low risk. The change is scoped to allowing pointer events from known
body-level UI portals while keeping other outside-dialog pointer events
under Radix Dialog control.
> 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 coding agent with repository tool use and local
command execution. Exact hosted context window is not surfaced in this
runtime.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] If this change affects the UI, I have included before/after
screenshots
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] I will address all Greptile and reviewer comments before
requesting merge
Screenshot note: this is an interaction/event-handling fix with no
visible UI change; verification is covered by the focused regression
tests above.
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Summary
- Removes the amber "Planning" pill from inbox / issue-list rows in
`IssueRow`
- Updates the focused IssueRow test to assert the badge is no longer
rendered
- Per [PAP-9691](https://paperclip.ing/PAP/issues/PAP-9691): user just
doesn't want to see the badge in list rows
The underlying `issue.workMode === "planning"` data, the issue detail
composer toggle, and the server/plugin/heartbeat work-mode contract
introduced in #5353 are all untouched. Planning mode still functions;
the list-row indicator is just gone.
## Test plan
- [x] `pnpm exec vitest run --project @paperclipai/ui
ui/src/components/IssueRow.test.tsx` (11 passed)
- [ ] Visual: open `/PAP/inbox` with a planning-mode issue assigned and
confirm no amber Planning pill on the row
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
## Thinking Path
> - Paperclip orchestrates AI agents for zero-human companies.
> - Operators need to inspect what agents changed inside execution and
project workspaces.
> - The existing workspace detail views did not provide a first-party
rich diff surface for staged, unstaged, head, renamed, binary,
oversized, and untracked changes.
> - The plugin system is the intended extension point for optional rich
UI surfaces.
> - This pull request adds a workspace diff plugin plus host services
and shared contracts so Changes tabs can render workspace diffs through
plugin slots.
> - The diff-renderer dependency should stay owned by the plugin package
rather than the core UI app.
> - The dependency surface must stay aligned with repository PR policy,
including intentionally omitting `pnpm-lock.yaml` from the PR.
> - The benefit is a more reviewable workspace surface without
hard-coding the renderer into every page.
## What Changed
- Added `@paperclipai/plugin-workspace-diff`, including diff
normalization, plugin manifest/worker/UI entrypoints, and focused plugin
tests.
- Kept `@pierre/diffs` scoped to `@paperclipai/plugin-workspace-diff`;
removed the core UI lab diff-renderer surface and direct UI package
dependency.
- Added shared workspace diff types and validators, plus plugin SDK
surface for workspace diff host services.
- Added server workspace diff service support and route coverage for
execution/project workspace diff flows.
- Wired Execution Workspace and Project Workspace Changes tabs to load
the diff plugin, including loading/error fallback behavior.
- Added UI tests and fixtures for the Changes tabs and plugin bridge
behavior.
- Added the new plugin package manifest to the Docker deps stage so PR
policy can validate dependency coverage.
- Addressed review hardening around empty untracked patches, workspace
path exposure, project workspace read capability checks, and default
base refs.
## Verification
- `pnpm --filter @paperclipai/plugin-workspace-diff test`
- `pnpm exec vitest run
packages/shared/src/validators/workspace-diff.test.ts
server/src/__tests__/workspace-diff-service.test.ts
ui/src/pages/ProjectWorkspaceDetail.test.tsx
ui/src/pages/ExecutionWorkspaceDetail.test.tsx`
- `pnpm exec vitest run ui/src/plugins/bridge.test.ts
server/src/__tests__/workspace-runtime-routes-authz.test.ts`
- `pnpm --filter @paperclipai/shared typecheck`
- `pnpm --filter @paperclipai/plugin-workspace-diff typecheck`
- `pnpm --filter @paperclipai/server typecheck`
- `pnpm --filter @paperclipai/ui typecheck`
- `node ./scripts/check-docker-deps-stage.mjs`
- Browser screenshot captured from the local worktree dev server:
https://files.catbox.moe/ofdpsp.png
- Confirmed branch is rebased onto `public-gh/master`,
`.github/workflows/pr.yml` is not included in the PR diff,
`ui/package.json` is not included in the PR diff, and `pnpm-lock.yaml`
is not included in the PR diff.
## Risks
- Medium UI integration risk: the Changes tab depends on the plugin slot
and host diff service path.
- Medium dependency risk: this adds `@pierre/diffs` in the plugin
package, but `pnpm-lock.yaml` is intentionally omitted per packaging
instructions because repository automation manages lockfile updates.
- Current CI blocker: downstream frozen installs fail until the
repository policy path for new plugin package dependencies is chosen.
- Diff rendering edge cases are covered for common working-tree and head
diff states, but very large repositories may still expose performance
limits.
- No migrations are included.
> 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 class coding model, tool-enabled local execution
environment. Exact context window was not exposed by the runtime.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] If this change affects the UI, I have included before/after
screenshots
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip orchestrates AI agents for zero-human companies
> - Operators manage per-company secrets through the Secrets page in the
web UI
> - A long secret value pasted into the "New secret" textarea blew out
the form's width, which pushed the Create/Cancel buttons off-screen and
made the form unusable
> - Root cause: the shadcn `Textarea` primitive sets `w-full` but does
not constrain `min-width`, so a flex parent honors the textarea's
intrinsic content width when a long unbreakable string is present
> - This pull request adds `min-w-0 max-w-full` to the shared `Textarea`
primitive and `min-w-0 overflow-x-hidden break-all` on the secret-value
usage so a long token wraps inside the form bounds
> - The benefit is the Create/Cancel buttons stay reachable regardless
of pasted token length, and every other `Textarea` consumer also gets
the flexbox-friendly width constraint
## What Changed
- `ui/src/components/ui/textarea.tsx`: added `min-w-0 max-w-full` to the
base shadcn `Textarea` so it cannot exceed its flex parent
- `ui/src/pages/Secrets.tsx`: added `min-w-0 overflow-x-hidden
break-all` on the new-secret value `Textarea` so long opaque tokens wrap
instead of pushing the form
- `ui/src/pages/Secrets.render.test.tsx`: new regression test that opens
the New Secret dialog and asserts the value textarea carries the
width-constraint classes
## Verification
- `cd ui && npx vitest run src/pages/Secrets.render.test.tsx` — 3/3 pass
- Manual: open the Secrets page, click "New secret", paste a long
unbroken string (e.g. a 500-char token) into the value field. The form
stays within its dialog and the Create/Cancel buttons remain in view.
Before:
<img width="1772" height="1432" alt="image"
src="https://github.com/user-attachments/assets/cb31a290-f82a-41dc-9346-91d18cbb5911"
/>
After:
<img width="672" height="734" alt="Screenshot 2026-05-17 at 5 39 38 PM"
src="https://github.com/user-attachments/assets/a08800c2-b09b-43be-b0e8-114d9149b8f5"
/>
After: the value field wraps with `break-all` inside the dialog;
Create/Cancel stay clickable. Covered by the new render test which
asserts `min-w-0`, `overflow-x-hidden`, and `break-all` are present on
`#new-secret-value`.
## Risks
- Low risk. The base `Textarea` change adds `min-w-0 max-w-full`, which
only affects layouts where a textarea was previously allowed to grow
past its parent — those cases were already buggy. `break-all` on the
secret-value textarea is the right behavior for opaque tokens; it would
be wrong for prose, but this field is explicitly a secret token.
## Model Used
- Provider: Anthropic Claude
- Model: claude-opus-4-7 (Opus 4.7)
- Mode: standard Claude Code agent, tool use 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 run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] If this change affects the UI, I have included before/after
screenshots
- [ ] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] I will address all Greptile and reviewer comments before
requesting merge
## Thinking Path
> - Paperclip is the control plane for autonomous AI companies, so agent
work needs visible ownership, recovery, and operator controls.
> - This local branch had accumulated several related control-plane
reliability and operator-experience fixes across recovery actions,
watchdog folding, model-profile defaults, mentions, markdown editing,
plugin launchers, and small UI polish.
> - The branch needed to be converted into a PR against the current
`origin/master` without losing dirty work or including lockfile/workflow
churn.
> - The safest standalone shape is a single rollup PR because the
recovery/server/UI files overlap heavily across the local commits and
splitting would create avoidable conflicts.
> - This pull request replays the local branch onto latest
`origin/master`, preserves the uncommitted work as logical commits, and
adds a Zod 4 validator compatibility fix found during verification.
> - The benefit is that the May 17 local branch can be reviewed and
merged as one coherent, conflict-free branch under the 100-file Greptile
limit.
## What Changed
- Rebased the local May 17 branch work onto current `origin/master` in a
dedicated worktree.
- Preserved and committed previously dirty changes for recovery retry
handling, plugin/sidebar launcher polish, and `.herenow` ignores.
- Added recovery-action behavior for returning source issues to `todo`
when retrying source-scoped recovery.
- Included the existing local recovery/liveness/watchdog fold, Codex
cheap-profile, markdown/mention, duplicate-agent, and UI polish commits
from the branch.
- Normalized shared validator `z.record(...)` schemas to explicit
string-key records for Zod 4 compatibility.
- Confirmed the PR has no `pnpm-lock.yaml` or `.github/workflows/*`
changes and stays below the 100-file Greptile limit.
## Verification
- `pnpm install --frozen-lockfile --ignore-scripts`
- `npm run install` in
`node_modules/.pnpm/sqlite3@5.1.7/node_modules/sqlite3` to build the
local native sqlite3 binding after installing with scripts disabled
- `pnpm exec vitest run packages/shared/src/validators/issue.test.ts
packages/shared/src/project-mentions.test.ts
packages/adapter-utils/src/server-utils.test.ts
server/src/__tests__/heartbeat-model-profile.test.ts
server/src/__tests__/issue-recovery-actions.test.ts
server/src/__tests__/issue-agent-mutation-ownership-routes.test.ts
server/src/__tests__/heartbeat-active-run-output-watchdog.test.ts
server/src/__tests__/plugin-local-folders.test.ts
ui/src/components/IssueRecoveryActionCard.test.tsx
ui/src/components/Sidebar.test.tsx
ui/src/components/SidebarAccountMenu.test.tsx
ui/src/components/IssueProperties.test.tsx
ui/src/components/MarkdownEditor.test.tsx
ui/src/components/MarkdownBody.test.tsx
ui/src/lib/duplicate-agent-payload.test.ts
ui/src/pages/Routines.test.tsx`
- First pass: 13 files passed with 201 passing tests; 3 server files
failed before sqlite3 native binding was built.
- After rebuilding sqlite3:
`server/src/__tests__/heartbeat-model-profile.test.ts`,
`server/src/__tests__/issue-recovery-actions.test.ts`, and
`server/src/__tests__/heartbeat-active-run-output-watchdog.test.ts`
passed/loaded; embedded Postgres tests were skipped by the local host
guard.
- `pnpm --filter @paperclipai/shared typecheck`
- `pnpm --filter @paperclipai/adapter-utils typecheck`
- `pnpm --filter @paperclipai/server typecheck`
- `pnpm --filter @paperclipai/ui typecheck`
## Risks
- Medium risk: this is a broad rollup PR across recovery semantics,
server tests, shared validators, and UI surfaces.
- Some embedded Postgres tests skipped locally due the host guard, so CI
should provide the stronger database-backed signal.
- UI changes were covered by component tests, but no browser screenshot
was captured in this PR creation pass.
- This branch may overlap with existing recovery/liveness PR work; merge
this PR independently or restack/close overlapping branches rather than
merging duplicate implementations together.
> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.
## Model Used
- OpenAI Codex, GPT-5-based coding agent, tool-enabled local repository
and GitHub workflow, medium reasoning effort.
## 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 run tests locally and they pass
- [x] I have added or updated tests where applicable
- [ ] If this change affects the UI, I have included before/after
screenshots
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip orchestrates AI agents for zero-human companies.
> - Scheduled routines are the control-plane path for recurring agent
work.
> - Routines already had dispatch/history, but their runtime environment
did not carry routine-owned secret bindings through execution.
> - Operators need routine-specific secrets that can override
project/agent env without exposing secret values in history, logs, or
access events.
> - This pull request adds the routine env runtime contract, wires it
into execution, and makes the routine UI/history surfaces show safe
secret metadata.
> - The benefit is that routine executions can use scoped secret refs
predictably while preserving company boundaries and auditability.
## What Changed
- Added routine env persistence/runtime support, including
`routines.env`, `routine_runs.routine_revision_id`, revision snapshots,
and idempotent migration `0086_routine_env_runtime_contract`.
- Resolved routine env during heartbeat adapter config assembly with
precedence `agent < project < routine` and secret access events recorded
against the routine consumer.
- Added secret binding synchronization for routine create/update/restore
flows and guarded cross-company, missing, disabled, and deleted secret
cases.
- Added a Secrets tab to routine detail, env/secret history diff
rendering, and Storybook coverage for the new UI states.
- Added server/UI regression tests, including an embedded-Postgres QA
path for routine secret execution and restore behavior.
- Updated implementation/database docs for routine env and
secret-binding behavior.
## Verification
- `pnpm install --frozen-lockfile` after rebasing onto
`public-gh/master` to refresh workspace links for the newly-added
upstream Grok adapter package.
- `pnpm exec vitest run
server/src/__tests__/heartbeat-project-env.test.ts
server/src/__tests__/routines-service.test.ts
server/src/__tests__/secrets-service.test.ts
server/src/__tests__/qa-routine-secrets-e2e.test.ts
ui/src/components/RoutineHistoryTab.test.tsx` passed: 5 files, 92 tests.
- `pnpm -r typecheck` passed across the workspace.
- `pnpm build` passed. Vite emitted the existing
large-chunk/dynamic-import warnings.
- UI screenshots were captured locally during QA in
`artifacts/pap-9521/` and `artifacts/pap-9522/`; generated screenshots
are not committed to avoid adding binary artifacts to the repo.
## Risks
- Migration risk is limited by `IF NOT EXISTS` guards for the new
columns, FK, and index, and the migration is ordered as `0086`
immediately after upstream `0085`.
- Runtime behavior changes env precedence for routine executions by
adding routine env as the highest-precedence layer; tests cover
agent/project/routine precedence.
- Secret handling is security-sensitive; tests cover value-free
manifests/events/errors, disabled/missing/deleted secrets, and
cross-company rejection.
- UI history now renders routine env/secret diffs; tests and Storybook
stories cover the main rendering paths.
> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.
## Model Used
- OpenAI Codex coding agent based on GPT-5, with shell/tool use and
medium reasoning effort.
## 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 run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] If this change affects the UI, I have included before/after
screenshots
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip orchestrates AI agents for zero-human companies
> - The `grok-local` adapter streams reasoning text to the issue
"Working..." panel as the grok CLI runs
> - The `grok` CLI's `--output-format streaming-json` mode silently
drops the `\n` separator between reasoning turns around tool calls
> - Consecutive `thought` chunks (e.g. `` "`" `` followed by `"The"`)
arrive with no intervening whitespace event, so the UI's `delta: true`
concatenator merged them into run-on text like `"…planningGreat, now I
have the issue descriptionThe only co"`
> - This PR adds a small turn-boundary helper that detects sentence
boundaries in the upstream `thought` stream and inserts a single `\n`
only when the previous chunk ended with sentence punctuation (or a
balanced closing backtick) AND the next chunk begins a new uppercase
sentence
> - The benefit is readable streaming reasoning in the UI without
changing how completed messages are stored
## What Changed
- Added `packages/adapters/grok-local/src/shared/turn-boundary.ts` with
per-stream state (last chunk + backtick parity) and a
`restoreTurnBoundary()` helper that inserts `\n` only between balanced,
sentence-terminated `thought` chunks
- Wired the helper into `parseGrokJsonl` (server) and added a new
`createGrokStdoutParser` factory used by `grokLocalUIAdapter` for the
live "Working..." panel
- Added focused tests in `shared/turn-boundary.test.ts`, plus regression
assertions in `server/parse.test.ts` and `ui/parse-stdout.test.ts`
## Verification
- `pnpm --filter @paperclip/grok-local test` — 23/23 adapter tests pass
- `pnpm --filter @paperclip/grok-local typecheck` and UI typecheck —
clean
- Replayed an actual broken `grok 0.1.210` stream from the report;
previously-merged boundaries (`` `ls`The ``, `returned:Confirmed`) now
render with a separating newline; chunks inside un-closed backtick spans
are left alone
## Risks
- Low risk. Boundary insertion only fires when prev ends with
`.`/`!`/`?`/balanced `` ` `` and next begins with an uppercase ≥2-char
word, with no whitespace on either side. Worst case: a rare missed split
or a misplaced newline inside reasoning — both purely cosmetic and
confined to the live streaming panel.
## Model Used
- Claude Opus 4.7 (claude-opus-4-7), Anthropic, extended thinking + tool
use via Claude Code
## 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 run tests locally and they pass
- [x] I have added or updated tests where applicable
- [ ] If this change affects the UI, I have included before/after
screenshots
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip orchestrates AI agents for zero-human companies, so
adapter quality directly affects what runtimes the control plane can
supervise.
> - Local CLI adapters are one of the core execution surfaces because
they turn real coding tools into Paperclip-managed employees with
heartbeats, transcripts, and reviewability.
> - Grok Build was installed on the Paperclip host, but Paperclip had no
built-in `grok_local` adapter, so the runtime could not be configured
through the normal server/UI/CLI adapter path.
> - That gap needed to be closed with the same built-in registry,
environment diagnostics, transcript parsing, and skill/instructions
behavior that the other local adapters already rely on.
> - After the initial adapter landed, a real follow-up run showed that
Grok streaming text was being rendered one fragment per line, which made
transcripts harder to read even though the runtime itself was working.
> - This pull request adds the built-in `grok_local` adapter end-to-end
and then fixes the transcript parser so streamed Grok output is
coalesced into readable assistant/thinking blocks.
> - The benefit is that Grok Build becomes a first-class Paperclip
runtime with a usable operator experience instead of a partially wired
runtime with noisy transcript output.
## What Changed
- Added a new built-in `@paperclipai/adapter-grok-local` package with
server, UI, and CLI entrypoints.
- Implemented Grok execution, session handling, environment diagnostics,
config building, skill syncing, and parser coverage inside the new
adapter package.
- Registered `grok_local` across the built-in adapter inventories and
capability/display metadata in server, UI, CLI, and shared constants.
- Added adapter route coverage for the new built-in type.
- Fixed Grok transcript readability by emitting streamed `text` and
`thought` fragments as deltas so the shared transcript builder coalesces
them into readable message blocks.
- Added regression tests for the Grok parser and transcript coalescing
behavior.
## Verification
- `pnpm vitest run
packages/adapters/grok-local/src/ui/parse-stdout.test.ts
ui/src/adapters/transcript.test.ts`
- `pnpm --filter @paperclipai/adapter-grok-local build`
- Manual runtime verification on the Paperclip host during
implementation and follow-up review:
- confirmed the Grok CLI was installed and authenticated
- confirmed the worktree dev server could be restarted cleanly and
health-checked after the parser follow-up
- No screenshots attached. This change is primarily adapter plumbing
plus transcript formatting behavior; reviewers can verify via the
Grok-backed run surfaces directly.
## Risks
- This adds a new built-in adapter, so any missed registration surface
could create inconsistencies between server, UI, and CLI behavior.
- The adapter depends on Grok Build's current event/output shape; if
upstream Grok streaming JSON changes, transcript parsing or session
extraction may need follow-up updates.
- The transcript readability fix intentionally changes how Grok
fragments are grouped, so any downstream code that implicitly expected
one entry per fragment would behave differently.
> 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 via Paperclip `codex_local` agent runtime.
- GPT-5-class coding model with tool use, shell execution, file editing,
and repo inspection enabled.
- Exact backend model ID/context window were not surfaced to the agent
in this Paperclip session.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [ ] If this change affects the UI, I have included before/after
screenshots
- [ ] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] I will address all Greptile and reviewer comments before
requesting merge
## Thinking Path
> - Paperclip orchestrates AI agents through a board-facing control
plane.
> - The UI is the operator surface where onboarding and company creation
flows need to remain understandable across languages.
> - The i18next foundation now supports locale resource loading and
validation, but only English was present on `master`.
> - The branch exists to populate that foundation with the supported
language catalog without changing routing, data contracts, or runtime
behavior.
> - This pull request adds locale JSON resources for the current
non-English language set.
> - The benefit is that future locale selection work has validated
message catalogs ready for the first translated onboarding strings.
## What Changed
- Added 39 localized message catalogs under `ui/src/i18n/locales/` for
the existing no-companies onboarding strings.
- Kept the PR rebased onto current `master` so it only contains the
all-languages layer on top of the already-merged i18next foundation.
## Verification
- `pnpm exec vitest run ui/src/i18n/locale-validation.test.ts`
- Checked `ROADMAP.md`; this is scoped locale catalog follow-up work and
does not duplicate a listed roadmap feature.
- No before/after screenshots included because this PR only adds
resource JSON files and does not change rendered layout or visible
default-English UI behavior.
## Risks
- Low risk: static JSON resource additions validated against the English
reference schema.
- Translation quality may need native-speaker review before enabling
visible locale switching broadly.
> 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 CLI, GPT-5 family coding agent, tool-enabled repository
access, medium reasoning effort.
## 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 run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] If this change affects the UI, I have included before/after
screenshots
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip orchestrates AI agents for autonomous companies.
> - Agents and board operators coordinate through company-scoped issues,
comments, documents, and heartbeat wake payloads.
> - Chinese, Japanese, and Hindi text needs to survive the full issue
lifecycle without normalization or prompt serialization damage.
> - The riskiest paths are board issue creation, server
issue/comment/document round-tripping, and scoped wake prompt rendering.
> - This pull request adds focused regression coverage across those
surfaces.
> - The benefit is higher confidence that multilingual operators and
agents can create, search, comment on, complete, and wake on issues
using non-Latin text.
## What Changed
- Added adapter-utils wake payload and prompt rendering coverage for
Chinese, Japanese, and Hindi issue/comment text.
- Added UI New Issue dialog coverage proving multilingual title and
description text is submitted unchanged.
- Added server route coverage that round-trips multilingual issue text
through create, search, comments, documents, completion comments, and
heartbeat context.
- Addressed Greptile feedback by using a typed storage mock and
splitting the server route integration path into smaller ordered
assertions.
## Verification
- `pnpm exec vitest run packages/adapter-utils/src/server-utils.test.ts
ui/src/components/NewIssueDialog.test.tsx
server/src/__tests__/multilingual-issues-routes.test.ts`
- Result: 3 test files passed, 51 tests passed.
## Risks
- Low risk: this PR adds regression coverage only and does not change
runtime behavior.
- The new server test uses embedded Postgres support and skips on
unsupported hosts using the existing helper pattern.
- No migrations are included.
- No `pnpm-lock.yaml` changes are included.
> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected - check the roadmap
first. See `CONTRIBUTING.md`.
## Model Used
- OpenAI Codex, GPT-5 based coding agent, with shell, git, Vitest, and
GitHub connector/CLI 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 run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] If this change affects the UI, I have included before/after
screenshots
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] I will address all Greptile and reviewer comments before
requesting merge
## Thinking Path
> - Paperclip orchestrates AI-agent companies through a web control
plane.
> - The UI currently renders operator-facing copy directly from React
components.
> - Internationalization needs a smallest-possible starting point before
broader locale work can proceed.
> - The package declarations for `i18next` and `react-i18next` landed
separately, so this PR can stay focused on the implementation slice.
> - The implementation keeps the first surface English-only and
deliberately tiny while using the established `i18next` +
`react-i18next` runtime.
> - Future language contributions should be able to add a single locale
JSON file, with validation guarding key shape, interpolation parity,
suspicious payloads, and string length.
> - Locale strings must remain display-only UI copy and must not flow
into prompts, agent instructions, tool calls, shell commands, issue
content, approvals, adapter config, or other LLM-visible control paths.
## What Changed
- Initialized `i18next` behind the existing `@/i18n` boundary with fixed
English resources, fallback English, no detector plugin, no backend
plugin, no language picker, and no rich-text translation component.
- Kept `ui/src/i18n/locales/en.json` as the English source locale and
converted the validated JSON locale registry into i18next resources
before app rendering.
- Routed the no-companies start page title, description, and button
through `t(key, { defaultValue })` while preserving unchanged rendered
English copy.
- Added locale validation and focused Vitest coverage for missing/extra
keys, non-string leaves, interpolation parity, suspicious
executable/link payloads, and length caps.
- Addressed Greptile i18n review feedback: case-insensitive
event-handler detection, multi-violation diagnostics,
future-locale-friendly registration test, surfaced i18next init errors,
and removed the redundant side-effect import.
- Rebasing note: rebased onto current `public-gh/master` after the
package-only PR landed; this PR no longer changes `ui/package.json` or
`pnpm-lock.yaml`.
## Verification
- `pnpm install --no-lockfile --ignore-scripts` to install local
dependencies without reading or writing `pnpm-lock.yaml`.
- `pnpm --filter @paperclipai/ui exec vitest run
src/i18n/locale-validation.test.ts` -> passed, 7 tests.
- `pnpm --filter @paperclipai/ui typecheck` -> passed.
- `git diff --name-only public-gh/master...HEAD` shows only i18n
implementation files and the touched App copy call site; no package
manifest or lockfile changes remain in this PR.
- Visual impact is intentionally unchanged for the touched no-companies
copy because the English translations match the previous literal
strings.
## Risks
- Locale validation reduces prompt-injection risk, but the main safety
invariant is architectural: locale strings must remain display-only and
must never be used as LLM-visible control text.
- This intentionally does not add non-English locales, a language
picker, browser detection, HTTP/backend locale loading, server
localization, adapter localization, broad copy migration, or new package
scripts.
- Repository-wide CI may still depend on the separate lockfile-refresh
workflow for the already-merged package declaration, but this PR no
longer introduces package manifest or lockfile changes itself.
> 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, tool-enabled coding agent in medium reasoning
mode.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] If this change affects the UI, I have included before/after
screenshots, or documented why screenshots are not applicable because
there is no intended visual change
- [x] I have updated relevant documentation to reflect my changes, or
confirmed no docs changed because behavior/commands did not change
- [x] I have considered and documented any risks above
- [x] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip is a control plane for autonomous AI-agent companies, and
the board UI needs to keep operator visibility clear as company work
scales.
> - The involved subsystem is the Issues page board mode, specifically
the Kanban rendering path for issue status columns.
> - The current board keeps the classic Kanban model, but high-volume
columns can become tall, slow, and hard to scan when hundreds of issues
are loaded.
> - We explored alternatives and chose the conservative Scaled Kanban
direction: preserve status lanes and drag/drop, but bound visible cards
and collapse low-signal lanes.
> - This pull request adds UI-only density controls and high-volume
defaults rather than introducing schema or API changes.
> - The benefit is a board that remains usable with large issue
inventories while keeping active workflow lanes visible.
## What Changed
- Added scaled Kanban behavior with compact cards, collapsed cold-lane
rails, per-column visible-card limits, and per-column "show more" reveal
controls.
- Added persisted board density preferences to the Issues page view
state, scoped through the existing company-specific localStorage path.
- Added board toolbar controls for compact cards, collapsed cold lanes,
cards-per-column page size (`10`, `25`, `50`), and density reset.
- Added a design spec and implementation plan under `doc/plans/`.
- Added focused Vitest coverage for `KanbanBoard` and `IssuesList`
high-volume board behavior.
## Verification
- `pnpm exec vitest run ui/src/components/IssuesList.test.tsx
ui/src/components/KanbanBoard.test.tsx` — pass, 35 tests.
- `pnpm -r typecheck` — pass.
- `pnpm build` — pass before the upstream merge; not rerun after
docs/assets cleanup.
- `curl -fsS http://127.0.0.1:3100/api/health` — pass against restarted
local dev server after applying pending migration
`0078_white_darwin.sql`.
- `pnpm test:run` — previously failed in unrelated Cursor remote-sandbox
server tests:
- `server/src/__tests__/cursor-local-adapter-environment.test.ts`:
expected probe status `pass`, received `fail`.
- `server/src/__tests__/cursor-local-execute.test.ts`: two remote
sandbox execution cases exited `127` instead of `0`.
Local dev server for manual UI inspection: `http://127.0.0.1:3100`.
Screenshots were captured for review and attached in the PR thread
rather than committed to source.
## Risks
- Low schema/API risk: this is UI-only and uses the existing issue data
path.
- Board users may need to notice the new density controls if they want
to override high-volume defaults.
- Collapsed cold lanes remain valid drop targets, so status moves can
happen without expanding the destination lane.
- Very large remote columns can still hit the existing 200-item
per-column query cap; this PR improves rendering, not server-side board
pagination.
> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.
## Model Used
- OpenAI Codex coding agent based on GPT-5, with repository tool use,
shell execution, local test/build execution, and inline implementation
planning. No subagents were used.
## 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 run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] If this change affects the UI, I have included before/after
screenshots
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] I will address all Greptile and reviewer comments before
requesting merge
## Thinking Path
> - Paperclip orchestrates AI-agent companies through a web control
plane.
> - The UI i18n slice needs `i18next` and `react-i18next` available as
runtime packages before the implementation PR can stay focused on code
changes.
> - The implementation PR should not mix package declaration work with
Greptile-driven i18n code feedback.
> - This pull request isolates only the package manifest additions
requested by the maintainer.
> - The benefit is a tiny dependency-declaration PR that can be reviewed
and merged independently before rebasing the i18n implementation PR.
## What Changed
- Added `i18next` to `ui/package.json` dependencies.
- Added `react-i18next` to `ui/package.json` dependencies.
- Intentionally did not change `pnpm-lock.yaml`, matching the repository
policy that PRs do not commit lockfile changes.
## Verification
- `node -e
"JSON.parse(require('fs').readFileSync('ui/package.json','utf8'));
console.log('ui/package.json valid JSON')"`
- `git diff --name-only public-gh/master...HEAD` shows only
`ui/package.json`.
- `npm view i18next version` -> `26.2.0`.
- `npm view i18next@26.1.0 version` -> `26.1.0`.
- `npm view react-i18next version` -> `17.0.8`.
- `npm view react-i18next@17.0.7 version` -> `17.0.7`.
- Did not run `pnpm install --frozen-lockfile` because this PR
intentionally changes only `ui/package.json` and leaves lockfile
handling to the repository's separate lockfile workflow.
## Risks
- CI jobs that run `pnpm install --frozen-lockfile` may fail until the
repository lockfile workflow handles these package declarations.
- Low behavioral risk: this PR does not import or execute the packages
and changes no runtime code.
> 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, tool-enabled coding agent in medium reasoning
mode.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have run the applicable local validation for this manifest-only
change
- [x] I have added or updated tests where applicable
- [x] If this change affects the UI, I have included before/after
screenshots, or documented why screenshots are not applicable because
there is no runtime UI change
- [x] I have updated relevant documentation to reflect my changes, or
confirmed no docs changed because behavior/commands did not change
- [x] I have considered and documented any risks above
- [x] I will address all Greptile and reviewer comments before
requesting merge
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip orchestrates AI agents for zero-human companies.
> - The board UI keeps issue pages responsive by subscribing to live
activity events and invalidating TanStack Query caches.
> - Issue documents are first-class issue artifacts, but document
activity events were not refreshing the document list, active document,
or revision caches.
> - That meant a user could update a document on an issue and another
open board would keep showing stale document content until a page
reload.
> - This pull request routes issue document activity events through the
same live invalidation path used for issue and comment updates.
> - The benefit is that issue document changes become visible
automatically on active issue pages without forcing operators to reload
the board.
## What Changed
- Added live-update cache invalidation for `issue.document_created`,
`issue.document_updated`, `issue.document_restored`, and
`issue.document_deleted` activity events.
- Invalidated the issue document list, the active document cache, and
document revisions for both issue id and identifier references when the
activity payload includes a document key.
- Added regression coverage for document activity events so active issue
pages refetch document caches without inactive-only behavior.
- Simplified the document invalidation test mock after Greptile feedback
so the test only models the cache reads it actually uses.
## Verification
- `git rebase public-gh/master` reported the branch was up to date after
fetching `public-gh/master`.
- `pnpm run preflight:workspace-links` passed.
- `pnpm exec vitest run --project @paperclipai/ui
ui/src/context/LiveUpdatesProvider.test.ts` passed: 1 file, 16 tests.
- `pnpm --filter @paperclipai/ui typecheck` passed.
- PR checks passed on `eecd19f7b0355490f17314c94bffa06aada8f9e3`:
`policy`, `verify`, `e2e`, all 4 serialized server shards, `Canary Dry
Run`, `security/snyk`, and `Greptile Review`.
- Greptile completed with `5/5` confidence and no unresolved review
threads.
## Risks
- Low risk. This expands cache invalidation for existing live activity
events and does not change API contracts, database schema, migrations,
or document persistence behavior.
- No migrations or `pnpm-lock.yaml` changes are included.
> 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 coding agent, tool-enabled local repository
workflow.
## 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 run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] No visible UI layout changed; screenshots are not applicable for
live cache invalidation behavior
- [x] No documentation changes were needed for this internal UI cache
refresh fix
- [x] I have considered and documented any risks above
- [x] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip orchestrates AI-agent companies through company-scoped
issues, comments, and issue documents.
> - Issue documents are the durable place where plans, handoffs, and
other work artifacts are revised over time.
> - Some documents need to be preserved as operator-approved snapshots
while agents continue working on the same issue.
> - Without document locking, a later board or agent write can overwrite
the document key that reviewers expected to remain stable.
> - This pull request adds board-managed issue document locks and makes
agent writes to locked keys create a derived document instead of
mutating the locked document.
> - The benefit is safer document handoffs: approved or frozen issue
documents stay immutable until the board explicitly unlocks them.
## What Changed
- Added `locked_at`, `locked_by_agent_id`, and `locked_by_user_id`
document fields plus migration `0085_tranquil_the_executioner.sql`.
- Added document lock/unlock service behavior, route endpoints, activity
events, and locked-document write protections.
- Made agent document writes to locked keys create a new derived key
such as `plan-2` rather than overwriting the locked document.
- Surfaced lock state through shared issue document types, UI API
methods, document header lock controls, and activity formatting.
- Added server and UI tests for lock/unlock behavior, locked document
immutability, and UI action visibility.
- Updated `doc/SPEC-implementation.md` with the V1 document lock
contract and endpoints.
## Verification
- `git rebase public-gh/master` completed cleanly after committing the
branch changes.
- `git diff --check` passed before commit.
- `pnpm run preflight:workspace-links && pnpm exec vitest run
server/src/__tests__/documents-service.test.ts
server/src/__tests__/issue-agent-mutation-ownership-routes.test.ts
ui/src/components/IssueDocumentsSection.test.tsx
ui/src/components/IssueContinuationHandoff.test.tsx
ui/src/lib/document-revisions.test.ts` passed: 5 files, 32 tests.
## Risks
- Medium risk because this changes the document persistence contract and
adds a migration.
- The migration uses `ADD COLUMN IF NOT EXISTS` and guarded foreign-key
creation so it remains safe for users who may have already applied an
earlier copy of the migration.
- Locked documents intentionally reject board edits/deletes/restores
until unlocked; any existing workflows that expected direct overwrite
need to unlock first.
- Agent writes to locked keys now create derived documents, which may
create extra issue documents when agents retry locked writes.
## Model Used
- OpenAI Codex coding agent based on GPT-5, with tool use and local code
execution in the Paperclip worktree.
## 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 run tests locally and they pass
- [x] I have added or updated tests where applicable
- [ ] If this change affects the UI, I have included before/after
screenshots
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip orchestrates AI agents for zero-human companies through
company-scoped issues, comments, approvals, and execution workspaces.
> - Operators need the Inbox to show not only active work, but also
blocked work that may need human or agent attention.
> - The existing inbox experience did not have a dedicated blocked-work
surface, so blocked tasks were harder to triage and resume deliberately.
> - Backend consumers also needed a compact attention signal that
distinguishes actionable blockers from covered or waiting blocker
states.
> - This pull request adds a Blocked Inbox tab backed by issue
blocker-attention metadata, shared validators, and UI helpers.
> - The benefit is a clearer triage path for stalled or blocked
Paperclip work without exposing external wait internals in the
operator-facing UI.
## What Changed
- Added shared issue blocker-attention types, validators, and exports
for the API/UI contract.
- Added backend blocker-attention computation and issue route support
for blocked inbox data.
- Added the Blocked Inbox tab, blocked reason chips, filtering/search
UI, responsive layouts, and Storybook stories.
- Updated inbox helpers and page behavior so toolbar controls only
appear where they apply.
- Added coverage for shared validators, server blocker-attention
behavior, blocked inbox UI helpers/components, and the Inbox page.
- Added a screenshot helper script for the blocked inbox Storybook
stories.
- Addressed Greptile feedback by making urgency sorting deterministic
for null stop times, avoiding full blocked-inbox list enrichment for
counts, and hardening the screenshot helper.
## Verification
- Rebased the branch cleanly onto `public-gh/master`.
- Confirmed the diff does not include `pnpm-lock.yaml`.
- Confirmed the diff does not include database migration files.
- Ran `pnpm exec vitest run packages/shared/src/validators/issue.test.ts
server/src/__tests__/issue-blocker-attention.test.ts
ui/src/components/BlockedInboxView.test.tsx
ui/src/components/BlockedReasonChip.test.tsx
ui/src/lib/blockedInbox.test.ts ui/src/lib/inbox.test.ts
ui/src/pages/Inbox.test.tsx`.
- Ran `pnpm --filter @paperclipai/shared typecheck && pnpm --filter
@paperclipai/server typecheck && pnpm --filter @paperclipai/ui
typecheck`.
- Checked `ROADMAP.md`; this is scoped inbox/operator triage work and
does not duplicate a listed roadmap feature.
- Greptile Review is green on the latest head and all four Greptile
review threads are resolved.
- GitHub PR checks are green on the latest head: policy, security/snyk,
e2e, verify, Canary Dry Run, Greptile Review, and serialized server
suites 1/4 through 4/4.
## Risks
- Medium review surface because this touches the shared issue contract,
server issue services, and the Inbox UI together.
- Blocker-attention classification may need product tuning after
operators use it on real blocked queues.
- UI screenshots were not attached in this PR-opening pass; the branch
includes `scripts/screenshot-blocked-inbox.mjs` and Storybook stories
for visual capture.
> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.
## Model Used
OpenAI Codex, GPT-5-based coding agent with shell, git, GitHub CLI,
GitHub connector, and Paperclip API tool use. Reasoning mode: medium.
Context window: not exposed by the runtime.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [ ] If this change affects the UI, I have included before/after
screenshots
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip orchestrates AI-agent companies through company-scoped
issues, comments, and execution context.
> - The issue detail page is the board surface where operators and
agents inspect a task in its parent/child workflow.
> - Ordered sub-issues need a low-friction way to move through work
without returning to the parent list after every issue.
> - Existing issue detail navigation only covered sibling transitions
and did not continue into a parent issue's first ordered child.
> - This pull request adds ordered previous/next navigation for issue
detail views and extends it to continue from a parent or last sibling
into the first direct child.
> - The benefit is a smoother review/execution path through hierarchical
work while preserving hidden issue filtering and dependency-aware
ordering.
## What Changed
- Added `IssueSiblingNavigation` and route-state handling so issue
detail footers can link to previous/next ordered issues.
- Extended sub-issue ordering helpers to build navigation from siblings
plus direct children, including root-parent and
last-sibling-to-first-child cases.
- Added page, component, and library tests for ordered sibling
navigation, child fallback navigation, hidden issues, and link
rendering.
- Fixed the quicklook blur/click race Greptile found by deferring close
until after portaled link clicks can complete, with a regression test.
- Polished the navigation landmark label so it remains accurate when the
next target is a direct child rather than a sibling.
## Verification
- `pnpm exec vitest run src/components/IssueLinkQuicklook.test.tsx
src/lib/issue-detail-subissues.test.ts
src/components/IssueSiblingNavigation.test.tsx
src/pages/IssueDetail.test.tsx --config vitest.config.ts` from `ui/` -
31 tests passed.
- `pnpm --filter @paperclipai/ui typecheck` - passed.
- `git diff --check` - passed.
- GitHub PR checks on latest head `34046be2` - passed: Greptile Review,
verify, e2e, Canary Dry Run, policy, Snyk, and serialized server shards.
- Screenshots: not captured in this heartbeat; this PR is a draft and
the changed states are covered by focused component/page tests.
## Risks
- Low risk; this is a UI navigation addition with no database or API
contract changes.
- The main behavioral risk is navigation ordering drift if
`workflowSort` expectations change later.
- The IssueDetail navigation now waits for child issue loading, which
avoids stale child fallback links but can delay footer navigation
briefly while data loads.
> 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 coding agent with repository tool use and shell
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 run tests locally and they pass
- [x] I have added or updated tests where applicable
- [ ] If this change affects the UI, I have included before/after
screenshots
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip is a control plane for autonomous AI companies, where work
must end with a clear disposition rather than ambiguous agent liveness.
> - Recovery currently detects stalled or missing-next-step issues, but
source issue recovery can become split across child recovery issues,
blockers, and comments.
> - That makes it harder for operators and agents to see who owns
recovery and what exact action is needed on the original issue.
> - Source-scoped recovery actions give the original issue a first-class
active recovery state with owner, evidence, wake policy, and resolution
outcome.
> - This pull request adds the recovery-action data model, backend
reconciliation and resolution APIs, and board UI indicators/actions.
> - The benefit is clearer stalled-work recovery without losing source
issue context or relying on comments as the liveness path.
## What Changed
- Added the `issue_recovery_actions` schema, shared
types/constants/validators, and an idempotent
`0084_issue_recovery_actions` migration ordered after current `master`
migrations.
- Updated stranded/missing-disposition recovery to create source-scoped
recovery actions, wake the recovery owner on the source issue, and avoid
locking the source issue for recovery-action wakes.
- Added API support for reading active recovery actions on issue
detail/list surfaces and resolving them with restored, blocked,
cancelled, or false-positive outcomes.
- Require blocked recovery resolutions to have an unresolved first-class
blocker, and removed the UI shortcut that could mark recovery blocked
without a blocker selection path.
- Surfaced recovery indicators/actions in the issue UI, blocker notices,
active run panels, issue rows, and Storybook coverage.
- Updated docs and focused tests for recovery semantics, ownership,
races, stale comments, and UI behavior.
## Verification
- `pnpm exec vitest run
server/src/__tests__/issue-recovery-actions.test.ts
server/src/__tests__/heartbeat-process-recovery.test.ts
ui/src/components/IssueRecoveryActionCard.test.tsx
ui/src/components/IssueBlockedNotice.test.tsx ui/src/api/issues.test.ts`
— 5 files, 72 tests passed.
- `pnpm --filter @paperclipai/shared typecheck` — passed.
- `pnpm --filter @paperclipai/db typecheck` — passed, including
migration numbering check.
- `pnpm --filter @paperclipai/server typecheck` — passed.
- `pnpm --filter @paperclipai/ui typecheck` — passed.
- Follow-up verification after blocker-resolution guard: `pnpm exec
vitest run server/src/__tests__/issue-recovery-actions.test.ts
ui/src/components/IssueRecoveryActionCard.test.tsx
ui/src/api/issues.test.ts` — 3 files, 27 tests passed.
- Follow-up `pnpm --filter @paperclipai/server typecheck` — passed.
- Follow-up `pnpm --filter @paperclipai/ui typecheck` — passed.
- UI states are available in
`ui/storybook/stories/source-issue-recovery.stories.tsx`; screenshot
capture helper is `scripts/screenshot-recovery-card.cjs`.
## Risks
- Medium: recovery behavior changes from child recovery issue ownership
toward source-scoped actions, so operators may see stalled-work state in
new places.
- Migration risk is mitigated by using the next migration slot after
`master` and making the table/constraints/index creation idempotent for
anyone who previously applied the old branch-local
`0082_dizzy_master_mold` migration.
- Existing child recovery issue paths are still guarded for
already-created recovery issues, but new source-scoped flows should be
watched in CI and Greptile review.
> 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 coding agent, tool use enabled for shell, Git,
GitHub, and local test execution. Context window not exposed by the
runtime.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] If this change affects the UI, I have included before/after
screenshots
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip’s issue chat is an audit surface: reviewers need to trust
who actually authored a message.
> - Some historical agent comments were persisted with `authorUserId`
and no surviving `createdByRunId`, so the UI rendered real agent output
as if it came from the board user.
> - A pure timestamp-window fallback is too risky because human
reviewers can comment while agents are running.
> - The safe recovery path is to derive attribution only when the server
can prove it from same-issue run logs that include the exact posted
comment id, then let the chat renderer prefer that recovered agent
attribution.
> - This keeps historical threads trustworthy without mutating old
database rows or guessing in ambiguous cases.
## What Changed
- Added shared `IssueComment` fields for derived attribution so server
and UI can carry recovered `derivedAuthorAgentId`,
`derivedCreatedByRunId`, and `derivedAuthorSource` consistently.
- Added server-side attribution recovery in
`server/src/services/issues.ts` that reads same-issue run logs and only
derives agent authorship when a run log contains the exact `comment id:
...` emitted during posting.
- Updated issue chat rendering in `ui/src/lib/issue-chat-messages.ts` to
prefer direct agent authorship, then activity-log `runAgentId`, then the
server-derived attribution.
- Removed the unsafe UI-only run-window fallback from
`ui/src/pages/IssueDetail.tsx` so human comments posted during an active
run are not silently relabeled as agent output.
- Added regression coverage for both the run-log derivation path and the
chat-rendering fallback behavior.
- Bounded server-side run-log enrichment to 8 concurrent reads per
request and removed the unused `issueCommentSchema` declaration during
PR cleanup.
## Verification
- `pnpm exec vitest run ui/src/lib/issue-chat-messages.test.ts
server/src/__tests__/issues-service.test.ts`
- `pnpm test:run:general`
- Live validation on May 12, 2026 in `PAPA-322`: confirmed the
previously misattributed historical comments on `PAPA-316` now render as
Claude-authored on `http://goldie.gerbil-company.ts.net:3100`.
- Reviewer check: open `PAPA-316` in the running instance and confirm
historical comments such as `## Investigation: exe.dev 422 + codex
re-test` render under Claude instead of the board user.
## Risks
- Low risk. The change is scoped to comment attribution recovery and
rendering.
- Derived attribution is intentionally conservative: if there is no
exact run-log proof, the comment remains user-authored instead of
guessing.
- Run-log recovery depends on retained same-issue logs, so older
comments without that evidence remain unchanged.
## Model Used
- OpenAI Codex via the Paperclip `codex_local` adapter (GPT-5-class
coding agent with tool use in the local Paperclip runtime; the exact
deployment/model ID is not surfaced by this workspace).
## 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 run tests locally and they pass
- [x] I have added or updated tests where applicable
- [ ] If this change affects the UI, I have included before/after
screenshots
- [ ] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip is the control plane for autonomous AI companies, and
plugins extend that control plane without bloating core.
> - The LLM Wiki plugin adds a knowledge surface through the plugin
runtime and shared plugin UI components.
> - After the LLM Wiki work merged to `master`, CI exposed TypeScript
contract drift between plugin code, SDK component types, and update
settings types.
> - The ingestion settings update path intentionally accepts partial
source toggles, but its type intersected with the full settings shape
and required every source key.
> - The LLM Wiki UI also passes managed routine default-drift metadata
through the shared routine list item shape, but that metadata was
missing from the public item type.
> - This pull request narrows those type contracts to match the existing
runtime behavior.
> - The benefit is restoring typecheck on `master` with a small,
non-behavioral follow-up.
## What Changed
- Added a `WikiEventIngestionSettingsUpdate` type that permits partial
source updates without weakening normalized stored settings.
- Added managed routine default-drift metadata to the plugin SDK
`ManagedRoutinesListItem` type.
- Mirrored that managed routine default-drift type in the host UI
component item type.
## Verification
- `pnpm --filter @paperclipai/plugin-llm-wiki typecheck`
- `pnpm --filter @paperclipai/plugin-sdk typecheck`
- `pnpm --filter @paperclipai/ui typecheck`
- `git diff --check`
## Risks
- Low risk. This is a TypeScript type-contract fix only; no runtime
behavior or database schema 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-based coding agent, tool-enabled local repository
editing and command 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 run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] If this change affects the UI, I have included before/after
screenshots
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] I will address all Greptile and reviewer comments before
requesting merge
Notes on checklist applicability: no screenshots are included because
the UI change is a shared type-only contract update with no visual
behavior change; no docs were required because no behavior or commands
changed.
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip orchestrates AI agents through visible, governable task
and routine workflows.
> - Routines are the recurring-work surface where operators configure
schedules, runs, and activity.
> - PR #3569 moved routine operational tabs into the right-hand
properties panel while also redesigning the routine trigger editor.
> - The current product request is to remove that routine properties
right-tab change for now and come back to it later.
> - The cleanest way to do that is a direct revert of #3569 on top of
current `master`, which already includes the #5703 revert.
> - This pull request restores the pre-#3569 routine trigger/detail
behavior and removes the right-tab properties-panel routine layout.
> - The benefit is a simple, reviewable rollback with no schema or API
changes.
## What Changed
- Reverted #3569: `fix(ui): prevent lossy cron rewrites + redesign
routine triggers tab`.
- Restored the previous `RoutineDetail` inline tabs and trigger editing
flow.
- Restored the earlier `ScheduleEditor` implementation.
- Removed the UI components and tests introduced by #3569:
`ConfirmDialog`, `TriggerDialog`, `TriggerListCard`, and
`ScheduleEditor.test.ts`.
## Verification
- `git diff --check origin/master..HEAD`
- `pnpm vitest run ui/src/pages/Routines.test.tsx
ui/src/components/RoutineHistoryTab.test.tsx`
- `pnpm --filter @paperclipai/ui typecheck`
Notes:
- `pnpm install --frozen-lockfile` was run in the clean worktree before
verification. It completed with known workspace bin-link warnings for
`paperclip-plugin-dev-server` because the plugin SDK `dist/dev-cli.js`
has not been built in that fresh worktree.
- `Routines.test.tsx` emitted existing Radix dialog accessibility
warnings during the test run; the tests passed.
### Screenshots
This is a direct revert of #3569. The visual state after this PR
corresponds to the old screenshot from #3569, and the state being
removed corresponds to the new/right-panel screenshots from #3569.
| Before this revert | After this revert |
| --- | --- |
| <img width="1410" height="1325"
alt="routine-triggers-before-this-revert"
src="https://github.com/user-attachments/assets/d70dd35b-e72f-4fc6-bb21-be9b0d92b3b1"
/> | <img width="721" height="707"
alt="routine-triggers-after-this-revert"
src="https://github.com/user-attachments/assets/260bb682-32cb-4dff-b038-d55e45824b04"
/> |
Right-hand properties panel state removed by this revert:
<img width="1409" height="830" alt="routine-properties-panel-removed"
src="https://github.com/user-attachments/assets/f1d42f07-7cd3-4614-8e93-5b585affd4bf"
/>
## Risks
- Low technical risk: this is a clean Git revert of a UI-only PR.
- Product risk: #3569 also fixed lossy cron editing and added broader
schedule presets, so this rollback intentionally removes those
improvements along with the right-tab routine layout.
- Follow-up risk: if we want only the schedule-editor fixes back later,
they should be reintroduced separately from the routine properties-panel
layout.
> 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 coding agent, tool-enabled with local shell and
GitHub CLI access. Context window size was not exposed in this session.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] If this change affects the UI, I have included before/after
screenshots
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] I will address all Greptile and reviewer comments before
requesting merge
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip orchestrates AI agents through visible, governable task
and routine workflows.
> - The routines UI includes the routine detail page, properties panel,
history tab, and shared sidebar components.
> - PR #5703 changed that workflow by widening the routine properties
panel and moving revision inspection/comparison into dialogs.
> - The product direction for that change is being paused for now, so
the safest path is a direct revert instead of partial edits.
> - This pull request reverts merge commit
`74cb560c41305ac3283067d1ec8d3060ffdc28cb` from #5703.
> - The benefit is restoring the prior routines UI behavior while
keeping the revert easy to review and re-apply later if needed.
## What Changed
- Reverted #5703: `fix(ui): improve routine properties panel and history
UX`.
- Restored the previous routine properties panel sizing, panel context
API, routine detail layout, and routine history rendering behavior.
- Removed the reverted sidebar pane test additions and restored the
previous focused routine history test expectations.
## Verification
- `git diff --check origin/master..HEAD`
- `pnpm vitest run ui/src/components/RoutineHistoryTab.test.tsx`
- `pnpm --filter @paperclipai/ui typecheck`
### Screenshots
This is a direct revert of #5703. The visual state after this PR
corresponds to the "Before" screenshots from #5703, and the state being
removed corresponds to the "After" screenshots from #5703.
#### Trigger Panel Width
| Before this revert | After this revert |
| --- | --- |
| <img width="1742" height="1288" alt="triggers-before-this-revert"
src="https://github.com/user-attachments/assets/9e818978-283c-49a3-9401-879be550c67b"
/> | <img width="1741" height="1289" alt="triggers-after-this-revert"
src="https://github.com/user-attachments/assets/2a391769-c355-4219-8da3-d1ea18698430"
/> |
#### History Panel
| Before this revert | After this revert |
| --- | --- |
| <img width="1741" height="1290" alt="history-before-this-revert"
src="https://github.com/user-attachments/assets/4c139238-8494-4438-89e1-4277d05bc3aa"
/> | <img width="1739" height="1289" alt="history-after-this-revert"
src="https://github.com/user-attachments/assets/eaea4f3d-bb65-4af6-b67f-3ba3026fe0c9"
/> |
## Risks
- Low technical risk: this is a clean Git revert of a recently merged
UI-only PR.
- Product risk: the routine properties panel and revision history return
to the older, narrower workflow that #5703 was improving.
- Re-application risk: future work that wants the #5703 behavior back
should re-apply it deliberately rather than cherry-picking around this
revert.
> 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 coding agent, tool-enabled with local shell and
GitHub CLI access. Context window size was not exposed in this session.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] If this change affects the UI, I have included before/after
screenshots
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] I will address all Greptile and reviewer comments before
requesting merge
Co-authored-by: Paperclip <noreply@paperclip.ing>
> _Stacked on top of #5685 → #5686 → #5687. Diff against master includes
commits from earlier PRs in the stack — review focuses on the two new
commits (`Add long-secret textarea variant to JsonSchemaForm
SecretField` + `Add exe.dev sandbox provider plugin`)._
## Thinking Path
> - Paperclip orchestrates AI agents for zero-human companies
> - Each agent runs in a sandbox environment, and operators choose the
provider — today E2B, Daytona, and (in this stack) Cloudflare
> - exe.dev offers per-VM sandboxes via a small CLI / HTTP API — useful
for operators who want full Linux VMs (vs container/runtime-only
sandboxes)
> - The plugin shape mirrors the e2b plugin: lifecycle hooks (`new`,
`ls`, `rm`) drive exe.dev's CLI; SSH plumbing handles direct VM access
for adapters that need it
> - exe.dev VMs come up bare — `node` is not preinstalled, so the
Paperclip sandbox callback bridge (a Node script) needs Node 20
installed at VM init via `--setup-script`. The plugin defaults the setup
script to a Nodesource install
> - The auth field accepts long SSH private keys, which need a textarea
variant of the existing `SecretField` in `JsonSchemaForm` — added behind
a `maxLength > THRESHOLD` opt-in so other secret fields are unaffected
> - The benefit is that operators get exe.dev as a fully working sandbox
provider out of the box, with no manual VM provisioning required
## What Changed
**Shared UI support (`Add long-secret textarea variant to JsonSchemaForm
SecretField`):**
- `ui/src/components/JsonSchemaForm.tsx` + new
`JsonSchemaForm.test.tsx`: when a secret-formatted field declares
`maxLength` larger than the existing single-line threshold, render a
monospace textarea instead of the masked input. Short secrets (API keys,
tokens) keep the existing masked-input + show/hide toggle behavior.
**The exe.dev plugin (`Add exe.dev sandbox provider plugin`):**
- `packages/plugins/sandbox-providers/exe-dev/`: plugin entry, manifest,
plugin runtime, README, and 19-test Vitest suite.
- Manifest fields: API token (with `secret-ref` + `/exec` permission
notes — needs `new`, `ls`, `rm`), API URL override, optional SSH
username, optional SSH private key (uses the new `JsonSchemaForm`
textarea variant via `maxLength: 4096`), optional SSH identity-file
path, optional setup script.
- Default `--setup-script` is a Nodesource Node 20 install. exe.dev VMs
come up bare and the Paperclip sandbox callback bridge is a Node script,
so without Node preinstalled the bridge can't start. Operators can
override by supplying their own setup script.
- `runLifecycleCommand` redacts env values from the executed command
before surfacing it in error messages, so secrets passed via
`--env=KEY=VALUE` don't leak into operator-visible failures.
- The plugin distinguishes exe.dev's SSH onboarding failures (`Please
complete registration by running: ssh exe.dev`) from general SSH
failures and surfaces a clear remediation message.
- `scripts/release-package-manifest.json`: register the new plugin for
CI publish alongside the existing daytona / e2b providers.
## Verification
- `pnpm typecheck`
- `pnpm exec vitest run --no-coverage
ui/src/components/JsonSchemaForm.test.tsx`
- `(cd packages/plugins/sandbox-providers/exe-dev && pnpm test)` — 19
passing
For an operator-side smoke test:
1. Get an exe.dev API token with `/exec` permission for `new`, `ls`,
`rm`.
2. Register the plugin in your Paperclip instance, configure an
environment with the token.
3. Create a sandbox env whose provider is `exe-dev`, then run a Codex or
Claude job against it. The default Node 20 setup script should bring the
VM up automatically.
## Risks
- Adds a new sandbox provider plugin that follows the existing daytona /
e2b shape; behavior on existing providers is unchanged.
- The `JsonSchemaForm` textarea variant only engages for fields that opt
in via `maxLength` larger than the existing threshold. All existing
secret fields (which don't declare a `maxLength`) keep their current
rendering. Test coverage pins both paths.
- The redaction in `runLifecycleCommand` is a defense-in-depth measure;
the test suite exercises the redaction path. If the redaction misses a
future env-arg shape, the worst case is restored behavior (secrets in
error messages), which is what the existing daytona / e2b plugins also
do today.
- Default setup script downloads from `deb.nodesource.com` over HTTPS at
VM init. Operators on air-gapped networks or with a different package
strategy can override the setup script.
## Model Used
- Provider: Anthropic
- Model: Claude Opus 4.7 (1M context)
- Capabilities used: extended reasoning, tool use (Read/Edit/Bash/Grep)
## 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 run tests locally and they pass
- [x] I have added or updated tests where applicable
- [ ] If this change affects the UI, I have included before/after
screenshots — UI change is a textarea variant of an existing secret
field; will attach screenshots before requesting merge
- [x] I have updated relevant documentation to reflect my changes
(plugin README, manifest descriptions)
- [x] I have considered and documented any risks above
- [x] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip orchestrates AI agents for zero-human companies.
> - Routines are the recurring-work surface where operators configure
schedules, executions, activity, and revision history.
> - The routine detail view uses a contextual right properties panel for
triggers, runs, activity, and history.
> - That panel was too cramped for routine workflows: the routine header
could collapse at constrained widths, and revision previews/comparisons
were trying to live inside the same narrow panel.
> - This pull request makes the routine properties panel wider and
responsive without changing the default panel behavior for other pages.
> - It also moves routine revision viewing and comparison into focused
dialogs so history stays usable instead of rendering dense revision
content inside the right panel.
> - The benefit is a cleaner routine workflow: triggers remain
scannable, the main routine stays readable, and revisions can be
inspected, compared, and restored without fighting the sidebar width.
## What Changed
- Added optional per-panel layout options for storage key, default
width, min/max width, and compact viewport behavior.
- Set the routine properties panel to use its own 400px default width
and persistence key, while compacting to 320px on narrower viewports.
- Made the shared resizable sidebar support right-side panes, custom
width bounds, compact max width, and keyboard resizing.
- Fixed the routine detail header so title text and action controls
remain readable beside the properties panel at constrained widths.
- Reworked routine history so selecting a revision opens a read-only
snapshot dialog instead of trying to render the whole revision inside
the right panel.
- Added a side-by-side current-vs-selected revision comparison dialog
with clearer diff markers for structured fields, triggers, and
variables.
- Added focused tests for the resizable pane and routine history
behavior.
## Verification
- `pnpm vitest run ui/src/components/RoutineHistoryTab.test.tsx
ui/src/components/ResizableSidebarPane.test.tsx`
- `pnpm --filter @paperclipai/ui typecheck`
- `pnpm -r typecheck`
- `git diff --check`
- Browser E2E in TestCo at `http://localhost:3100/TES/dashboard`:
- created and edited a routine
- added, edited, toggled, and deleted schedule triggers
- paused automation
- ran the routine and stopped the live run
- verified runs, activity, history, snapshot dialog, compare mode,
restore confirmation, routine list, recent runs, row actions, panel
close/reopen, and constrained-width layout
### Screenshots
#### Trigger Panel Width
| Before | After |
| --- | --- |
| <img width="1741" height="1289" alt="triggers-before"
src="https://github.com/user-attachments/assets/2a391769-c355-4219-8da3-d1ea18698430"
/> | <img width="1742" height="1288" alt="triggers-after"
src="https://github.com/user-attachments/assets/9e818978-283c-49a3-9401-879be550c67b"
/> |
#### History Panel
Before, selecting a revision attempted to show dense revision content
inside the already narrow right panel. After, history remains a compact
list and revision details open separately.
| Before | After |
| --- | --- |
| <img width="1739" height="1289" alt="history-before"
src="https://github.com/user-attachments/assets/eaea4f3d-bb65-4af6-b67f-3ba3026fe0c9"
/> | <img width="1741" height="1290" alt="history-after"
src="https://github.com/user-attachments/assets/4c139238-8494-4438-89e1-4277d05bc3aa"
/> |
#### Revision Snapshot
The selected revision now opens in a dedicated read-only dialog instead
of crowding the properties panel.
<img width="1740" height="1289" alt="revision-single"
src="https://github.com/user-attachments/assets/f930f50f-7016-434b-bd81-d8d97304c528"
/>
#### Revision Compare
Historical revisions can be compared side-by-side with the current
revision, including changed structured fields and trigger differences.
<img width="1740" height="1287" alt="revision-compare"
src="https://github.com/user-attachments/assets/5640201e-de4f-446b-8941-1b0f140c56d7"
/>
## Risks
- Low to moderate UI risk: the shared resizable pane API gained optional
layout parameters, but existing callers keep the previous defaults.
- Routine history now uses dialogs for revision viewing and comparison,
so reviewers should confirm the new workflow feels right for restore and
compare.
- Routine panel width now persists under a routine-specific key, so
previous global properties panel width preferences do not carry into
routines.
> 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 coding agent in Codex Desktop, tool-enabled with
local shell, git, and in-app browser automation. Context window size was
not exposed in this session.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] If this change affects the UI, I have included before/after
screenshots
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] I will address all Greptile and reviewer comments before
requesting merge
## Thinking Path
> - Paperclip orchestrates AI agents for zero-human companies
> - Humans configure when those agents run via **routines**, which are
driven by cron-backed triggers
> - The routine detail page exposed triggers through an always-visible
inline add form and per-row inline editor, with a ScheduleEditor that
only understood a narrow set of cron shapes
> - That combination was actively lossy: pasting `0 9,13,17 * * *`
silently collapsed to `0 10 * * *` on save, and common shapes
(every-N-minutes within a window, multiple times per day, monthly on
several dates) had no first-class UI
> - This pull request rebuilds the triggers tab around a list of cards +
add/edit modal, teaches ScheduleEditor the cron shapes users actually
want, and prevents cron round-trips from dropping data
> - It also *optionally* tucks the Triggers/Runs/Activity tabs into the
shared right-hand PropertiesPanel (same pattern as Issues and Goals) so
they stay in view alongside the routine instead of being hidden below
the main content
> - The benefit is that routine scheduling becomes non-destructive and
legible — operators can see, describe, and edit real-world schedules
without dropping into raw cron and without fear that saving will
silently rewrite their trigger
## What Changed
**Core fixes + redesign (required):**
- **ScheduleEditor correctness** — `parseCronToPreset` now detects comma
lists, ranges, steps, and unknown tokens across every cron field and
routes anything it can't round-trip losslessly to the `custom` preset
(except `dow === "1-5"` → `weekdays`). Fixes the `0 9,13,17 * * *` → `0
10 * * *` regression.
- **ScheduleEditor presets** — adds first-class support for
every-N-minutes (with optional hour window + weekdays-only),
every-N-hours, hourly at minute offset, daily with multiple times/day,
selected-days-of-week with multiple times, and monthly on multiple
dates. `describeSchedule` unfolds multi-value hour/day lists into
readable sentences.
- **ScheduleEditor polish** — swaps raw `<input type=\"checkbox\">` for
the shadcn `Checkbox` primitive so hour-window and weekdays-only toggles
match the rest of the app.
- **Triggers tab redesign** — replaces the inline add form + inline
editor with a header + \"Add trigger\" button, compact `TriggerListCard`
entries, and a `TriggerDialog` add/edit modal. Enable/disable is now a
single-click switch on each card; delete goes through a `ConfirmDialog`.
- **Webhook trigger gating** — webhook kind is visible but disabled with
\"— COMING SOON\" in the add dialog, matching the old inline form's
production behaviour. Editing existing webhook triggers still works.
- **Tests** — adds `ScheduleEditor.test.ts` covering the regression cron
strings (`0 9,13,17 * * *`, `0 */4 * * *`, `0 10,16 * * *`) plus
existing preset patterns as regression guards in the other direction.
**Optional layout change (commit `145a86b5` — can be dropped without
affecting the rest):**
- Moves Triggers/Runs/Activity into the shared right-hand
`PropertiesPanel` (persisted open/close, header toggle button),
mirroring `IssueDetail` and `GoalDetail`. The reasoning: these tabs are
the primary way a human *operates* a routine, and keeping them docked on
the right means they're always in view next to the routine content
rather than hidden below the fold. Mobile parity is preserved by
rendering the same tabs inline below `md`. Trigger cards and
run/activity rows were restructured into vertical stacks so they fit the
320px panel without overflow, and the last-result badge became a
wrapping inline chip so long error strings no longer fill the card
width.
- **If reviewers prefer to keep the tabs inline below the routine, this
commit can be reverted cleanly without touching any of the fixes
above.**
## Screenshots:
Old:
<img width="721" height="707" alt="triggers-old"
src="https://github.com/user-attachments/assets/260bb682-32cb-4dff-b038-d55e45824b04"
/>
New:
<img width="1410" height="1325" alt="Screenshot 2026-04-13 at 12 25 00"
src="https://github.com/user-attachments/assets/d70dd35b-e72f-4fc6-bb21-be9b0d92b3b1"
/>
New Add Trigger modal:
<img width="1408" height="1321" alt="Screenshot 2026-04-13 at 12 25 07"
src="https://github.com/user-attachments/assets/0f23a83d-ba2c-47ed-9efa-829e777dcdf5"
/>
Commit 145a86b5 Properties panel:
<img width="1409" height="830"
alt="commit-145a86b51265e326160cb8c48e0874cb36d86f37"
src="https://github.com/user-attachments/assets/f1d42f07-7cd3-4614-8e93-5b585affd4bf"
/>
## Verification
- `cd ui && npm test -- ScheduleEditor` — new cron parser/describer
cases pass.
- Full UI test suite + typecheck green locally.
- Manual:
1. Open a routine → Triggers tab → verify cards render with enable
switch, edit, and delete (confirm dialog).
2. Create a schedule trigger with each preset (every-N-min with window,
every-N-hours, hourly@offset, daily multi-time, weekly multi-time,
monthly multi-date) → save → reopen → preset + values round-trip intact.
3. Paste `0 9,13,17 * * *` into an existing trigger → editor routes to
Custom with the raw cron preserved → save → value unchanged.
4. Try to add a webhook trigger → kind option shows \"— COMING SOON\"
and is disabled; edit an existing webhook trigger still works.
5. Toggle the properties panel via header button → state persists across
reload. Resize below `md` → tabs render inline.
- **Before/after screenshots:** attached in PR description (inline
triggers tab → list+modal; raw-cron save hazard → custom preset
preservation; bottom-of-page tabs → right-hand PropertiesPanel).
## Risks
- **Medium-low.** UI-only change; no API, schema, or migration impact.
- `parseCronToPreset` / `describeSchedule` signatures are preserved, but
their *behaviour* shifts: more cron strings now resolve to `custom` than
before. Any external caller relying on the old (lossy) classification
would see different preset tags — none known in-repo.
- PropertiesPanel reuse (optional commit) depends on the existing
localStorage key behaviour; if two routes ever write conflicting
open/close state under the same key, one could clobber the other.
Mirrors the established `IssueDetail`/`GoalDetail` pattern, so risk is
bounded. Reverting `145a86b5` removes this risk entirely while keeping
the fixes.
- Webhook kind is disabled in the add dialog only; existing webhook
triggers remain editable, so no data is stranded.
## Model Used
- **Authoring / PR drafting:** Anthropic Claude — `claude-opus-4-6` (1M
context window), via Claude Code CLI. Used for diff review and PR
description drafting. Code authored by @aronprins.
- **Post-hoc audit:** OpenAI Codex — `gpt-5.4` (high reasoning). Audited
the completed work after implementation; found no issues.
## Checklist
- [x] Thinking path traces from project context to this change
- [x] Model used specified with version + capability details
- [x] Tests run locally and pass
- [x] Added/updated tests (`ScheduleEditor.test.ts`)
- [x] Before/after screenshots attached
- [ ] Documentation updated — none required (internal UI only)
- [x] Risks documented
- [x] Will address all Greptile + reviewer comments before merge
## Thinking Path
> - Paperclip orchestrates AI agents for zero-human companies
> - There are many adapter types, one per agent-runtime product (Claude,
Codex, OpenCode, Cursor local CLI, etc.)
> - Cursor shipped a public TypeScript SDK on 2026-04-29 that exposes
Cursor's full hosted-agent platform (cloud VMs, harness, MCP, skills,
hooks)
> - Paperclip had no first-class adapter for this — agents that wanted
to use Cursor's managed cloud runtime had to fall back to the local CLI
adapter, which loses the cloud session, streaming, and durable run model
> - This PR adds a new `cursor_cloud` adapter built directly on
`@cursor/sdk`, with Paperclip's heartbeat mapped to Cursor's
durable-agent + per-run model
> - The benefit is that any Paperclip agent can now drive a Cursor cloud
agent across heartbeats with native session reuse, streaming, and
cancellation, while Paperclip remains the source of truth for issue/task
state
## What Changed
- New built-in adapter package `packages/adapters/cursor-cloud` (15
files, ~1.7k LOC) backed by `@cursor/sdk` ^1.0.12
- `src/server/execute.ts` — SDK-first lifecycle: `Agent.create` /
`Agent.resume` / `Agent.getRun` / `agent.send` / `run.stream` /
`run.wait`, with session reuse keyed on the (runtime env type, env name,
repo set) tuple
- `src/server/session.ts` — codec for `cursorAgentId` + `latestRunId` +
repo metadata, persisted in `runtime.sessionParams`
- `src/server/test.ts` — environment probe via `Cursor.me()` and
optional model validation via `Cursor.models.list()`
- `src/ui/parse-stdout.ts` + `src/cli/format-event.ts` — normalize
Cursor SDK message types (`status`, `thinking`, `assistant`, `user`,
`tool_call`, `tool_result`, `result`) into Paperclip transcript events
for the UI and CLI
- Registrations: `packages/shared/src/constants.ts`,
`packages/adapter-utils/src/session-compaction.ts`,
`server/src/adapters/{registry,builtin-adapter-types}.ts`,
`ui/src/adapters/{registry,adapter-display-registry}.ts` +
`ui/src/adapters/cursor-cloud/index.ts`, `cli/src/adapters/registry.ts`,
plus workspace deps in `cli`/`server`/`ui` `package.json`
- `ui/src/components/AgentConfigForm.tsx` — hide local-Cursor
`mode`/thinking-effort field for `cursor_cloud` (different config
surface)
- 11 vitest tests covering execute paths (fresh create, matching-resume,
active-run reattach, non-finished result), session codec round-trip,
transcript parsing, and config building
## Verification
Reviewer steps:
```bash
pnpm install
pnpm --filter @paperclipai/adapter-cursor-cloud typecheck # → clean
pnpm vitest run packages/adapters/cursor-cloud # → 11/11 passing
```
End-to-end check against a real Cursor cloud agent (requires
`CURSOR_API_KEY` and Cursor GitHub-app install on the target repo):
1. Create a `cursor_cloud` agent in Paperclip with `repoUrl` set to the
test repo, `repoStartingRef: main`, and `env.CURSOR_API_KEY` set
2. Trigger a heartbeat → adapter calls `Agent.create({ cloud: { env: {
type: "cloud" }, repos: [...] } })`, streams events, terminates on
`finished`
3. Trigger a second heartbeat → adapter calls `Agent.resume` or
`agent.send` follow-up depending on prior-run state, reusing
`cursorAgentId`
4. The Paperclip UI/CLI transcript reflects Cursor `status` / `thinking`
/ `assistant` events as they stream
5. Cancellation from Paperclip maps to `run.cancel()` or Cloud API v1
`cancelRun` for cross-heartbeat cancellation
A direct-SDK smoke run against a real repo (devinfoley/my_test_project @
main) confirmed: `Cursor.me()` ok → `Agent.create` → `agent.send` →
`run.stream()` (30 events) → terminal status `finished` in ~11s.
## Risks
- **New adapter, additive only.** No existing adapter or registry is
replaced; current `cursor` local-CLI adapter is untouched. Default
behavior of any existing agent is unchanged.
- **External dependency on `@cursor/sdk`.** Cursor's SDK is v1.0.x and
may evolve. Mocked unit tests cover the public surface used here; if the
SDK breaks compatibility we update the adapter independently.
- **Cost/budget.** `cursor_cloud` runs on Cursor's billed cloud VMs;
operators must understand they are spending money outside Paperclip's
budget controls when they enable this adapter. Same shape as other
API-billed adapters.
- **No webhook support in V1.** The SDK already provides
stream/wait/cancel/reattach, so V1 does not require a public callback
URL. If a future use case needs out-of-band wakes, we add a Cloud API v1
webhook bridge as a separate change. This is called out in the issue
plan document.
- **Lockfile.** Per repo policy, `pnpm-lock.yaml` is intentionally not
in this PR — CI's lockfile workflow will update it on merge given the
manifest changes.
## Model Used
- Provider: Anthropic Claude (via Claude Code / Paperclip `claude_local`
adapter)
- Model: `claude-opus-4-7` (Claude Opus 4.7), knowledge cutoff January
2026
- Mode: standard tool-use with extended reasoning
- Context: ~200k token window
- Capabilities used: code generation, multi-file edits, shell/test
execution, GitHub PR workflow
## 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 run tests locally and they pass (11/11 in
`packages/adapters/cursor-cloud`)
- [x] I have added or updated tests where applicable (4 new test files,
11 cases)
- [ ] If this change affects the UI, I have included before/after
screenshots (the only UI change is hiding the local-Cursor mode field on
the `cursor_cloud` adapter — happy to attach a screenshot if the
reviewer wants one)
- [x] I have updated relevant documentation to reflect my changes (issue
plan document supersedes the pre-SDK design; tracked in PAPA-203)
- [x] I have considered and documented any risks above
- [x] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip orchestrates AI agents for zero-human companies.
> - Company Environments is the operator-facing seam for choosing where
compatible adapters execute work.
> - Sandbox provider plugins such as E2B extend that seam, but they are
not agent adapters themselves.
> - The current Company Environments copy put adapter capability rows
and sandbox-provider enablement on the same page without clearly
distinguishing the two concepts.
> - That made it look like installing the E2B sandbox provider caused a
new adapter to appear under adapters.
> - This pull request clarifies the UI language so provider plugins are
described as backing the Sandbox driver rather than being adapter types.
> - The benefit is a more accurate mental model for operators
configuring environments and adapters.
## What Changed
- Added explicit Company Environments copy stating that installed
sandbox providers are not adapter types and instead back the Sandbox
driver for compatible adapters.
- Renamed the support-matrix column from `Sandbox` to `Sandbox via
plugin` to make the provider relationship visible in the table itself.
- Extended the existing environments UI test to assert the new
clarification text.
## Verification
- `pnpm test -- --run ui/src/pages/CompanySettings.test.tsx`
Result: could not complete cleanly in this worktree because the checkout
is missing its local workspace install links.
- Direct Vitest fallback against `ui/src/pages/CompanySettings.test.tsx`
Result: failed before test collection on local dependency resolution
(`react/jsx-dev-runtime`), so there is no passing automated signal from
this checkout.
- Manual review
Confirm the Company Environments page now says sandbox providers are not
adapter types and labels the table column as `Sandbox via plugin`.
## Risks
- Low risk. This is a copy-only UI clarification plus a matching test
assertion; the main risk is wording drift if the product later decides
sandbox providers should be surfaced differently.
## Model Used
- OpenAI Codex via the local `codex_local` Paperclip adapter. This run
used tool-assisted code editing and shell execution. The exact backend
model ID and context window are not exposed in the Paperclip run context
for this session.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [ ] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [ ] If this change affects the UI, I have included before/after
screenshots
- [ ] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [ ] I will address all Greptile and reviewer comments before
requesting merge
## Thinking Path
> - Paperclip is the control plane for AI-agent companies.
> - The board UI sidebar is one of the main ways operators scan active
agents and projects.
> - Agents and projects had duplicated section header behavior, which
made collapse controls, add actions, and future section menus harder to
keep consistent.
> - Operators also need lightweight ways to switch between their curated
sidebar order and common scan orders like alphabetical or recent
activity.
> - This pull request introduces a shared sidebar section header and
uses it for the Agents and Projects sidebar sections.
> - The benefit is a more consistent sidebar surface with reusable
header controls and persisted sort modes without losing the existing
drag-ordered Top view.
## What Changed
- Added a reusable `SidebarSection` component that supports collapsible
content, header actions, and section dropdown menus.
- Updated the Agents sidebar section to use the shared header and add
persisted `Top`, `Alphabetical`, and `Recent` sort modes.
- Updated the Projects sidebar section to use the shared header and add
persisted `Top`, `Alphabetical`, and `Recent` sort modes.
- Added local-storage helpers and cross-tab update events for
agent/project sidebar sort preferences.
- Added focused component coverage for the shared section behavior and
the updated Agents/Projects sidebar ordering paths.
## Verification
- `pnpm run preflight:workspace-links && pnpm exec vitest run
ui/src/components/SidebarSection.test.tsx
ui/src/components/SidebarProjects.test.tsx
ui/src/components/SidebarAgents.test.tsx`
- 3 test files passed
- 18 tests passed
## Risks
- Low-to-moderate UI risk: this changes sidebar section header
interactions and adds persisted client-side sort preferences.
- Drag ordering is intentionally limited to `Top` mode; non-top modes
render sorted lists and do not persist drag order changes.
- No database migrations or API contract changes.
> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.
## Model Used
- OpenAI Codex coding agent, GPT-5-based model, tool-use enabled; exact
hosted model build/context-window identifier was not exposed in this
session.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [ ] If this change affects the UI, I have included before/after
screenshots
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip orchestrates AI-agent companies and needs secrets handling
to work across local development, hosted operators, and governed agent
execution.
> - The affected subsystem is the company-scoped secrets control plane:
database schema, server services/routes, CLI workflows, and the Secrets
settings UI.
> - The gap was that secrets were local-only and operators could not
manage provider vaults or import existing remote references without
exposing plaintext.
> - This branch adds provider vault configuration plus an AWS Secrets
Manager remote-import path while preserving company boundaries, binding
context, and audit trails.
> - I kept the PR to a single branch PR, removed unrelated
lockfile/package drift, rebased the full branch onto the current
`public-gh/master`, and addressed fresh Greptile findings.
> - The benefit is a reviewable implementation of provider-backed
secrets with focused tests covering provider selection, import
conflicts, deleted secret reuse, rotation guards, and AWS signing
behavior.
## What Changed
- Added provider vault support for company secrets, including provider
config storage, default vault handling, health checks, binding usage,
access events, and remote import preview/commit.
- Added an AWS Secrets Manager provider using SigV4 request signing,
bounded request timeouts, namespace guardrails, cached runtime
credential resolution, and external-reference linking without plaintext
reads.
- Added Secrets UI surfaces for vault management and remote import, plus
CLI/API documentation for setup and operations.
- Stabilized routine webhook secret binding paths and SSH
environment-driver fixture bindings discovered during verification.
- Addressed Greptile and CI findings: no lockfile/package drift,
monotonic migration metadata, disabled-vault default races, soft-deleted
secret hiding/recreate behavior, remove behavior with disabled vaults,
soft-deleted external-reference re-import, non-active rotation guards,
managed-secret soft deletion through PATCH, and per-call AWS SDK
credential client churn.
- Rebased this branch onto `public-gh/master` at `0e1a5828` and
force-pushed with lease to keep this as the single PR for the branch.
## Verification
- `git fetch public-gh master`
- `git rebase public-gh/master`
- `git diff --name-only public-gh/master...HEAD | grep
'^pnpm-lock\.yaml$' || true` confirmed `pnpm-lock.yaml` is not in the PR
diff.
- Confirmed migration ordering: master ends at `0081_optimal_dormammu`;
this PR adds `0082_dry_vision` and
`0083_company_secret_provider_configs`.
- Inspected migrations for repeat safety: new tables/indexes use `IF NOT
EXISTS`; foreign keys are guarded by `DO $$ ... IF NOT EXISTS`; column
additions use `ADD COLUMN IF NOT EXISTS`.
- `pnpm -r typecheck` passed before the Greptile follow-up commits.
- `pnpm test:run` ran the full stable Vitest path before the Greptile
follow-up commits; it completed with 3 timing-related failures under
parallel load: `codex-local-execute.test.ts`,
`cursor-local-execute.test.ts`, and `environment-service.test.ts`.
- `pnpm --filter @paperclipai/server exec vitest run
src/__tests__/codex-local-execute.test.ts
src/__tests__/cursor-local-execute.test.ts
src/__tests__/environment-service.test.ts` passed on targeted rerun
(`24/24`).
- `pnpm build` passed before the Greptile follow-up commits. Vite
reported existing chunk-size/dynamic-import warnings.
- After Greptile follow-up commits: `pnpm --filter @paperclipai/server
exec vitest run src/__tests__/secrets-service.test.ts` passed (`26/26`).
- After Greptile follow-up commits: `pnpm --filter @paperclipai/server
exec vitest run src/__tests__/aws-secrets-manager-provider.test.ts
src/__tests__/secrets-service.test.ts` passed (`39/39`).
- After Greptile follow-up commits: `pnpm --filter @paperclipai/server
typecheck` passed.
- Captured Storybook screenshots from `ui/storybook-static` for visual
review.
- Latest PR checks on `5ca3a5cf`: `policy`, serialized server suites
1/4-4/4, `Canary Dry Run`, `e2e`, `security/snyk`, and `Greptile Review`
pass; aggregate `verify` is still registering the completed child
checks.
- Greptile review loop continued through the latest requested pass; all
Greptile review threads are resolved and the latest `Greptile Review`
check on `5ca3a5cf` passed with 0 comments added.
## Screenshots
Before: the provider-vault and remote-import surfaces did not exist on
`master`; these are after-state screenshots from the Storybook fixtures.



## Risks
- Migration risk: this adds new secret provider tables and extends
existing secret rows. The migrations were checked for monotonic ordering
and idempotent guards, but reviewers should still inspect upgrade
behavior carefully.
- Provider risk: AWS support uses direct SigV4 requests. Automated tests
cover signing, request timeouts, vault-config selection, namespace
guardrails, pending-version archival, sanitized provider errors, and
service-level cleanup paths. A real-vault AWS smoke test remains
deployment validation for an operator with AWS credentials rather than
an unverified merge blocker in this local branch.
- UI risk: the Secrets page and import dialog are large new surfaces;
screenshots are included above for reviewer inspection.
- Verification risk: the full local stable test command hit
parallel-load timing failures, although the exact failed files passed
when rerun directly.
- Operational risk: remote import intentionally avoids plaintext reads;
operators must understand that imported external references resolve at
runtime and may fail if AWS permissions 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, GPT-5 coding agent with local shell/tool use in the
Paperclip worktree. Exact context-window size was not exposed by the
runtime.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [ ] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] If this change affects the UI, I have included before/after
screenshots
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
## Thinking Path
> - Paperclip orchestrates AI agents for zero-human companies, so issue
threads are a core operator surface for reviewing work.
> - The issue detail page is the place where humans read agent messages,
user comments, and execution context together.
> - That thread originally rendered oldest-first, which made recent
activity harder to see during active review.
> - Reversing the thread order changes navigation expectations,
timestamp placement, and the "Jump to latest" affordance, so the UI
behavior needed to move as a coherent set.
> - Because this is a visible core-product behavior shift, it also
needed a safe rollout path instead of becoming the default immediately.
> - This pull request adds the newest-first issue thread behavior behind
an Experimental setting, updates the thread UI to match that mode, and
keeps the legacy oldest-first experience unchanged by default.
> - The benefit is that reviewers can opt into a more recent-first issue
workflow without forcing a global behavior change on every Paperclip
instance.
## What Changed
- Reversed issue thread rendering so the newest comments and messages
appear first when the experiment is enabled.
- Moved the plain comment timestamp into the card header in newest-first
mode and kept the legacy timestamp placement for oldest-first mode.
- Moved the `Jump to latest` control to the bottom of the thread in
newest-first mode while leaving the existing top placement for the
legacy mode.
- Added the `Enable Newest-First Issue Thread` experimental instance
setting and wired issue detail to read that toggle.
- Added regression coverage for thread order, timestamp placement,
jump-button placement, and the issue-detail experiment toggle behavior.
## Verification
- `pnpm -r typecheck`
- `pnpm test:run`
- `pnpm build`
- Focused checks that also passed during issue review:
- `pnpm vitest run src/components/IssueChatThread.test.tsx
src/pages/IssueDetail.test.tsx` in `ui/`
- `pnpm vitest run src/__tests__/instance-settings-routes.test.ts` in
`server/`
- Manual review path:
- Enable `Instance Settings > Experimental > Enable Newest-First Issue
Thread`
- Open an issue with comments/messages and confirm newest activity
renders first, timestamps move into the header, and `Jump to latest`
sits below the thread
- Disable the experiment and confirm the legacy oldest-first behavior
returns
## Risks
- Low risk: the behavioral change is gated behind an instance-level
experimental toggle and defaults off.
- The main regression risk is thread navigation drift between the two
modes, especially around anchor scrolling and the `Jump to latest`
affordance.
- There is some UI coupling between issue-detail query state and
experimental settings fetches, so future changes in that area should
keep both modes covered.
- Screenshots are not attached in this PR body; verification is
described with automated coverage and manual steps instead.
> I checked [`ROADMAP.md`](ROADMAP.md). This is a scoped issue-thread UX
improvement and rollout gate, not a duplicate of a roadmap-level planned
core feature.
## Model Used
- OpenAI Codex via the local `codex_local` Paperclip adapter,
GPT-5-based coding agent with terminal tool use and local code execution
in this repository worktree.
## 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 run tests locally and they pass
- [x] I have added or updated tests where applicable
- [ ] If this change affects the UI, I have included before/after
screenshots
- [ ] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] I will address all Greptile and reviewer comments before
requesting merge
## Thinking Path
> - Paperclip orchestrates AI agents for zero-human companies
> - Operators use the sidebar as their primary board navigation surface
> - The board now has a dedicated search page, so the header search icon
should behave as normal navigation instead of only dispatching a
command-palette shortcut
> - The Work nav also had a separate Search row, which duplicated the
always-visible header search affordance
> - This pull request keeps search one click away while making it a
direct `/search` link and reducing sidebar nav noise
> - The benefit is a smaller, clearer sidebar with search still
accessible from the top-level chrome
## What Changed
- Changed the sidebar header search icon into a direct `NavLink` to
`/search`.
- Removed the duplicate `Search` row from the Work navigation section.
- Added focused Sidebar coverage that asserts the header search link
target and confirms Search is not rendered in the Work nav.
- Refactored the Sidebar test setup helper to avoid repeating the React
Query wrapper across tests.
## Verification
- `pnpm install --frozen-lockfile` in the PR worktree so workspace
package symlinks existed for test execution. This completed with
existing plugin SDK bin warnings for missing built artifacts.
- `pnpm exec vitest run ui/src/components/Sidebar.test.tsx` — 3 passed.
- `pnpm --filter @paperclipai/ui typecheck` — passed.
## Risks
- Low: this changes a sidebar navigation affordance only. Users who
previously clicked the header icon now land on the full search page
instead of opening the command-palette shortcut path.
- Low: removing the Work nav Search row could affect users who expected
Search in that section, but the icon remains in the fixed sidebar header
and is covered by a targeted DOM test.
> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.
## Model Used
- OpenAI Codex coding agent, GPT-5 model family (`gpt-5`), tool-enabled
Paperclip heartbeat environment. Context window and internal reasoning
mode are not exposed by the runtime.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] If this change affects the UI, I have included before/after
screenshots or equivalent focused UI verification
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip orchestrates AI agents for zero-human companies
> - The issue graph and liveness recovery system decide whether assigned
work is executable or parked
> - Assigned issues created without an explicit status could silently
land in backlog, making parents look blocked with no productive wake
path
> - The server, shared validators, recovery analysis, and UI all need to
agree on that execution semantic
> - This pull request makes assigned issue creation default to `todo`,
flags assigned backlog blockers, and surfaces the state in the board
> - The benefit is that parked assigned work becomes intentional and
visible instead of creating silent liveness stalls
## What Changed
- Adds contract tests for assigned issue creation defaults.
- Defaults assigned issue creation to `todo` when status is omitted
while preserving explicit `backlog` parking.
- Exposes `resolveCreateIssueStatusDefault` through shared validators.
- Teaches liveness/blocker attention paths to distinguish assigned
backlog blockers.
- Adds UI notices, row/header badges, and issue detail safeguards for
assigned backlog blockers.
- Adds Storybook fixtures and execution-semantics documentation for the
assigned-backlog behavior.
## Verification
- `pnpm run preflight:workspace-links && pnpm exec vitest run
packages/shared/src/validators/issue.test.ts
server/src/__tests__/issue-assigned-backlog-contract-routes.test.ts
server/src/__tests__/issue-blocker-attention.test.ts
server/src/__tests__/issue-liveness.test.ts
server/src/__tests__/heartbeat-issue-liveness-escalation.test.ts
ui/src/components/IssueAssignedBacklogNotice.test.tsx
ui/src/components/IssueRow.test.tsx` — 50 passed, 23 skipped.
- Skipped tests were embedded Postgres suites on this host with the repo
skip message: `Postgres init script exited with code null. Please check
the logs for extra info. The data directory might already exist.`
- Pairwise merge check against the issue-controls PR branch completed
without conflicts via `git merge --no-commit --no-ff` in a temporary
worktree.
- Screenshots for assigned-backlog UI states:
[light](docs/pr-screenshots/pr-5428/assigned-backlog-light.png),
[dark](docs/pr-screenshots/pr-5428/assigned-backlog-dark.png).
- Follow-up checks: `pnpm --filter /ui typecheck`; `pnpm --filter
/mcp-server build`; `pnpm --filter /mcp-server test`; `pnpm exec vitest
run packages/shared/src/validators/issue.test.ts`; focused UI component
tests.
- Remote PR checks on head `6300b3c`: policy, verify, serialized server
shards 1/4-4/4, Canary Dry Run, e2e, Greptile Review, and Snyk all
passed.
## Risks
- Medium: changes status defaulting for assigned issue creation when the
caller omits status. Explicit `backlog` remains supported, and
server/shared tests cover both paths.
- Medium: liveness classification changes can affect blocker attention
labels; focused service and UI tests cover the new assigned-backlog
state.
> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.
## Model Used
- OpenAI Codex coding agent, GPT-5 model family (`gpt-5`), tool-enabled
Paperclip heartbeat environment. Context window and internal reasoning
mode are not exposed by the runtime.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] If this change affects the UI, I have included before/after
screenshots
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip orchestrates AI agents for zero-human companies
> - Operators spend most of their day scanning skills, routines, inbox
groups, and activity cards
> - Several small UI rough edges made those surfaces harder to scan or
easier to crash on real API payloads
> - These fixes are grouped together because they are low-risk operator
quality-of-life improvements rather than separate control-plane
contracts
> - This pull request polishes skills metadata, routine run-now access,
grouped issue creation defaults, monitor activity rendering, and
activity row identity layout
> - The benefit is a smoother board workflow with fewer small
interruptions while keeping the change set compact
## What Changed
- Improves company skill source display and the used-by agent list.
- Truncates long skill source paths and adds a copy affordance.
- Adds a row-level run-now button to the routines table.
- Adds grouped issue creation defaults for inbox issue groups and aligns
grouped add buttons to the right.
- Fixes `IssueMonitorActivityCard` when `monitorNextCheckAt` arrives as
an ISO string.
- Polishes activity row actor avatar/name layout by using the shared
avatar primitive.
## Verification
- `pnpm run preflight:workspace-links && pnpm exec vitest run
ui/src/pages/Routines.test.tsx ui/src/components/IssuesList.test.tsx
ui/src/lib/inbox.test.ts
ui/src/components/IssueMonitorActivityCard.test.tsx` — 91 passed.
- The routines test emitted the pre-existing Radix warning about missing
`DialogTitle`/description in dialog content; tests still passed.
- Pairwise merge checks against the other two PR branches reported no
textual conflicts.
## Risks
- Low: changes are UI-focused and covered by targeted component/lib
tests.
- Low-to-medium: activity row layout changes could affect dense feed
scanability; the implementation uses the shared avatar component and
keeps truncation 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 coding agent, GPT-5 model family (`gpt-5`), tool-enabled
Paperclip heartbeat environment. Context window and internal reasoning
mode are not exposed by the runtime.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] If this change affects the UI, I have included before/after
screenshots
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip orchestrates AI agents for zero-human companies
> - Issue operators need clear controls for execution settings, model
overrides, and recovery retries
> - Existing issue properties hid useful adapter override state and did
not expose a board-triggered retry for scheduled heartbeat recovery
> - Scheduled retries also need to respect the same safety gates as
normal execution instead of bypassing budget, review, pause, dependency,
or terminal-state checks
> - This pull request adds the issue property controls and retry-now
surfaces together because they share the issue details/properties UI
> - The benefit is that operators can inspect and adjust issue execution
settings and safely trigger pending scheduled recovery without hidden
control-plane behavior
## What Changed
- Adds editable issue assignee model override controls in
`IssueProperties`, with focused coverage.
- Removes the stale workspace tasks link from issue properties.
- Adds a scheduled retry `retry-now` backend path and shared response
types.
- Adds main-pane and properties-pane scheduled retry UI, backed by a
shared `useRetryNowMutation` hook.
- Adds suppression coverage for budget hard stops, review participant
changes, subtree pause holds, unresolved blockers, terminal issues, and
company scoping.
- Updates the `IssueProperties` test harness with toast actions required
by the retry-now hook.
## Verification
- `pnpm exec vitest run ui/src/components/IssueProperties.test.tsx
ui/src/components/IssueScheduledRetryCard.test.tsx` — 31 passed.
- `pnpm exec vitest run
server/src/__tests__/issue-scheduled-retry-routes.test.ts` — exited 0,
but this host skipped the embedded Postgres route tests with: `Postgres
init script exited with code null. Please check the logs for extra info.
The data directory might already exist.`
- Pairwise merge check against the assigned-backlog PR branch completed
without conflicts via `git merge --no-commit --no-ff` in a temporary
worktree.
### Visual verification screenshots
Storybook story: `Product/Issue Scheduled retry surfaces /
ScheduledRetrySurfaces`.


## Risks
- Medium: this touches issue execution/retry behavior, so CI should run
the embedded Postgres route tests on a host that can initialize
Postgres.
- Low-to-medium UI risk around duplicated retry-now entry points; both
surfaces share one mutation hook to keep behavior consistent.
> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.
## Model Used
- OpenAI Codex coding agent, GPT-5 model family (`gpt-5`), tool-enabled
Paperclip heartbeat environment. Context window and internal reasoning
mode are not exposed by the runtime.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] If this change affects the UI, I have included before/after
screenshots
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip orchestrates AI agents for zero-human companies
> - The issue thread is the operator's durable audit trail for what
changed and why
> - Workspace changes and stale disposition notices need to be visible
in that same timeline without noisy or misleading rendering
> - The local branch already contained backend activity details,
timeline conversion, and UI rendering work for those events
> - This pull request isolates the issue-thread activity work into a
standalone branch against `origin/master`
> - The benefit is a focused audit-trail PR that can merge independently
of the sidebar/operator UI polish branch
## What Changed
- Adds readable workspace-change activity details to issue update
activity events.
- Surfaces workspace-change events in issue chat/timeline rendering.
- Makes the existing issue comment migration idempotent.
- Folds and renders stale disposition notices inline so they match
activity-log styling and spacing.
- Adds focused route, timeline, and issue-thread system notice coverage.
## Verification
- `pnpm install --frozen-lockfile`
- `pnpm exec vitest run
server/src/__tests__/issue-activity-events-routes.test.ts
ui/src/lib/issue-timeline-events.test.ts
ui/src/components/IssueChatThreadSystemNotice.test.tsx` — 3 files
passed, 22 tests passed.
- Confirmed the PR changes 9 files and does not include `pnpm-lock.yaml`
or `.github/workflows/*`.
- `pnpm exec vitest run
server/src/__tests__/issue-closed-workspace-routes.test.ts` — 1 file
passed, 4 tests passed.
- `pnpm exec vitest run
server/src/__tests__/issue-activity-events-routes.test.ts
ui/src/lib/issue-timeline-events.test.ts
ui/src/components/IssueChatThreadSystemNotice.test.tsx
server/src/services/recovery/successful-run-handoff.test.ts
packages/shared/src/validators/issue.test.ts` — 5 files passed, 54 tests
passed.
- `pnpm --filter @paperclipai/shared typecheck && pnpm --filter
@paperclipai/server typecheck && pnpm --filter @paperclipai/ui
typecheck`.
- `pnpm --filter @paperclipai/ui typecheck` after adding the Storybook
screenshot fixture.
- Captured Storybook screenshots for the new UI rendering paths:
- Collapsed stale notice + workspace-change row:
`docs/pr-screenshots/pr-5356/issue-thread-notices-collapsed.png`
- Expanded stale notice details:
`docs/pr-screenshots/pr-5356/issue-thread-notices-expanded.png`
### Screenshots
Collapsed stale notice with workspace-change row:

Expanded stale notice details:

## Risks
- Moderate risk: this touches issue activity serialization and
issue-thread rendering, both of which are central operator surfaces.
- Migration risk is low: the only migration change makes an existing
migration idempotent.
- No new migrations are introduced, so there is no cross-PR migration
ordering requirement.
> 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 coding agent, shell/tool-use enabled, used to
split the existing branch, verify the isolated PR branch, and create
this PR.
## 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 run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] If this change affects the UI, I have included before/after
screenshots
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip orchestrates AI agents for zero-human companies
> - Operators use the board sidebar and issue properties panel to move
between companies and understand task metadata
> - Small UI regressions in these controls make repeated board operation
slower and less predictable
> - The local branch already contained targeted fixes for company
ordering, issue date display, and sidebar rail sizing
> - This pull request isolates those operator UI quality-of-life fixes
into a standalone branch against `origin/master`
> - The benefit is a focused, reviewable PR that can merge independently
of the issue-thread activity work
## What Changed
- Shows issue property timestamps with time, not just dates.
- Adds edit-mode support for ordering companies in the sidebar company
menu.
- Fixes a workspace switcher rail regression and keeps the account menu
aligned with the rail width.
- Includes focused component coverage for the touched controls.
## Verification
- `pnpm install --frozen-lockfile`
- `pnpm exec vitest run ui/src/components/IssueProperties.test.tsx
ui/src/components/SidebarCompanyMenu.test.tsx
ui/src/components/Layout.test.tsx
ui/src/components/SidebarAccountMenu.test.tsx` — 4 files passed, 29
tests passed.
- `pnpm --filter /ui typecheck`
- PR checks on `a4030f7a` are green: policy, verify, serialized server
suites 1/4-4/4, e2e, Canary Dry Run, Greptile Review, and Snyk.
- Captured a local Storybook screenshot of `Product/Navigation & Layout`
after the sidebar polish:
`/tmp/pap-3659-screenshots/navigation-layout-after.png`.
- Confirmed the PR changes 8 files and does not include `pnpm-lock.yaml`
or `.github/workflows/*`.
## Risks
- Low to moderate UI risk: this touches shared sidebar components and
issue metadata rendering.
- The company ordering behavior depends on existing query/cache
behavior, so stale cache bugs would show up as ordering inconsistencies.
- No database, API, workflow, or lockfile changes are included.
> 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 coding agent, shell/tool-use enabled, used to
split the existing branch, verify the isolated PR branch, and create
this PR.
## 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 run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] If this change affects the UI, I have included before/after
screenshots
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip relies on issue identifiers, execution policies, and agent
heartbeat rules to keep autonomous work auditable.
> - Safety checks need to reject ambiguous agent handoffs, and
identifier parsing needs to support Cloud tenant prefixes.
> - Agent instructions also need to make final-disposition rules
explicit so work does not stall in vague states.
> - This pull request isolates backend correctness and governance
hardening from the UI and recovery-system-notice branches.
> - The benefit is safer in-review transitions, better identifier
compatibility, and clearer agent operating contracts.
## What Changed
- Fixed run-aware confirmation ordering and interrupted-run state
cleanup.
- Added Cloud tenant identity bootstrap and alphanumeric issue
identifier support across shared parsing and server routes.
- Guarded agent-authored `in_review` updates unless a real review path
exists.
- Tightened heartbeat disposition instructions in adapter
utilities/default AGENTS/Paperclip skill.
## Verification
- `pnpm install --frozen-lockfile`
- `pnpm exec vitest run packages/shared/src/issue-references.test.ts
server/src/__tests__/issue-identifier-routes.test.ts
server/src/__tests__/issue-execution-policy-routes.test.ts
packages/adapter-utils/src/server-utils.test.ts` initially had the first
execution-policy test hit Vitest's 5s timeout under the parallel bundle
while the rest passed.
- `pnpm exec vitest run
server/src/__tests__/issue-execution-policy-routes.test.ts
--testTimeout=20000` passed with 10/10 tests.
- Follow-up: `pnpm run typecheck:build-gaps` passed.
- Follow-up: `pnpm --filter @paperclipai/ui typecheck` passed.
- Follow-up: `pnpm vitest run
server/src/__tests__/issue-comment-reopen-routes.test.ts
server/src/__tests__/company-portability.test.ts
server/src/__tests__/costs-service.test.ts` passed.
- Follow-up: `pnpm vitest run ui/src/context/LiveUpdatesProvider.test.ts
ui/src/lib/issue-chat-messages.test.ts
ui/src/lib/issue-reference.test.ts
ui/src/lib/issue-timeline-events.test.ts` passed.
## Risks
- Medium control-plane risk: in-review update validation changes agent
behavior. The error message is explicit and tests cover allowed review
paths.
## Model Used
- OpenAI GPT-5 Codex via Paperclip `codex_local` adapter, with
shell/git/GitHub CLI 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 run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] If this change affects the UI, I have included before/after
screenshots
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip is a control plane for autonomous AI companies.
> - Issues are the core unit of work, and issue comments are how board
users and agents coordinate execution.
> - Some issue conversations need to produce plans and approvals instead
of immediate implementation work.
> - The existing issue contract did not distinguish standard execution
comments from planning-oriented issue work.
> - This pull request adds an issue work-mode contract and board UI
affordances for standard vs planning mode.
> - The benefit is that planning-mode issues can be created, displayed,
discussed, and carried through agent heartbeat context without losing
the normal issue workflow.
## What Changed
- Added `standard` / `planning` issue work-mode contracts across DB,
shared validators/types, server issue flows, plugin protocol, and
adapter heartbeat payloads.
- Added an idempotent `0081_optimal_dormammu` migration for
`issues.work_mode`, ordered after current `public-gh/master` migrations.
- Updated heartbeat/context summaries and issue-thread interaction
behavior so planning work mode is preserved when creating suggested
follow-up issues.
- Added UI support for planning-mode issue creation, issue rows, detail
composer styling, and composer work-mode toggles.
- Added focused server/shared/UI tests plus a Playwright visual
verification spec for planning-mode surfaces.
- Rebased the branch onto current `public-gh/master` and added durable
planning-mode screenshots under `doc/assets/pap-3368/`.
## Verification
- `pnpm --filter @paperclipai/db run check:migrations`
- `pnpm exec vitest run --project @paperclipai/shared
packages/shared/src/validators/issue.test.ts`
- `pnpm exec vitest run --project @paperclipai/server
server/src/__tests__/heartbeat-context-summary.test.ts
server/src/__tests__/issue-thread-interactions-service.test.ts
server/src/__tests__/issues-goal-context-routes.test.ts --pool=forks
--poolOptions.forks.isolate=true`
- `pnpm exec vitest run --project @paperclipai/ui
ui/src/components/IssueChatThread.test.tsx
ui/src/components/NewIssueDialog.test.tsx
ui/src/components/IssueRow.test.tsx ui/src/pages/IssueDetail.test.tsx`
- `pnpm exec vitest run --project @paperclipai/adapter-utils
packages/adapter-utils/src/server-utils.test.ts`
- `PAPERCLIP_E2E_SKIP_LLM=true npx playwright test --config
tests/e2e/playwright.config.ts
tests/e2e/planning-mode-visual-verification.spec.ts`
## Screenshots
Desktop planning detail:

Desktop planning row:

Desktop staged standard toggle:

Mobile planning detail:

Mobile planning row:

## Risks
- Medium migration risk: this adds a non-null issue column. The
migration uses `ADD COLUMN IF NOT EXISTS` so installations that applied
an older branch-local migration number can still apply the final
numbered migration safely.
- Medium contract risk: issue payloads, plugin payloads, and adapter
heartbeat payloads now include work mode; compatibility is handled by
defaulting missing values to `standard`.
- UI risk is moderate because composer controls changed; focused
component tests and visual e2e coverage exercise standard vs planning
display and toggle 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, GPT-5 coding agent in a local Paperclip worktree, with
shell/tool use. Exact context-window size is not exposed in this
runtime.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] If this change affects the UI, I have included before/after
screenshots
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip orchestrates AI agents for zero-human companies.
> - Operators need to find work, documents, agents, projects, comments,
and activity across a company without jumping through separate surfaces.
> - The existing Command-K flow was useful for fast navigation but not
enough for deeper company-wide discovery.
> - Search also needs company-scoped backend contracts, query cost
controls, and indexed document matching so it stays safe as company data
grows.
> - This pull request adds a full company search API and a dedicated
board search page that Command-K can hand off to.
> - The benefit is a single searchable control-plane surface with richer
result context, recents, highlights, and test coverage across server and
UI behavior.
## What Changed
- Added a company-scoped search endpoint/service with query validation,
rate limiting, text matching, fuzzy title matching, and result typing
shared through `@paperclipai/shared`.
- Added idempotent search migrations for document search indexes and
fuzzy matching support.
- Added the full `/companies/:companyKey/search` UI, search result row
components, highlighted snippets, recent searches, and sidebar/Command-K
handoff.
- Added Storybook coverage for search surfaces and Vitest coverage for
server search behavior, rate limiting, route generation, Command-K
behavior, and the search page.
- Addressed Greptile findings by renaming the no-match SQL helper,
applying search pagination after cross-type merge sorting, and
lazy-initializing the default search service so unrelated route-test
mocks do not need to know about it.
- Merged current `public-gh/master` and renumbered the search migrations
behind upstream `0078_white_darwin`: search indexes are now
`0079_company_search_document_indexes` and fuzzy matching is
`0080_company_search_fuzzystrmatch`.
## Verification
- `git fetch public-gh master`
- `git diff --check public-gh/master...HEAD`
- `git diff --name-only public-gh/master...HEAD | rg '^pnpm-lock\.yaml$'
|| true` produced no output before opening the PR.
- `pnpm run preflight:workspace-links && pnpm exec vitest run
server/src/__tests__/company-search-service.test.ts
server/src/__tests__/company-search-rate-limit-routes.test.ts
ui/src/pages/Search.test.tsx ui/src/components/CommandPalette.test.tsx
ui/src/lib/company-routes.test.ts` passed: 5 files, 25 tests.
- `pnpm --filter @paperclipai/shared typecheck && pnpm --filter
@paperclipai/db typecheck && pnpm --filter @paperclipai/server typecheck
&& pnpm --filter @paperclipai/ui typecheck` passed.
- `pnpm exec vitest run
server/src/__tests__/company-search-service.test.ts
server/src/__tests__/company-search-rate-limit-routes.test.ts && pnpm
--filter @paperclipai/server typecheck` passed after Greptile pagination
fixes.
- `pnpm exec vitest run
server/src/__tests__/issue-agent-mutation-ownership-routes.test.ts
server/src/__tests__/company-search-rate-limit-routes.test.ts
server/src/__tests__/company-search-service.test.ts && pnpm --filter
@paperclipai/server typecheck` passed after the CI mock fix.
- After resolving the migration conflict with current
`public-gh/master`: `pnpm --filter @paperclipai/db typecheck && pnpm
exec vitest run server/src/__tests__/company-search-service.test.ts
server/src/__tests__/company-search-rate-limit-routes.test.ts && pnpm
--filter @paperclipai/server typecheck` passed.
- DB migration numbering check passed as part of `@paperclipai/db`
typecheck.
- UI states are covered by the added Storybook stories in
`ui/storybook/stories/search.stories.tsx`.
- GitHub reports the PR merge state as `CLEAN` on head `18e54fa8`.
- GitHub PR checks are green on head `18e54fa8`: policy, verify,
serialized server shards 1/4 through 4/4, e2e, canary dry run, Snyk, and
Greptile Review.
## Risks
- Search ranking and snippets are new user-facing behavior, so reviewers
should check whether result ordering feels right on real company data.
- Search touches broad company data, so company scoping and query
cost/rate-limit behavior should be reviewed carefully.
- The migrations add search indexes/extensions; they are idempotent with
`IF NOT EXISTS` for users who may have applied an earlier branch
migration number.
> ROADMAP.md checked. This PR adds a focused board search surface and
does not duplicate an open roadmap item.
## Model Used
- OpenAI Codex, GPT-5 coding agent, tool-enabled shell/git/GitHub CLI
session with medium reasoning effort. Existing branch commits were
produced across prior agent sessions; this packaging pass verified,
opened the PR, addressed Greptile findings, resolved migration conflicts
after upstream PRs landed, and got PR checks green.
## 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 run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] If this change affects the UI, I have included before/after
screenshots
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
## Thinking Path
> - Paperclip is a control plane operators use repeatedly to supervise
agent companies.
> - Common operator workflows depend on fast scanning of inboxes, issue
sidebars, workspaces, cost totals, and runtime services.
> - Several small UI and service gaps made those workflows slower or
less clear.
> - This pull request groups the operator-facing QoL changes that can
stand alone from recovery and adapter work.
> - The benefit is a denser, clearer board experience for issue triage
and workspace operation.
## What Changed
- Added inbox assignee/project grouping and issue list token/runtime
totals.
- Improved issue properties with removable blocker chips and workspace
task links.
- Improved execution workspace layout, runtime controls, issues tab
default, and stopped-port reuse behavior.
- Added mobile markdown/routine dialog fixes, page title company names,
sidebar polish, and dashboard run task label cleanup.
## Verification
- `pnpm install --frozen-lockfile`
- `pnpm exec vitest run ui/src/lib/inbox.test.ts
ui/src/components/IssueProperties.test.tsx
ui/src/components/WorkspaceRuntimeControls.test.tsx
server/src/__tests__/workspace-runtime.test.ts
server/src/__tests__/costs-service.test.ts`
## Risks
- Medium UI risk because this touches several operator surfaces. The
branch is intentionally grouped around workflow/QoL files and keeps the
file count below the Greptile limit.
## Model Used
- OpenAI GPT-5 Codex via Paperclip `codex_local` adapter, with
shell/git/GitHub CLI 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 run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] If this change affects the UI, I have included before/after
screenshots
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip orchestrates AI agents across several adapter
implementations.
> - ACPX is a local adapter path that can proxy Claude and Codex-style
execution.
> - Its configuration needed stronger schema defaults, provider-aware
model handling, and better UI support.
> - Plugin authors also need clear docs for managed resources.
> - This pull request improves ACPX adapter configuration and documents
plugin-managed resources.
> - The benefit is a more predictable adapter setup path without
changing unrelated control-plane behavior.
## What Changed
- Improved ACPX config schema, execution config handling, UI build
config, and route coverage.
- Added ACPX model filtering support and tests.
- Updated the agent config form and storybook coverage for ACPX
model/provider behavior.
- Expanded plugin authoring documentation for managed resources.
## Verification
- `pnpm install --frozen-lockfile`
- `pnpm exec vitest run server/src/__tests__/acpx-local-execute.test.ts
server/src/__tests__/adapter-routes.test.ts
ui/src/lib/acpx-model-filter.test.ts`
## Risks
- Low-to-medium risk: adapter configuration behavior changes can affect
ACPX users, but the change is isolated to ACPX/plugin-doc surfaces and
covered by targeted adapter tests.
## Model Used
- OpenAI GPT-5 Codex via Paperclip `codex_local` adapter, with
shell/git/GitHub CLI 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 run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] If this change affects the UI, I have included before/after
screenshots
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip orchestrates AI agents for zero-human companies.
> - Agent runs can end productively while the source issue still lacks a
durable final disposition.
> - That leaves the control plane unsure whether to resume, escalate, or
close the work.
> - Issue comments also need a presentation contract so system-authored
recovery notices can render as first-class thread messages without
overloading normal comments.
> - This pull request adds successful-run handoff recovery, comment
presentation metadata, and system notice rendering.
> - The benefit is stricter task liveness with clearer operator-facing
recovery state.
## What Changed
- Added successful-run handoff decisions, wake payloads, escalation
behavior, and recovery tests.
- Added issue comment presentation metadata with migration
`0078_white_darwin.sql` and shared/server/company portability support.
- Rendered recovery/system notices in issue chat with dedicated UI
components, fixtures, tests, and storybook/lab coverage.
- Included the current recovery model-profile hint patch so automatic
recovery follow-ups use the cheap profile.
## Verification
- `pnpm install --frozen-lockfile`
- `pnpm exec vitest run
server/src/services/recovery/successful-run-handoff.test.ts
ui/src/components/SystemNotice.test.tsx
ui/src/lib/system-notice-comment.test.ts
ui/src/components/IssueChatThreadSystemNotice.test.tsx`
## Risks
- Migration-bearing PR: merge this before any other branch that might
later add a migration.
- The branch touches both recovery services and issue-thread rendering,
so review should pay attention to recovery wake idempotency and comment
metadata compatibility.
## Model Used
- OpenAI GPT-5 Codex via Paperclip `codex_local` adapter, with
shell/git/GitHub CLI 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 run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] If this change affects the UI, I have included before/after
screenshots
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip orchestrates AI agents for zero-human companies
> - The agent live-run route lets operators trigger a manual heartbeat
invocation so an agent can pick up a specific issue or step out of band
> - The current route flow drops the caller's scope (issue/run context)
when forwarding the manual invoke into the heartbeat service, so the
resulting run loses the targeting the operator specified
> - This pull request threads the operator-supplied scope through the
manual invoke path on both the server route and the UI client, with a
regression test that confirms the scope round-trips
> - The benefit is manual heartbeat invokes from the live-run UI
actually pick up the scoped issue/run instead of falling through to the
agent's default routine
## What Changed
- `server/src/routes/agents.ts`: forward the operator-supplied scope
into the manual invoke heartbeat service call
- `server/src/__tests__/agent-live-run-routes.test.ts`: new test
verifying the manual invoke path preserves scope
- `ui/src/api/agents.ts`: pass scope through the live-run client API
## Verification
- `pnpm vitest run --no-coverage
server/src/__tests__/agent-live-run-routes.test.ts`
- `pnpm typecheck` clean
## Risks
Low. The change is purely additive on the route surface — handlers that
did not previously pass scope continue to work; handlers that did pass
it now have it preserved instead of dropped.
## Model Used
Claude Opus 4.7 (1M context)
## 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 run tests locally and they pass
- [x] I have added or updated tests where applicable — new test covers
the preserved-scope path
- [x] If this change affects the UI, I have included before/after
screenshots — N/A (internal API change, no visible UI shift)
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] I will address all Greptile and reviewer comments before
requesting merge
## Thinking Path
> - Paperclip is the control plane for autonomous AI companies.
> - Routines are the scheduled/recurring work surface that keeps a
company operating without manual kicks.
> - Operators need routine edits to be auditable and recoverable,
especially when routines control assignments, prompts, triggers, and
webhook secrets.
> - Documents already have revision-style safety, but routines did not
have equivalent history or restore semantics.
> - This pull request adds append-only routine revisions across the
database, shared contracts, server routes, and board UI.
> - The benefit is safer routine iteration: users can inspect history,
compare changes, restore older definitions, and avoid overwriting newer
edits.
## What Changed
- Added `routine_revisions` storage, latest revision pointers on
routines, shared types, validators, and API docs for routine revision
history.
- Added server service/route support for listing routine revisions,
conflict-aware routine saves, and append-only restore operations.
- Added a History tab on routine detail with revision preview,
structured change summaries, description line diffs, dirty-edit
blocking, restore confirmation, and restored webhook secret surfacing.
- Extracted the line diff helper from `DocumentDiffModal` into
`ui/src/lib/line-diff.ts` for reuse.
- Rebased the branch onto current `public-gh/master` and renumbered the
routine revision migration to `0077_unusual_karnak` after upstream
`0076_useful_elektra`.
- Made the `0077` routine revision migration idempotent so installs that
already applied the branch-local `0076_unusual_karnak` can safely
advance.
- Updated the plugin SDK test harness routine fixture with the new
revision fields required by the shared `Routine` contract.
## Verification
- `pnpm --filter @paperclipai/db run check:migrations` passed.
- `pnpm exec vitest run --project @paperclipai/shared
packages/shared/src/validators/routine.test.ts` passed.
- `pnpm exec vitest run --project @paperclipai/ui
ui/src/lib/line-diff.test.ts
ui/src/components/RoutineHistoryTab.test.tsx
ui/src/lib/workspace-routines.test.ts ui/src/pages/Routines.test.tsx`
passed.
- `pnpm exec vitest run --project @paperclipai/server
server/src/__tests__/routines-service.test.ts --pool=forks
--poolOptions.forks.isolate=true` passed.
- `pnpm exec vitest run --project @paperclipai/server
server/src/__tests__/routines-routes.test.ts --pool=forks
--poolOptions.forks.isolate=true` passed.
- `pnpm --filter @paperclipai/plugin-sdk typecheck` passed after
updating the SDK test harness fixture.
- `pnpm --filter @paperclipai/plugin-sdk build` passed; this refreshed
local generated SDK output needed by plugin example typechecks.
- `pnpm -r typecheck` passed.
## Risks
- Medium migration risk: this adds routine revision storage and
backfills existing routines. The migration is ordered after upstream
`0076` and uses `IF NOT EXISTS` / duplicate-object guards to tolerate
earlier branch-local migration application.
- Restore behavior intentionally appends a new revision instead of
mutating history; callers expecting an in-place rollback need to follow
the new latest revision pointer.
- Restoring webhook triggers recreates webhook secret material, so users
must copy newly surfaced secrets after restore.
- Conflict-aware saves now reject stale routine edits when the client
sends an older `baseRevisionId`.
> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.
## Model Used
- OpenAI Codex, GPT-5-based coding agent, with shell/tool use in a local
git worktree. Exact context-window size is not exposed in this runtime.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] If this change affects the UI, I have included before/after
screenshots
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] I will address all Greptile and reviewer comments before
requesting merge
Screenshots: not attached in this draft PR; the new UI flow is covered
by component tests listed above.
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip orchestrates AI agents for zero-human companies
> - The plugin system is the extension boundary for optional product
capabilities
> - Rich plugins need more than a worker entrypoint: they need scoped
database storage, local project folders, managed agents/routines, host
navigation, and reusable UI components
> - The LLM Wiki work exposed those missing host surfaces while keeping
plugin code outside the core control plane
> - This pull request expands the core plugin host, SDK, server APIs,
and UI bridge so plugins can declare and use those surfaces
> - The benefit is that future plugins can integrate with Paperclip
through documented, validated contracts instead of bespoke server or UI
imports
## What Changed
- Added plugin-managed database namespaces and migration tracking,
including Drizzle schema/migration files and SQL validation for
namespace isolation.
- Added server support for plugin local folders, managed agents, managed
routines, scoped plugin APIs, and plugin operation visibility.
- Expanded shared plugin manifest/types/validators and SDK
host/testing/UI exports for richer plugin surfaces.
- Added reusable UI pieces for file trees, managed routines, resizable
sidebars, route sidebars, and plugin bridge initialization.
- Updated plugin docs and example plugins to use the expanded host and
SDK surface.
## Verification
- `pnpm install --frozen-lockfile`
- `pnpm run preflight:workspace-links && pnpm exec vitest run
packages/shared/src/validators/plugin.test.ts
server/src/__tests__/plugin-database.test.ts
server/src/__tests__/plugin-local-folders.test.ts
server/src/__tests__/plugin-managed-agents.test.ts
server/src/__tests__/plugin-managed-routines.test.ts
server/src/__tests__/plugin-orchestration-apis.test.ts
ui/src/api/plugins.test.ts ui/src/components/FileTree.test.tsx
ui/src/components/ResizableSidebarPane.test.tsx
ui/src/pages/PluginPage.test.tsx ui/src/plugins/bridge.test.ts` passed:
11 files, 67 tests.
- Confirmed this PR changes 89 files and does not include
`pnpm-lock.yaml` or `.github/workflows/*`.
## Risks
- Medium: this expands plugin host contracts across db/shared/server/ui
and includes a new core migration (`0076_useful_elektra.sql`).
- The plugin database namespace validator is intentionally restrictive;
plugin authors may need follow-up affordances for SQL patterns that
remain blocked.
- Merge this before the LLM Wiki plugin PR so the plugin can resolve the
new SDK and host APIs.
> 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 coding agent, tool-enabled shell/git/GitHub
workflow. Context window size was not exposed by the runtime.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] If this change affects the UI, I have included before/after
screenshots
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Summary
- Allow Cloud tenant issue identifiers with alphanumeric prefixes, such
as `PC1897-1`, to normalize as issue references.
- Resolve those identifiers through issue detail/update routes, active
run/live run polling, activity, costs, and `issueService.getById`.
- Keep UI issue-link parsing aligned so tenant links normalize back to
`/issues/<IDENTIFIER>`.
## Root Cause
Cloud tenant issue prefixes include digits from the stack-id hash. The
app-side route normalization still accepted only all-letter prefixes, so
`/api/issues/PC1897-1` skipped identifier lookup and fell through as a
non-UUID id.
## Verification
- `pnpm exec vitest run packages/shared/src/issue-references.test.ts
ui/src/lib/issue-reference.test.ts
server/src/__tests__/issue-identifier-routes.test.ts
server/src/__tests__/activity-routes.test.ts
server/src/__tests__/costs-service.test.ts
server/src/__tests__/agent-live-run-routes.test.ts
server/src/__tests__/issues-service.test.ts`
- `pnpm --filter @paperclipai/shared typecheck && pnpm --filter
@paperclipai/server typecheck`
- `git diff --check`
Co-authored-by: Paperclip <noreply@paperclip.ing>
> **Stacked PR (part 4 of 7).** Depends on:
- PR #5114
- PR #5115
- PR #5116
> Diff against `master` includes commits from earlier PRs in the stack —
the new commit in this PR is the topmost one.
## Thinking Path
> - Paperclip orchestrates AI agents for zero-human companies
> - When creating an OpenCode-local agent, Paperclip currently validates
> `adapterConfig.model` against the *Paperclip host's* `opencode models`
output
> - SSH testing surfaced that this blocks creating an OpenCode agent for
an SSH
> environment: the model that exists on the SSH target isn't visible to
the
> host, so creation fails with "OpenCode requires `adapterConfig.model`
in
> provider/model format" even when the operator picked a real remote
model
> - The initial direction was environment-aware model discovery; the
final
> decision was to keep OpenCode on the same explicit-model pattern as
other
> adapters (default + curated list + manual override) and stop blocking
> creation on host-side discovery
> - This PR does both: the adapter-models endpoint now accepts
`environmentId` and
> probes against the target environment, and the create-time hard gate
is
> replaced by `requireOpenCodeModelId` which validates `provider/model`
*format*
> without requiring host-local discovery. Test/run-time still surfaces
real
> auth/availability problems
> - The benefit is that operators can create OpenCode agents for remote
> environments without out-of-band setup, and the model picker in the UI
> reflects the actually-targeted environment
## What Changed
- Added `requireOpenCodeModelId(input)` in
`opencode-local/src/server/models.ts`,
exported it from the adapter index
- `ensureOpenCodeModelConfiguredAndAvailable` now delegates the format
check to
`requireOpenCodeModelId`
- `agentsApi.adapterModels(companyId, adapterType, { environmentId })`
now accepts
an environment ID and passes it as a query parameter
- `queryKeys.agents.adapterModels` now keys on `(companyId, adapterType,
environmentId)`
- `server/src/routes/agents.ts` reads and validates the new query
parameter,
forwarding it to the adapter's model probe
- `AgentConfigForm.tsx` and `OnboardingWizard.tsx` build the model query
key from
the currently selected default environment ID and disable autodetect for
`opencode_local` (model selection is explicit)
- `NewAgent.tsx` simplified — no longer special-cases OpenCode
autodetect
- `company-portability.ts` no longer needs OpenCode-specific autodetect
handling
- Tests added/updated:
`adapter-model-refresh-routes.test.ts`, `adapter-models.test.ts`,
`agent-permissions-routes.test.ts`,
`opencode-local/src/server/models.test.ts`
## Verification
- `pnpm --filter @paperclipai/server test -- adapter-models
adapter-model-refresh agent-permissions`
- `pnpm --filter @paperclipai/adapter-opencode-local test`
- `pnpm --filter @paperclipai/ui test -- AgentConfigForm
OnboardingWizard NewAgent`
- Manual QA in browser:
1. Boot Paperclip on Tailscale-bound port (so it's reachable from
another
machine), create an OpenCode-local agent, switch the default environment
between two installed sandboxes, and confirm the model list refreshes
per-environment
2. Submit with a malformed `provider/model` string and verify the new
`requireOpenCodeModelId` error surfaces
- Before/after screenshots attached for `AgentConfigForm` model picker
## Risks
- Behavioural shift: switching default environment now triggers a model
refetch.
Should be cheap but introduces a new UI loading state for OpenCode
users.
- Removing dynamic autodetect for OpenCode: if any user configured an
agent
without specifying `model` and relied on autodetect populating it, that
agent
will now fail at submit time. Mitigation: validation error is explicit
and
actionable.
- New query string parameter on `/api/companies/:id/adapter-models` —
older
clients that omit it still work (parameter is optional and defaults to
null).
## Model Used
- OpenAI GPT-5.4 (reasoning effort: high) via Codex CLI
- Provider: OpenAI
- Used to author the code changes in this PR
## 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 run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] If this change affects the UI, I have included before/after
screenshots
- [ ] I have updated relevant documentation to reflect my changes — N/A
- [x] I have considered and documented any risks above
- [x] I will address all Greptile and reviewer comments before
requesting merge
## Thinking Path
> - Paperclip orchestrates AI agents for autonomous companies, and
heartbeat execution is the control-plane loop that keeps assigned work
moving.
> - Max-turn exhaustion is a recoverable local-adapter stop condition
for Claude and Gemini agents when a run needs another heartbeat to
continue safely.
> - The previous behavior could leave max-turn continuation details hard
to inspect, and duplicate/stale continuation wakes could keep running
after issue state changed.
> - The adapter layer also needed to avoid trusting arbitrary
stdout/stderr text as scheduler control metadata.
> - This pull request adds bounded max-turn continuation scheduling,
visible retry state, structured stop metadata handling, and
stale/duplicate continuation guards.
> - The benefit is safer automatic continuation after max-turn stops,
clearer operator visibility, and fewer duplicate or stale agent runs.
## What Changed
- Replaces closed PR #4952, whose head repository was deleted.
- Rebases the recovered max-turn continuation branch onto current
`paperclipai/paperclip:master`.
- Adds max-turn continuation scheduling and retry-state plumbing for
heartbeat runs.
- Adds stale/duplicate continuation suppression when issue status,
ownership, or execution locks change.
- Normalizes Claude/Gemini max-turn detection around structured stop
metadata instead of unstructured stdout/stderr text.
- Surfaces max-turn continuation settings and retry visibility in the
board UI.
- Adds focused server, adapter, and UI tests for max-turn stop metadata,
retry scheduling, stale queued-run invalidation, adapter
parsing/execution, run ledger display, and agent config patching.
## Verification
- `pnpm install --no-frozen-lockfile` to refresh local dependencies
after rebasing onto current `master`.
- `pnpm run preflight:workspace-links && pnpm exec vitest run
server/src/__tests__/claude-local-adapter.test.ts
server/src/__tests__/claude-local-execute.test.ts
server/src/__tests__/gemini-local-adapter.test.ts
server/src/__tests__/gemini-local-execute.test.ts
server/src/__tests__/heartbeat-retry-scheduling.test.ts
server/src/__tests__/heartbeat-stale-queue-invalidation.test.ts
server/src/services/heartbeat-stop-metadata.test.ts
ui/src/components/IssueRunLedger.test.tsx
ui/src/lib/agent-config-patch.test.ts ui/src/lib/runRetryState.test.ts
--testTimeout=20000`
- `pnpm --filter @paperclipai/adapter-claude-local typecheck && pnpm
--filter @paperclipai/adapter-gemini-local typecheck && pnpm --filter
@paperclipai/server typecheck && pnpm --filter @paperclipai/ui
typecheck`
- UI screenshot note: the UI changes are limited to config/ledger state
rendering rather than layout changes; component/unit coverage above
verifies the rendered behavior.
## Risks
- Medium behavior risk: heartbeat retry gating now suppresses max-turn
continuations when issue state or execution locks drift, so any callers
that relied on stale continuations running will now see cancellation
instead.
- Low adapter risk: Claude/Gemini unstructured text no longer triggers
max-turn scheduler metadata, so only structured stop signals and Gemini
exit code 53 are trusted.
- No database migrations.
> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.
## Model Used
- OpenAI Codex coding agent, GPT-5-class model, tool-enabled local
repository editing and command 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 run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] If this change affects the UI, I have included before/after
screenshots (not applicable: state/default rendering only; covered by
component/unit tests)
- [x] I have updated relevant documentation to reflect my changes (not
applicable: no user-facing command or docs contract changed)
- [x] I have considered and documented any risks above
- [x] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip is a control plane for autonomous AI companies where work
must stay observable, governable, and recoverable.
> - The task/heartbeat subsystem owns agent execution continuity, issue
state transitions, and visible recovery behavior.
> - Waiting on an external service is not the same as being blocked when
the assignee still owns a future check.
> - The gap was that agents had no first-class one-shot monitor state
for external-service waits, so recovery could look stalled or require ad
hoc comments.
> - This pull request adds bounded issue monitors that can wake the
owner, clear exhausted waits, and produce explicit recovery behavior.
> - It also surfaces monitor status in the board UI and documents when
to use monitors versus `blocked`.
> - The benefit is clearer liveness semantics for asynchronous waits
without weakening single-assignee task ownership.
## What Changed
- Added issue monitor fields, shared types, validators, constants, and
an idempotent `0075` migration for scheduled monitor state.
- Added server-side monitor scheduling, dispatch, recovery bounds,
activity logging, and external-ref redaction.
- Added board/agent route coverage for monitor permissions and child
monitor scheduling.
- Added issue detail/property UI for monitor state, a monitor activity
card, and Storybook stories for review surfaces.
- Documented monitor semantics and recovery policy behavior in
`doc/execution-semantics.md`.
- Addressed Greptile review feedback by preserving monitor state in
skipped-stage builders and making board monitor saves send `scheduledBy:
"board"`.
## Verification
- `pnpm install --frozen-lockfile`
- `pnpm run preflight:workspace-links && pnpm exec vitest run
server/src/__tests__/issue-execution-policy-routes.test.ts
server/src/__tests__/issue-execution-policy.test.ts
server/src/__tests__/issue-monitor-scheduler.test.ts
server/src/__tests__/recovery-classifiers.test.ts
ui/src/components/IssueMonitorActivityCard.test.tsx
ui/src/components/IssueProperties.test.tsx
ui/src/lib/activity-format.test.ts`
- First run passed 5 files and failed to collect 2 server suites because
the worktree was missing the optional `acpx/runtime` dependency.
- After `pnpm install --frozen-lockfile`, reran the 2 failed suites
successfully.
- `pnpm exec vitest run
server/src/__tests__/issue-monitor-scheduler.test.ts
server/src/__tests__/recovery-classifiers.test.ts`
- `pnpm --filter @paperclipai/shared typecheck && pnpm --filter
@paperclipai/db typecheck && pnpm --filter @paperclipai/server typecheck
&& pnpm --filter @paperclipai/ui typecheck`
- `pnpm exec vitest run
server/src/__tests__/issue-execution-policy.test.ts
ui/src/components/IssueProperties.test.tsx`
- `pnpm --filter @paperclipai/server typecheck && pnpm --filter
@paperclipai/ui typecheck`
- `pnpm exec vitest run
ui/src/components/IssueMonitorActivityCard.test.tsx
ui/src/components/IssueProperties.test.tsx`
- `pnpm --filter @paperclipai/ui typecheck`
- Storybook screenshot captured from
`http://127.0.0.1:6006/iframe.html?viewMode=story&id=product-issue-monitor-surfaces--monitor-surfaces`
with Playwright.
## Screenshots

## Risks
- Medium: this changes heartbeat recovery behavior for scheduled
external-service waits, so regressions could affect wake timing or
recovery issue creation.
- Migration risk is reduced by using `IF NOT EXISTS` for the new issue
monitor columns and index.
- External monitor references are treated as secret-adjacent and are
intentionally omitted from visible activity/wake payloads.
> 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 coding agent with repository tool use and terminal
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 run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] If this change affects the UI, I have included before/after
screenshots or Storybook review surfaces
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip is the control plane for autonomous AI companies.
> - The board UI needs a clear persistent way to move between company
workspaces.
> - The previous layout kept company switching in a separate left rail,
which made the sidebar feel split between workspace selection and
navigation.
> - The workspace switcher belongs in the sidebar header so navigation
and workspace context stay together.
> - This pull request removes the separate company rail from the layout
and turns the sidebar company menu into the primary workspace switcher.
> - The benefit is a cleaner sidebar structure that keeps workspace
identity, switching, company actions, and navigation in one place.
## What Changed
- Removed the standalone `CompanyRail` from the main layout.
- Added the company/workspace switcher to the default, company settings,
and instance settings sidebars.
- Expanded `SidebarCompanyMenu` to list active workspaces, indicate the
current workspace, navigate out of instance settings when switching, and
expose add-company onboarding.
- Updated focused component tests for the new workspace-switcher
behavior.
## Verification
- `pnpm --filter @paperclipai/ui exec vitest run
src/components/SidebarCompanyMenu.test.tsx
src/components/CompanySettingsSidebar.test.tsx`
- `pnpm --filter @paperclipai/ui typecheck`
- `git diff --check`
- Visual smoke attempted against the managed dev server at
`http://127.0.0.1:57385`; a fresh browser context reached the
authenticated sign-in screen, so I could not capture an authenticated
sidebar screenshot from this heartbeat.
## Risks
- Low-to-medium UI risk: this changes the primary sidebar structure and
workspace-switching entry point.
- The instance-settings switch behavior now routes back to the selected
company dashboard when a workspace is selected.
- No migrations, API contracts, or lockfile 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 coding agent, tool-enabled, medium reasoning mode.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [ ] If this change affects the UI, I have included before/after
screenshots
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] I will address all Greptile and reviewer comments before
requesting merge
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip orchestrates AI agents through issue lists and
issue-thread interactions
> - The inbox must preserve nested issue visibility and keyboard
navigation as work decomposes into deeper sub-issues
> - Some UI polish issues made nested rows harder to scan and pending
question cancellation less covered
> - The issue list also had a small test indentation regression around
load-more behavior
> - This pull request tightens nested inbox rendering and related
issue-thread/list polish
> - The benefit is a more reliable operator inbox for multi-level work
trees
## What Changed
- Included nested grandchild issues in inbox keyboard navigation and
recursive row rendering.
- Sort parent rows by descendant activity so active subtrees remain
visible.
- Removed extra inbox card background styling in favor of the page
surface.
- Added regression coverage for pending question cancellation.
- Cleaned up the issue-list load-more test indentation.
## Verification
- `pnpm exec vitest run ui/src/lib/inbox.test.ts
ui/src/components/IssueChatThread.test.tsx
ui/src/components/IssuesList.test.tsx`
- Screenshots were not captured in this PR split; the visible flow is
covered by focused component/helper tests and should get browser QA in
the follow-up issue.
## Risks
- Medium risk: nested inbox rendering and keyboard navigation are
user-visible. The changes are localized to inbox grouping/rendering
helpers and covered by targeted tests.
> 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 coding agent, tool use and local command
execution. Exact context window was not exposed in the runtime.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [ ] If this change affects the UI, I have included before/after
screenshots
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip orchestrates AI agents through reusable execution
workspaces and routines
> - Operators need a fast way to run workspace-aware routines against a
specific execution workspace
> - The existing workspace detail surface showed configuration, runtime
logs, and linked issues, but not routines that depend on workspace
variables
> - Routine runs also needed to prefill the selected execution workspace
so branch variables resolve correctly
> - This pull request adds a workspace routines tab and prefilled
routine-run dialog support
> - The benefit is a tighter workflow for rerunning reviews, smoke
checks, and other workspace-specific routines
## What Changed
- Added an execution workspace `Routines` tab and company-prefixed
routes.
- Listed routines that declare or reference workspace-specific
variables.
- Added `Run now` support that preselects the current execution
workspace in `RoutineRunVariablesDialog`.
- Centralized reusable execution workspace ordering/deduplication for
issue creation and workspace cards.
- Added focused UI helper and dialog regression tests.
## Verification
- `pnpm exec vitest run ui/src/lib/reusable-execution-workspaces.test.ts
ui/src/lib/workspace-routines.test.ts
ui/src/components/RoutineRunVariablesDialog.test.tsx
ui/src/lib/company-routes.test.ts`
- Screenshots were not captured in this PR split; the visible flow is
covered by focused component/helper tests and should get browser QA in
the follow-up issue.
## Risks
- Medium risk: this adds a new workspace detail tab and routine-run
path. It is isolated to workspace-scoped routines and uses existing
routine run APIs.
> 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 coding agent, tool use and local command
execution. Exact context window was not exposed in the runtime.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [ ] If this change affects the UI, I have included before/after
screenshots
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip orchestrates AI agents through issue comments and
heartbeat runs
> - The board UI needs to distinguish a comment that triggered a live
run from comments queued after that run started
> - The run payload already stores comment context, but active-run API
responses did not expose the ids the UI needs
> - Without those ids, the triggering comment can flash as queued while
the agent is already responding to it
> - This pull request exposes live-run comment context and teaches the
optimistic comment helper to ignore the trigger comment
> - The benefit is clearer issue-chat state during comment-triggered
agent interruptions
## What Changed
- Added `contextCommentId` and `contextWakeCommentId` to active/live run
payloads.
- Threaded those ids through server routes, heartbeat summaries, UI API
types, and issue detail rendering.
- Updated optimistic comment classification to avoid marking the
triggering comment as queued.
- Added server and UI regression coverage.
## Verification
- `pnpm exec vitest run
server/src/__tests__/agent-live-run-routes.test.ts
ui/src/lib/optimistic-issue-comments.test.ts`
## Risks
- Low-to-medium risk: adds optional fields to existing run payloads.
Existing consumers should ignore unknown fields, and UI handling is
null-safe.
> 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 coding agent, tool use and local command
execution. Exact context window was not exposed in the runtime.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [ ] If this change affects the UI, I have included before/after
screenshots
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip orchestrates AI agents for zero-human companies
> - Agent heartbeat settings control how much parallel work one employee
can run
> - The previous default of 5 concurrent runs was too restrictive for
active local agent teams
> - The shared default, heartbeat clamp, docs, and route/import/UI
expectations need to agree
> - This pull request raises the default heartbeat concurrency to 20
while keeping explicit headroom up to 50 for power users
> - The benefit is higher throughput for agent teams without each new
agent needing manual runtime config edits
## What Changed
- Raised `AGENT_DEFAULT_MAX_CONCURRENT_RUNS` from 5 to 20.
- Raised the heartbeat service max clamp from 10 to 50, keeping the new
default below the ceiling.
- Updated V1 implementation docs and tests that assert default
imported/exported runtime config.
- Updated the new-agent UI runtime config test to assert the shared
default constant instead of duplicating the numeric value.
## Verification
- `pnpm exec vitest run
server/src/__tests__/agent-permissions-routes.test.ts
server/src/__tests__/company-portability.test.ts
ui/src/lib/new-agent-runtime-config.test.ts`
## Risks
- Medium risk: new agents can consume more local execution capacity by
default. The heartbeat scheduler still respects configured max
concurrency and budget/pause controls, and operators can lower or raise
the per-agent cap within the `1..50` clamp.
> 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 coding agent, tool use and local command
execution. Exact context window was not exposed in the runtime.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] If this change affects the UI, I have included before/after
screenshots
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip orchestrates AI-agent companies through a control plane
that can start, supervise, and recover agent runs.
> - Local adapters are the bridge between Paperclip issues and concrete
agent runtimes such as Claude, Codex, and other ACP-compatible tools.
> - The roadmap calls out broader “bring your own agent” and claw-style
agent support, and ACPX gives Paperclip one path to normalize multiple
ACP agents behind a single adapter.
> - The branch needed to become one reviewable PR against current
`paperclipai/paperclip:master`, without carrying stale base conflicts or
generated lockfile churn.
> - This pull request adds an experimental built-in `acpx_local`
adapter, integrates it through the server/CLI/UI adapter surfaces, and
adds regression coverage for runtime execution, skill sync, stream
parsing, diagnostics, and log redaction.
> - The benefit is that Paperclip can run Claude/Codex/custom ACP agents
through ACPX while keeping operator configuration, skills, logging, and
transcript rendering inside the existing adapter model.
## What Changed
- Added `@paperclipai/adapter-acpx-local` with server execution, config
schema, ACPX session handling, CLI formatting, UI config helpers, and
stdout parsing.
- Registered `acpx_local` across CLI, server, shared constants, UI
adapter metadata, adapter capabilities, and agent creation/editing
surfaces.
- Added ACPX runtime execution support with persistent sessions,
local-agent JWT environment handling, skill snapshots, runtime skill
materialization, and isolation/security regressions.
- Added ACPX adapter diagnostics and marked the adapter experimental in
the UI.
- Added command/env secret redaction for resolved command metadata in
adapter-utils, server event storage, and the Agent Detail invocation UI.
- Added Storybook coverage for ACPX config, transcript rendering, and
skill states, plus PR screenshots under `docs/pr-screenshots/pap-2944/`.
- Rebased the branch onto current `public-gh/master`; `pnpm-lock.yaml`
is intentionally not included and there are no migration/schema changes.
## Verification
- `pnpm exec vitest run
packages/adapters/acpx-local/src/server/execute.test.ts
packages/adapters/acpx-local/src/server/test.test.ts
packages/adapters/acpx-local/src/cli/format-event.test.ts
packages/adapters/acpx-local/src/ui/parse-stdout.test.ts
packages/adapter-utils/src/server-utils.test.ts
server/src/__tests__/redaction.test.ts
server/src/__tests__/acpx-local-execute.test.ts
server/src/__tests__/acpx-local-skill-sync.test.ts
server/src/__tests__/acpx-local-adapter-environment.test.ts
server/src/__tests__/adapter-routes.test.ts
server/src/__tests__/agent-skills-routes.test.ts
ui/src/adapters/metadata.test.ts` — 12 files, 87 tests passed.
- `pnpm --filter @paperclipai/adapter-acpx-local typecheck` — passed.
- `pnpm --filter @paperclipai/server typecheck` — passed.
- `pnpm --filter @paperclipai/ui typecheck` — passed.
- Confirmed PR diff does not include `pnpm-lock.yaml`, database schema
files, or migrations.
Screenshots:



## Risks
- Medium risk: this introduces a new built-in adapter package and
touches runtime execution, adapter registration, agent config, skills,
and transcript rendering.
- ACPX and ACP agent behavior can vary by installed tool versions; the
adapter is marked experimental to set operator expectations.
- `pnpm-lock.yaml` is excluded per repository PR policy, so dependency
lock refresh must be handled by the repo’s automation or maintainers.
- No database migration risk: no schema or migration files changed.
> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.
## Model Used
- OpenAI Codex coding agent based on GPT-5, with repository tool use,
shell execution, git operations, and local verification. Exact hosted
context window was 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 run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] If this change affects the UI, I have included before/after
screenshots
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip is a control plane for autonomous AI companies, where
adapters are the boundary between the board, agents, and execution
runtimes.
> - Local adapters currently expose a primary runtime configuration, but
operators often need a cheaper model lane for routine or low-risk work.
> - That cheap lane has to stay adapter-owned: runtime profile settings
should not mutate the primary adapter config or bypass existing
auth/secret mediation.
> - Issue creation also needs an ergonomic way to request primary,
cheap, or custom model behavior for a selected assignee.
> - This pull request adds a first-class `cheap` model profile contract
across adapter capabilities, heartbeat config resolution, agent
configuration, and issue creation.
> - The benefit is cheaper task execution can be configured and
requested explicitly while preserving adapter boundaries, secret
handling, and audit visibility.
## What Changed
- Added adapter model-profile capability metadata and a `cheap` profile
contract for supported local adapters.
- Applied `runtimeConfig.modelProfiles.cheap.adapterConfig` during
heartbeat config resolution, including requested/applied/fallback run
metadata.
- Added agent configuration UI for cheap model profile settings without
writing those settings into primary `adapterConfig`.
- Added New Issue assignee model lane controls for Primary / Cheap /
Custom and request payload handling.
- Added run ledger profile badges and Storybook stories for the new
cheap-lane UI states.
- Added tests for validators, heartbeat model profile application,
permission/secret mediation, UI payload helpers, and run ledger
rendering.
- Added committed UI verification screenshots under
`docs/pr-screenshots/pap-2837/`.
- Addressed Greptile review feedback around cheap-profile defaults,
shared profile types, and fallback test data.
## Verification
Local:
- `pnpm exec vitest run packages/shared/src/validators/issue.test.ts
server/src/__tests__/adapter-registry.test.ts
server/src/__tests__/agent-permissions-routes.test.ts
server/src/__tests__/heartbeat-model-profile.test.ts
ui/src/components/IssueRunLedger.test.tsx
ui/src/lib/agent-config-patch.test.ts
ui/src/lib/issue-assignee-overrides.test.ts
ui/src/lib/new-agent-runtime-config.test.ts` — passed, 8 files / 103
tests.
- `pnpm exec vitest run ui/src/lib/new-agent-runtime-config.test.ts
ui/src/components/IssueRunLedger.test.tsx` — passed after
Greptile/rebase follow-up, 2 files / 17 tests.
- `pnpm --filter @paperclipai/ui typecheck` — passed after
Greptile/rebase follow-up.
- `pnpm -r typecheck` — passed.
- `pnpm build` — passed.
- `pnpm test:run` — did not complete successfully in this local
worktree: it stopped in pre-existing `@paperclipai/adapter-utils`
sandbox/SSH fixture suites outside this PR diff. Failures were 5s local
timeouts plus `git init -b` unsupported by this machine's Git 2.21.0.
The branch-specific targeted suites above passed.
- Branch was fetched/rebased onto `public-gh/master`; `git rev-list
--left-right --count public-gh/master...HEAD` reports `0 9`.
Remote PR checks on latest head
`e30bf399146451c86cee98ed528d51d33fa5af5a`:
- `policy` — passed.
- `verify` — passed.
- `e2e` — passed.
- `Greptile Review` — passed, confidence score 5/5; Greptile review
threads resolved.
- `security/snyk (cryppadotta)` — passed.
Screenshots:
- [New issue cheap lane
desktop](https://github.com/paperclipai/paperclip/blob/PAP-2837-plan-cheap-model-for-adapters-that-can-support-it/docs/pr-screenshots/pap-2837/newissue-cheap-desktop.png)
- [New issue custom lane
desktop](https://github.com/paperclipai/paperclip/blob/PAP-2837-plan-cheap-model-for-adapters-that-can-support-it/docs/pr-screenshots/pap-2837/newissue-custom-desktop.png)
- [New issue unsupported adapter
desktop](https://github.com/paperclipai/paperclip/blob/PAP-2837-plan-cheap-model-for-adapters-that-can-support-it/docs/pr-screenshots/pap-2837/newissue-unsupported-desktop.png)
- [Run ledger model profile badges
desktop](https://github.com/paperclipai/paperclip/blob/PAP-2837-plan-cheap-model-for-adapters-that-can-support-it/docs/pr-screenshots/pap-2837/runledger-profile-badges-desktop.png)
- Mobile variants are also in `docs/pr-screenshots/pap-2837/`.
## Risks
- Medium: heartbeat config mediation now merges runtime model profiles
into adapter configs, so adapter secret normalization and host-command
restrictions must keep covering nested config paths.
- Medium: the UI adds another issue creation choice; unsupported
adapters must keep hiding the cheap lane and preserve primary behavior.
- Low migration risk: no database migration is included.
> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.
## Model Used
OpenAI Codex coding agent using GPT-5-class reasoning with repo tool use
and command execution. Exact served model/context window was not exposed
by the runtime.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [ ] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] If this change affects the UI, I have included before/after
screenshots
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
## Thinking Path
> - Paperclip's board UI and bundled skills are the operator layer for
configuring agents, routines, issue workflows, and local troubleshooting
loops.
> - The prior rollup mixed this operator polish with database backups,
backend reliability, thread scale, and cost/workflow primitives.
> - This pull request isolates the remaining board QoL, settings,
issue-detail integration, adapter config cleanup, and skills smoke
tooling.
> - It includes some integration-level overlap with the thread and
workflow slices so this branch can run from `origin/master` while still
preserving the full original work.
> - Preferred merge order is the narrower primitives first, then this
integration PR last.
> - The benefit is that reviewers can inspect the user-facing
board/settings/skills layer separately from backend infrastructure
changes.
## What Changed
- Added board/settings polish for agents, routines, company settings,
project workspace detail, and issue detail controls.
- Added agent/routine UI regression tests and New Issue dialog coverage.
- Integrated issue-detail activity/cost/interaction surfaces and leaf
work pause/resume controls.
- Cleaned bundled adapter UI config defaults and onboarding copy.
- Added terminal-bench loop and work-stoppage diagnosis skills plus a
smoke test script.
- Updated attachment type handling and Paperclip skill/API guidance.
## Verification
- `pnpm install --frozen-lockfile`
- `pnpm exec vitest run ui/src/pages/Agents.test.tsx
ui/src/pages/Routines.test.tsx ui/src/components/NewIssueDialog.test.tsx
ui/src/pages/IssueDetail.test.tsx
server/src/__tests__/costs-service.test.ts
server/src/__tests__/issue-thread-interaction-routes.test.ts
server/src/__tests__/issue-thread-interactions-service.test.ts`
- Result: 7 test files passed, 54 tests passed.
- `pnpm run smoke:terminal-bench-loop-skill`
- Result: JSON output included `"ok": true` and `"cleanup": true`.
- UI screenshots not included because verification is focused
component/page coverage for the changed board surfaces.
## Risks
- This is the integration-heavy PR in the split and intentionally
overlaps some component/API primitives with the issue-thread and
workflow PRs so it can run from `origin/master`.
- Preferred merge order: #4859, #4860, #4861, #4862, then this PR last.
If earlier branches merge first, this PR may need a straightforward
conflict refresh in shared UI files.
- The terminal-bench smoke script creates temporary mock issues and
relies on cleanup; the verified run returned `cleanup: 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.5, code execution and GitHub CLI tool use, medium
reasoning effort.
## 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 run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] If this change affects the UI, I have included before/after
screenshots
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip coordinates work through issue-thread interactions, run
history, and cost telemetry.
> - Operators need workflow prompts to be cancellable and costs to be
visible at the issue level.
> - The earlier rollup mixed this workflow/cost work with database
backups, reliability recovery, thread scaling, and settings polish.
> - This pull request isolates the interaction and cost surfaces into a
reviewable slice.
> - The backend now supports cancelling pending question interactions
and summarizing issue-tree costs.
> - The UI component layer can render cancelled questions and interleave
activity with run ledger rows.
## What Changed
- Added `cancelled` as an issue-thread interaction status and result
shape for question interactions.
- Added the board-only `POST
/issues/:id/interactions/:interactionId/cancel` route and service
implementation.
- Added issue-tree cost summary support in the cost service and
`/issues/:id/cost-summary` API route.
- Extended shared cost exports and UI API/query keys for issue cost
summaries.
- Updated `IssueThreadInteractionCard` and `IssueRunLedger` components
for cancelled questions, issue cost surfaces, and activity/run
interleaving.
- Added focused server and component regression coverage.
## Verification
- `pnpm install --frozen-lockfile`
- `pnpm exec vitest run server/src/__tests__/costs-service.test.ts
server/src/__tests__/issue-thread-interaction-routes.test.ts
server/src/__tests__/issue-thread-interactions-service.test.ts
ui/src/components/IssueRunLedger.test.tsx`
- Result: 4 test files passed, 45 tests passed.
- UI screenshots not included because this PR updates reusable
components and API surfaces without wiring a new page-level layout.
## Risks
- Adds a new interaction terminal status; clients that switch
exhaustively on interaction status may need to handle `cancelled`.
- Issue-tree cost summaries use recursive issue traversal and should be
watched on unusually large issue trees.
- Page-level issue detail wiring is intentionally left to the board
QoL/issue-detail branch to keep this PR narrow.
> 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.5, code execution and GitHub CLI tool use, medium
reasoning effort.
## 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 run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] If this change affects the UI, I have included before/after
screenshots
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip's board UI is the operator surface for supervising
AI-agent companies.
> - Issue threads are where operators read progress, respond to agents,
inspect markdown, and jump through long histories.
> - Large threads and rich markdown had become difficult to navigate and
expensive to render.
> - The previous rollup mixed these UI scale fixes with unrelated
backend recovery, costs, backups, and settings changes.
> - This pull request isolates the issue-thread scale and markdown
polish work.
> - The benefit is a reviewable UI slice that can merge independently of
the backend reliability, database backup, workflow, and board QoL PRs.
## What Changed
- Virtualized long issue chat threads and stabilized
anchor/jump-to-latest behavior for large histories.
- Added incremental issue-list row loading and tests for
scroll-triggered pagination behavior.
- Hardened markdown body rendering and markdown editor behavior around
HTML tags, image drops, code-copy UI, and escaped newline handling.
- Added a long-thread measurement harness at
`scripts/measure-issue-chat-long-thread.mjs` plus
`perf:issue-chat-long-thread`.
- Added focused UI/lib regression coverage for thread rendering,
markdown, optimistic comments, and message building.
## Verification
- `pnpm install --frozen-lockfile`
- `pnpm exec vitest run ui/src/components/IssueChatThread.test.tsx
ui/src/components/IssuesList.test.tsx
ui/src/components/MarkdownBody.test.tsx
ui/src/components/MarkdownEditor.test.tsx
ui/src/lib/issue-chat-messages.test.ts
ui/src/lib/optimistic-issue-comments.test.ts`
- Result: 6 test files passed, 170 tests passed.
- UI screenshots not included because this PR is covered by targeted
component tests and does not introduce a new page layout.
## Risks
- Virtualization changes can affect scroll anchoring in edge cases on
very long threads.
- Markdown/editor hardening changes are intentionally defensive, but
malformed content may render differently than before.
> 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.5, code execution and GitHub CLI tool use, medium
reasoning effort.
## 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 run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] If this change affects the UI, I have included before/after
screenshots
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip is the control plane for autonomous AI companies.
> - Reliable backups are part of operating that control plane safely.
> - The previous backup path was public-schema oriented and did not
clearly cover plugin-owned schemas or migration history.
> - Paperclip now has plugin database namespaces and Drizzle migration
state that must survive backup/restore.
> - This pull request expands logical database backups to non-system
schemas and documents the backup boundary.
> - The benefit is safer restore behavior for core and plugin-owned
database state without implying full filesystem disaster recovery.
## What Changed
- Include non-system database schemas in JavaScript and pg_dump backup
paths.
- Preserve enum, table, sequence, index, constraint, migration, and
plugin-schema objects across backup/restore.
- Add restore coverage for plugin-owned schemas and Drizzle migration
history.
- Clarify docs that DB backups are logical database backups, not full
instance filesystem backups.
## Verification
- `pnpm install --frozen-lockfile`
- `pnpm exec vitest run packages/db/src/backup-lib.test.ts`
- Result: 1 test file passed, 4 tests passed.
- Confirmed this PR does not include `pnpm-lock.yaml` or
`.github/workflows/*` changes.
## Risks
- Medium: backup generation touches schema discovery and restore
ordering, so unusual database objects may need additional coverage
later.
- No migrations are included.
> 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 coding agent, tool use enabled, medium reasoning
effort. Exact hosted context-window details are not exposed in this
runtime.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] If this change affects the UI, I have included before/after
screenshots
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] I will address all Greptile and reviewer comments before
requesting merge
Note: no UI changes are included in this PR, so screenshots are not
applicable.
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip orchestrates AI agents for zero-human companies
> - E2B is a sandbox provider plugin that runs agent code in isolated
cloud environments
> - Operators configure E2B through the plugin settings page
> - But the E2B API key configuration was unclear — the settings field
description didn't explain that pasted keys are auto-saved as company
secrets, and the fallback to the host `E2B_API_KEY` variable wasn't
documented
> - Additionally, long-running E2B sandbox commands were timing out
because the plugin environment RPC driver used a fixed timeout, and
environment commands competed for the single foreground command slot
> - This PR clarifies the E2B configuration UX, fixes RPC timeouts for
plugin environment execution, and runs E2B environment commands in
background mode to avoid blocking the foreground slot
> - The benefit is clearer E2B setup for operators and more reliable
sandbox command execution
## What Changed
- Updated E2B plugin manifest and settings UI to clarify API key
configuration — field description now explains that pasted keys are
saved as company secrets and documents the `E2B_API_KEY` host fallback
- Added test coverage for the plugin settings page rendering
- Fixed `plugin-environment-driver.ts` to pass the configured timeout
through to RPC calls instead of using a hardcoded default
- Updated `environment-runtime.ts` to propagate timeout from the
environment lease to the plugin driver
- Changed E2B sandbox command execution to use background handles so
long-running agent commands don't block the foreground slot needed by
the callback bridge
## Verification
- `pnpm test` — all existing and new tests pass
- `pnpm typecheck` — clean
- Manual: navigate to plugin settings, verify E2B API key field shows
the updated description text
- Manual: run an E2B-backed agent task with a long-running command,
verify it completes without RPC timeout
## Risks
- Low risk. Configuration UX change is cosmetic. The timeout fix passes
an existing value through instead of dropping it. Background command
execution is a behavioral change but only affects E2B sandbox commands —
the foreground slot is still available for bridge health checks.
## Model Used
Codex GPT 5.4 high via Paperclip.
## 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 run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] If this change affects the UI, I have included before/after
screenshots
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] I will address all Greptile and reviewer comments before
requesting merge
## Thinking Path
> - Paperclip orchestrates AI agents for zero-human companies
> - Agents run inside environments (local, SSH, E2B sandbox)
> - Operators need to configure and manage these environments
> - But environment settings were buried inside the general company
settings page, making them hard to find
> - Additionally, when testing an agent from the configuration form, the
test always ran locally regardless of which environment was selected
> - This PR moves environments into a dedicated top-level company
settings section and wires the "Test Environment" button to run inside
the selected environment
> - The benefit is operators can find and manage environments more
easily, and the test button now validates the actual environment the
agent will use
## What Changed
- Added a dedicated `CompanyEnvironments` settings page with its own
route and sidebar entry
- Updated `CompanySettingsSidebar` and `CompanySettingsNav` to include
the new environments section
- Modified the agent test route (`POST /agents/:id/test`) to accept an
optional `environmentId` parameter
- Updated all adapter `test.ts` handlers to resolve and use the
specified execution target environment
- Added `resolveTestExecutionTarget` to `execution-target.ts` for remote
environment test resolution with cwd fallback
- Moved the "Test Environment" button and its feedback display into the
`NewAgent` page footer for better UX flow
## Verification
- `pnpm test` — all existing and new tests pass
- `pnpm typecheck` — clean
- Manual: navigate to Company Settings, confirm "Environments" appears
as a top-level section
- Manual: configure an agent with a non-local environment, click "Test
Environment", confirm the test runs inside that environment
## Risks
- Low risk. UI-only routing change for the settings page. The
test-in-environment change adds an optional parameter with a local
fallback, so existing behavior is preserved when no environment is
specified.
## Model Used
Codex GPT 5.4 high via Paperclip.
## 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 run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] If this change affects the UI, I have included before/after
screenshots
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] I will address all Greptile and reviewer comments before
requesting merge
## Thinking Path
> - Paperclip orchestrates AI agents through a company-scoped control
plane.
> - The affected surface is the board UI for issue threads, issue lists,
routines, dialogs, navigation, and issue review indicators.
> - Closed PR #4692 bundled backend, schema, docs, workflow, and UI/QoL
work into one oversized change set.
> - Greptile could not keep reviewing that broad PR because it exceeded
the 100-file review limit and mixed unrelated concerns.
> - This pull request extracts the UI/QoL slice into a fresh branch
under the review limit while leaving workflow and lockfile churn out.
> - The benefit is a focused review path for the board UI performance
and workflow improvements without reopening the oversized PR.
## What Changed
- Added long issue-thread virtualization, scroll-container binding,
anchor preservation, latest-comment jump targeting, and related
regression/perf fixtures.
- Improved issue list scalability with scroll-based loading, server
offset parameters, and pagination-focused UI tests.
- Reduced new issue dialog typing churn and split dialog action
subscriptions so broad layout/nav surfaces avoid unnecessary renders.
- Added routine variables help and routine description mention options
for users, agents, and projects.
- Added productivity review badge/link UI and fixed the badge to use
Paperclip's company-prefixed router link.
- Kept the split PR below Greptile's review limit and excluded
`.github/workflows/pr.yml` and `pnpm-lock.yaml`.
## Verification
- `pnpm install --no-frozen-lockfile` in the clean worktree to install
`@tanstack/react-virtual` locally without committing lockfile churn.
- `pnpm --filter @paperclipai/ui exec vitest run --config
vitest.config.ts src/components/IssueChatThread.test.tsx
src/components/IssuesList.test.tsx
src/components/NewIssueDialog.test.tsx src/pages/Routines.test.tsx
src/pages/Issues.test.tsx` passed: 5 files, 83 tests.
- `pnpm --filter @paperclipai/ui typecheck` passed.
- `git diff --check origin/master..HEAD` passed.
- Split-scope checks: 53 changed files; no `.github/workflows/pr.yml`;
no `pnpm-lock.yaml`.
- Screenshots were not captured in this heartbeat; the changes are
primarily virtualization, routing, pagination, and editor behavior
covered by focused regression tests.
## Risks
- Moderate UI risk because issue-thread virtualization changes scroll
behavior on long conversations; regression tests cover anchor jumps,
latest-comment targeting, row metadata, and short-thread fallback.
- Moderate integration risk because the issue-list offset parameter and
productivity review field depend on matching API behavior.
- Dependency risk: the UI package adds `@tanstack/react-virtual` while
repository policy keeps `pnpm-lock.yaml` out of PRs, so CI must resolve
dependency changes through the repo's normal lockfile policy.
> 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 coding agent, tool-enabled local repository and
GitHub workflow. Exact runtime context window was not exposed by the
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 run tests locally and they pass
- [x] I have added or updated tests where applicable
- [ ] If this change affects the UI, I have included before/after
screenshots
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip is the control plane for autonomous AI companies, so
backend task ownership, recovery, review visibility, and company-scoped
limits need to stay enforceable without UI-only coupling.
> - Closed PR #4692 bundled those backend changes with UI workflow,
docs, skills, workflow, and lockfile churn.
> - PAP-2694 asks for a clean backend/control-plane slice from that
closed branch.
> - This branch starts from current `master` and mines only the `cli`,
`packages/db`, `packages/shared`, and `server` contracts/tests needed
for the backend behavior.
> - It explicitly excludes UI workflow/performance work,
`.github/workflows/pr.yml`, `pnpm-lock.yaml`, docs, skills,
package-script, adapter UI build-config, and perf fixture script
changes; the only UI files are fixture/test updates required by the
tightened shared `Company` contract.
> - The benefit is a smaller reviewable PR that preserves the
control-plane fixes while staying under Greptile s 100-file review
limit.
## What Changed
- Added company-scoped attachment-size limits through DB
schema/migrations, shared company portability contracts, CLI
import/export coverage, and server attachment upload enforcement.
- Added productivity review service/API behavior for no-comment streak,
long-active, and high-churn review issues, including request-depth
clamping and issue summary exposure.
- Hardened issue ownership and recovery/control-plane paths: peer-agent
mutation denial, issue tree pause/resume behavior, stranded recovery
origins, and related activity/test coverage.
- Preserved related backend contract updates for routine timestamp
variables and managed agent instruction bundles because they live in
shared/server contracts from the source branch.
- Addressed Greptile feedback by making `Company.attachmentMaxBytes`
non-optional, simplifying review request-depth clamping, fixing the
migration final newline, and enforcing the process-level attachment cap
as the final ceiling for uploads.
- Added minimal company fixtures needed for repo-wide typecheck/build
and kept the PR to 66 changed files with forbidden/non-slice paths
excluded.
## Verification
- `pnpm install --frozen-lockfile`
- `git diff --check origin/master..HEAD`
- `git diff --name-only origin/master..HEAD | wc -l` -> 66 files
- `git diff --name-only origin/master..HEAD -- .github/workflows/pr.yml
pnpm-lock.yaml package.json doc skills .agents scripts
packages/adapters` -> no output
- `pnpm exec vitest run --config vitest.config.ts
packages/shared/src/validators/issue.test.ts
packages/shared/src/routine-variables.test.ts
packages/shared/src/adapter-types.test.ts
cli/src/__tests__/company-import-export-e2e.test.ts
cli/src/__tests__/company.test.ts
server/src/__tests__/productivity-review-service.test.ts
server/src/__tests__/issue-tree-control-service.test.ts
server/src/__tests__/issue-tree-control-routes.test.ts
server/src/__tests__/issue-agent-mutation-ownership-routes.test.ts
server/src/__tests__/issue-attachment-routes.test.ts
server/src/__tests__/heartbeat-process-recovery.test.ts
server/src/__tests__/issues-service.test.ts` -> 12 files, 147 tests
passed
- `pnpm exec vitest run --config vitest.config.ts
cli/src/__tests__/company-delete.test.ts
cli/src/__tests__/company-import-export-e2e.test.ts
server/src/__tests__/productivity-review-service.test.ts` -> 3 files, 18
tests passed
- `pnpm exec vitest run --config vitest.config.ts
server/src/__tests__/issue-attachment-routes.test.ts` -> 1 file, 6 tests
passed
- `pnpm --filter @paperclipai/db typecheck && pnpm --filter
@paperclipai/shared typecheck && pnpm --filter @paperclipai/server
typecheck && pnpm --filter paperclipai typecheck`
- `pnpm --filter @paperclipai/server typecheck`
- `pnpm --filter @paperclipai/ui typecheck && pnpm --filter
@paperclipai/ui build`
## Risks
- Includes migrations `0073_shiny_salo.sql` and
`0074_striped_genesis.sql`; merge ordering matters if another PR adds
migrations first.
- This is intentionally backend-only apart from fixture/test updates
forced by shared type correctness; UI affordances from PR #4692 are not
present here and should land in separate UI slices.
- The worktree install emitted plugin SDK bin-link warnings for unbuilt
plugin packages, but the targeted tests and package typechecks completed
successfully.
> 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 coding agent, tool-enabled terminal/GitHub
workflow. Exact runtime context window was not exposed by the 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 run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] If this change affects the UI, I have included before/after
screenshots
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip's board UI relies on compact selectors for frequent issue
and agent edits.
> - Inline selectors often live inside larger keyboard-aware surfaces
such as composers and popovers.
> - Arrow, enter, tab, and escape keys handled by the selector should
not leak to parent document shortcuts.
> - Stale company selection should also stay hidden until the company
list confirms it is valid.
> - This pull request tightens inline selector keyboard handling and
adds regression coverage for stale company bootstrap behavior.
> - The benefit is fewer accidental parent interactions and safer
company-scoped UI initialization.
## What Changed
- Added a stable empty `recentOptionIds` default so selector filtering
does not get a new array every render.
- Mirrored highlighted option state into a ref so Enter/Tab commits the
current highlighted option reliably after keyboard navigation.
- Stopped propagation for selector-owned navigation/commit/escape keys.
- Added jsdom regressions for inline selector keyboard handling and
CompanyProvider stale selection behavior.
## Verification
- `pnpm exec vitest run ui/src/components/InlineEntitySelector.test.tsx
ui/src/context/CompanyContext.test.tsx`
- Targeted selector and CompanyProvider tests pass cleanly without React
`act(...)` warnings.
- Screenshots not attached: this is keyboard/state behavior covered by
component tests.
## Risks
- Low risk: changes are scoped to inline selector key handling and
tests. The main behavior shift is intentionally preventing handled
selector keys from reaching parent listeners.
> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.
## Model Used
- OpenAI Codex coding agent based on GPT-5, tool-enabled local
repository and shell access, Paperclip heartbeat context.
## 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 run tests locally and they pass
- [x] I have added or updated tests where applicable
- [ ] If this change affects the UI, I have included before/after
screenshots
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip operators need fast control over the agents running their
company.
> - The sidebar is the persistent place operators scan agent state while
navigating the board UI.
> - Agent pause and resume already exist as control-plane actions, but
sidebar users had to navigate away to use them.
> - This pull request adds a compact per-agent action menu in the
sidebar.
> - It keeps edit, pause, and resume close to the visible agent list
while preserving existing navigation behavior.
> - The benefit is faster operator intervention when an agent needs to
be paused or restarted.
## What Changed
- Refactored sidebar agent rows into a small item component with a
hover/focus action menu.
- Added edit, pause, and resume actions using existing agent API calls
and cache invalidation keys.
- Added success/error toasts for pause and resume mutations.
- Tracked pause/resume pending state per agent so one active mutation
does not disable every sidebar row.
- Disabled direct sidebar resume for budget-paused agents and labeled
that state clearly.
- Added jsdom coverage for active-agent pause, paused-agent resume,
per-agent pending state, and budget-paused resume protection.
- Added visual review artifacts for the default row and opened action
menu:
- [Sidebar row](docs/pr-screenshots/pr-4616/sidebar-agent-row.png)
- [Sidebar action
menu](docs/pr-screenshots/pr-4616/sidebar-agent-actions.png)
## Verification
- `pnpm exec vitest run ui/src/components/SidebarAgents.test.tsx`
- `pnpm --filter @paperclipai/server prepare:ui-dist`
- Browser screenshot pass against a temporary local trusted instance at
`http://127.0.0.1:3102` using Playwright.
## Risks
- Low risk: UI-only addition using existing agent pause/resume
endpoints. The main risk is layout crowding for very narrow sidebars,
mitigated by the icon-only trigger and existing truncation.
- Budget-paused agents now require a non-sidebar path to resume, which
is intentional to avoid accidentally restarting agents stopped by budget
enforcement.
> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.
## Model Used
- OpenAI Codex coding agent based on GPT-5, tool-enabled local
repository and shell/browser access, Paperclip heartbeat context.
## 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 run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] If this change affects the UI, I have included before/after
screenshots
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip orchestrates AI agents for zero-human companies
> - The board UI is the operator’s control surface for selecting the
active company
> - A company id stored in localStorage can become stale across resets,
imports, or deleted companies
> - Exposing that stale id before companies load can briefly put
downstream UI in an invalid company scope
> - This pull request defers selected-company exposure until the loaded
company list validates the stored id
> - The benefit is a cleaner company-selection bootstrap path and fewer
transient invalid API requests
## What Changed
- Initialized `CompanyProvider` selection as `null` until companies
finish loading.
- Reused a stored company id only when it exists in the loaded
selectable company list.
- Cleared storage and selected state when no companies are available.
- Added jsdom regression coverage for stale stored ids before and after
company loading.
## Verification
- `pnpm exec vitest run --project @paperclipai/ui
ui/src/context/CompanyContext.test.tsx`
## Risks
- Low risk. The change only affects selection bootstrap and keeps valid
stored selections intact.
- There may be a slightly longer initial `null` selected-company state
while the company list is loading.
> 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 coding agent, tool-enabled terminal/GitHub
workflow, reasoning mode active. Context window 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 run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] If this change affects the UI, I have included before/after
screenshots
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip orchestrates AI agents for zero-human companies.
> - The issue list and issue detail surfaces summarize child/sub-issue
progress for operators.
> - Those summaries need to be compact and visually consistent because
they appear in dense lists.
> - The progress strip is most useful when there are multiple sub-issues
to compare, so the summary intentionally stays hidden for a single
sub-issue.
> - This pull request tightens the sub-issue progress summary styling
and updates the related tests.
> - The benefit is a cleaner, more scannable task list without changing
task ownership, status, or workflow behavior.
## What Changed
- Adjusted sub-issue progress summary copy/styling in the issue list and
detail summary helpers.
- Intentionally render the progress summary only for two or more child
issues; a single child issue still appears in the normal sub-issue list
without a redundant progress strip.
- Updated the UI tests that assert the rendered summary behavior.
- Clarified the two-plus-child threshold in code with a named constant.
## Verification
- `pnpm exec vitest run --project @paperclipai/ui
ui/src/components/IssuesList.test.tsx
ui/src/lib/issue-detail-subissues.test.ts`
## Screenshots

## Risks
- Low risk; this is a small UI presentation change with focused test
coverage.
- The intentional threshold change means parents with exactly one child
no longer show the aggregate progress strip, avoiding redundant summary
chrome while keeping the child visible in the list.
- No schema or API behavior 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`, coding model with tool use and local command
execution; context window not exposed by the runtime.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] If this change affects the UI, I have included before/after
screenshots
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>