Files
PaperClipAI/server
DottaandPaperclip 09d4ca533e perf(server): return only sidebar failure badge candidates (#17935)
<!-- entire-trail-link-start -->
https://entire.com/gh/paperclipai/-/trails/95/changes/paperclip/95
<!-- entire-trail-link-end -->

## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - The sidebar badge shows failures that need attention.
> - The service reads the newest 200 runs before it selects one run per
agent.
> - Most of those rows cannot contribute to the failure count.
> - This pull request selects failure candidates in SQL and returns four
required fields.
> - The benefit is less database response data with the same badge and
access rules.

## Linked Issues or Issue Description

Refs: #12770. Refs: #17756. Refs: #17683.

These are related load, inbox, and badge reports. This implementation
was written independently from the current service. It does not change
which issue states count as failures.

**What existing behavior does this improve?**

The run read used by the company sidebar badge.

**Current behavior**

Every call transfers up to 200 rows with eight scalar fields. The
application then selects the latest run per agent and applies status,
Mine, dismissal, and access rules.

**Proposed behavior**

SQL selects the same latest run per agent from the same 200-row window.
It then applies status and Mine rules. It returns only the run ID,
scope, issue ID, and creation time. The service still checks dismissals
and current issue access.

**Reason and benefit**

A mixed synthetic fixture reduces the response from 30,689 to 1,297
value bytes. The complete badge response stays equal.

**Breaking changes**

None. The endpoint response and the 200-run window stay the same.

## What Changed

- Keep company and active-agent filters before the existing 200-row
limit.
- Rank each agent by the full creation timestamp in descending order,
then by the observation ordinal. The timestamp order explicitly selects
the newest run. Exact ties retain the first observed row. No ID
tie-break is added.
- Apply failure and Mine filters after ranking. A shared agent's success
cannot expose an older failure.
- Return four fields for dismissal and fresh issue authorization. Supply
the already constrained company ID in the service.
- Add real PostgreSQL regression fixtures and a service-generated SQL
comparison harness. Document the command in `doc/DEVELOPING.md`.

## Verification

- `pnpm -r typecheck` passed. `pnpm build` then passed in a separate
sequential step. The normal Runner verifier checked 1,509 inputs and
1,555 outputs before reuse.
- `PAPERCLIP_SIDEBAR_TEST_DATABASE_URL=<dedicated-test-url> pnpm
--filter @paperclipai/server exec vitest run
src/__tests__/sidebar-badges.test.ts` passed all 14 tests on PostgreSQL
17.11.
- The 10 unchanged `inbox-dismissals.test.ts` route tests passed against
the same real PostgreSQL server through a temporary local adapter for
database startup and cleanup. The adapter was removed. The standard host
run passed the 13 new tests and skipped those 10 embedded-PostgreSQL
tests.
- The first focused attempt could not import the unbuilt plugin SDK. The
normal build supplied that package before the passing reruns.
- `pnpm test:run` was attempted. It recorded 16 embedded-PostgreSQL
startup failures in the unrelated native-session executor suite. Other
database suites also reported startup failures and skipped. The owned
command was then stopped with exit 143. This is incomplete local
full-suite evidence, not a pass. Full remote CI passed on final head
`df5b2c81af43c282d031cc37e6bc867054217df5`: 54 successful checks, 2
neutral checks, and 2 optional Storybook skips; no pending or failed
checks. Fresh exact-head Greptile review is 5/5 with zero unresolved
threads.
- A fixture rollback test reproduced the review finding before the fix:
a failed final approval insert left 2 companies, 44 agents, 2 issues,
and 657 runs. After the fix, the same PostgreSQL constraint error leaves
zero owned rows in all five tables. The test also confirms that more
than 600 runs were written before the deliberate failure.
- Fixture seeding now uses one transaction, including the submillisecond
SQL insert. The 14 focused tests and all 10 benchmark cases pass after
this repair. The server typecheck also passes. Production SQL is
unchanged.
- The final ordering repair passed all 14 real-PostgreSQL tests, all 10
pinned-baseline benchmark cases, and affected server typecheck and
build, run sequentially. The plans now explicitly sort by agent ID, full
creation time descending, then observation ordinal. Complete badge
responses and candidate bindings remain equal.
- One server-test shard was interrupted by its CI runner receiving a
shutdown signal. Its test step was canceled without an assertion
failure. A single targeted retry passed; the aggregate `ci / verify`
gate also passed. No source change was needed for that interruption.
- `git diff --check` passed. No schema, workflow, or lockfile changed.

Reproduce the benchmark with current schema migrations and a dedicated
synthetic database whose name ends in `_test`:

```sh
PAPERCLIP_SIDEBAR_TEST_DATABASE_URL=<dedicated-test-url> PAPERCLIP_LOG_LEVEL=error SIDEBAR_REQUIRE_BYTE_REDUCTION=1 pnpm --filter @paperclipai/server exec tsx ../scripts/benchmark-sidebar-badges.ts --baseline-ref=bf3ef12f3a78d0b15a32ad57f7e16520fbf6cd5e
```

The harness loads the baseline service from that commit. Its
authorization, privacy, and shared helper dependencies are unchanged. It
captures the SQL each service executes. It compares complete badge
responses and every returned candidate field. Fixtures cover more than
200 runs, shared agents, superseding successes, exact and submillisecond
ties, Mine user variants, dismissal equality, private and deleted
issues, missing issue bindings, terminated agents, and cross-company
rows. Tests also cover absent actors, grants, revocation, deletion after
a prior read, and index versus sort plans.

Synthetic local SQL results from PostgreSQL 17.11 on Docker, Node
24.22.1, and postgres.js 3.4.9 follow. Each timing is the median of
seven alternating warm samples after two warmups. The report includes
all samples, ranges, settings, SQL, parameters, and `EXPLAIN (ANALYZE,
BUFFERS, FORMAT JSON)` plans.

| Fixture | Rows before → after | Value bytes before → after | Reduction
| Median ms before → after |
| --- | --- | --- | --- | --- |
| Mixed, company-wide | 200 → 18 | 30,689 → 1,297 | 95.8% | 1.361 →
1.305 |
| Mixed, user-1 | 200 → 9 | 30,689 → 676 | 97.8% | 1.785 → 1.391 |
| Mixed, local-board | 200 → 6 | 30,689 → 414 | 98.7% | 1.463 → 1.232 |
| All failed, 200 distinct latest agents | 200 → 200 | 30,298 → 13,778 |
54.5% | 1.277 → 1.237 |
| All succeeded | 200 → 0 | 30,898 → 0 | 100% | 1.095 → 0.898 |
| Empty | 0 → 0 | 0 → 0 | Neutral | 0.703 → 1.002 |

Value bytes mean UTF-8 non-null cell values before Drizzle decoding.
They exclude PostgreSQL protocol framing and are not wire bytes. The
primary claim is less response data. Warm timing ranges overlap. Earlier
all-failed samples were slower, and the empty control remains slower.
SQL adds ranking work. No production latency improvement is claimed. The
plans keep the limit below both window operations.

## Risks

- SQL adds two bounded window operations. The all-failed control
transfers fewer bytes but can take more SQL time.
- Per-agent ranking explicitly orders by full creation time descending.
Exact timestamp ties retain the observation ordinal. Real PostgreSQL
tests compare complete candidates with the original helper under index
and sort plans.
- Mine rules now also have a SQL form. Tests cover undefined, null,
empty, named, and local-board users, including empty responsible-user
IDs.
- Issue access stays fresh and fail closed. No authorization result is
cached. No migration or stored run change is required.

> 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-6 through Codex. The exact serving model ID and context
window are not exposed in this session. The model used reasoning, code
execution, SQL inspection, and GitHub tools.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-10-27 16:32:09 -05:00
..