paperclip-plugin-plica 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,378 @@
1
+ # Changelog
2
+
3
+ Short-form. The reader-facing write-up for each version is in
4
+ [`docs/releases/`](docs/releases/).
5
+
6
+ ## Unreleased
7
+
8
+ ## 0.5.0
9
+
10
+ - **Published to npm as `paperclip-plugin-plica`.** Install it from Paperclip's
11
+ Plugin Manager (**Install Plugin**, then the package name) or with
12
+ `paperclipai plugin install paperclip-plugin-plica`, and upgrade it with
13
+ `paperclipai plugin upgrade nickallevato.plugin-plica`. The package is
14
+ prebuilt, so there is nothing to clone or build. Installing from a git clone
15
+ with `--local` remains the development route.
16
+ - **Host-duplicate selectors are subtracted in the browser, not at build time.**
17
+ Plica's sheet is now Tailwind's whole output; at injection, Plica reads every
18
+ selector the page's other stylesheets define through the CSSOM and deletes its
19
+ own class rules the host already has, so a stray `.hidden` still cannot pin
20
+ Paperclip in its mobile layout. The build-time version subtracted against
21
+ whichever Paperclip UI build was on the builder's disk, which went stale on
22
+ every Paperclip upgrade and would have made a published package wrong for
23
+ everyone else's Paperclip.
24
+ - **Removed: the "Stylesheet stale" badge and `PLICA_HOST_CSS`.** With nothing
25
+ recorded at build time there is nothing to go stale, so the badge that warned
26
+ about it, the override that pointed the build at a host sheet, and
27
+ `scripts/host-css.mjs` are gone. The build no longer needs a Paperclip UI
28
+ build on disk.
29
+ - **Paperclip upgrades no longer need a Plica rebuild.** The one operational rule
30
+ in the README — rebuild Plica after every Paperclip upgrade, host first — no
31
+ longer applies, and one package no longer carries a particular Paperclip
32
+ version's stylesheet baked into it.
33
+ - **Plica reloads itself after an upgrade.** After a `git pull && pnpm build`
34
+ that bumps the version, a **Reload 0.x.y** chip appears in the header; one
35
+ click has Paperclip re-read the plugin from disk in place, where it used to
36
+ take a DevTools snippet. Instance admins only. A new version that adds a
37
+ capability shows **Reinstall needed** instead, since Paperclip only grants
38
+ capabilities on a fresh install.
39
+ - **"Recent" replaces the live strip.** The full-width row of pills across the
40
+ top of the page is gone; the left column now carries **Recent** — one line per
41
+ task, live ones first and newest first, with the same pulsing dot on the rows
42
+ an agent is on right now and the task's own status glyph on the rest. A row is
43
+ as wide as the column, so the ticket title fits, and a task that has just
44
+ finished stays in place instead of vanishing with its run. The list scrolls
45
+ inside a capped height, so a run starting cannot shove Portfolio or Routines
46
+ down the page.
47
+ - **Fixed: the board no longer overflows the viewport at phone width.** Recent's
48
+ rows were as wide as their untruncated ticket titles, which widened the
49
+ single-column grid past the window and clipped every pane in it on the right —
50
+ Orgs lost its runs/day and Need-you figures, and its header lost `need you`.
51
+ Every panel now has a floor of zero width, so the column is the window again
52
+ and the titles truncate.
53
+
54
+ ## 0.4.0
55
+
56
+ ### On a phone
57
+
58
+ - **Queue rows stay readable at phone width.** A row's actions drop to their
59
+ own line under the title instead of crushing it to "Budget…" and the meta to
60
+ one word per line; the Today / This week / Whenever picks come back there
61
+ instead of hiding.
62
+
63
+ ### Decide by
64
+
65
+ - **"Orgs", not "companies"**, everywhere Plica labels them: the list, the
66
+ queue and portfolio grouping, the header ("all orgs").
67
+
68
+ - **The queue owns the page.** The wide company ledger is now a compact
69
+ **Orgs** list in the left column — name, who is working, runs per day,
70
+ and Need you — so the queue takes the main column. Every figure the ledger
71
+ showed (Questions, Blocked, Review, Open, Tokens, the run sparkline, the
72
+ lead agent) is in a card on hovering a company's name, with Watch and Open.
73
+ Narrow, the page stacks Orgs, the queue, then Portfolio and Routines.
74
+
75
+ - **The queue is grouped by when you'll decide.** Today, Unsorted, This week,
76
+ Alerts, Whenever and Snoozed replace Now / Soon / Later as the default
77
+ grouping (Severity is still one click away). The lanes come from Paperclip's
78
+ own decision triage, so a day set in Plica is the day Paperclip's Decisions
79
+ page shows.
80
+ - **Unsorted rows sort in one click**: Today · This week · Whenever sit inline.
81
+ - **Every row has a triage menu**: decide by, snooze (1 hour to a week), wake,
82
+ archive. Changes apply immediately and are rolled back with a toast if
83
+ Paperclip refuses them.
84
+ - **Snoozed items are away.** They sit folded in the Snoozed lane and nowhere
85
+ else, and they no longer count toward the Needs you badge.
86
+ - The header reads *N today (N overdue) · N unsorted · N snoozed*.
87
+ - The demo fixture now carries realistic triage (it used to store an instant
88
+ where Paperclip stores a preset or a date), and demo mode accepts the triage
89
+ and archive writes.
90
+
91
+ ### A calmer board
92
+
93
+ - **Colour marks one thing per row.** The ledger's Questions, Blocked and
94
+ Review counts are plain ink now; only **Need you** carries ochre or brick.
95
+ Every nonzero count used to be ochre, so one question read as loudly as five
96
+ things waiting on you.
97
+ - **Critical rows in Now carry an edge, not a band.** A faint tint and a brick
98
+ edge replace the solid pink (maroon in dark mode) fill.
99
+ - **Moving work and good runs are sage, not neon.** Portfolio bars, the
100
+ sparkline and answered cards use Plica's new low-chroma `ok` colour.
101
+ - **Liveness is Paperclip's own blue**, the same one its nav dots and
102
+ in-progress icons use.
103
+ - **The ledger no longer clips its last column.** The totals row was padded
104
+ wider than the rows above it, which pushed the pin and open icons out of the
105
+ card. The sparkline now hides when the ledger is narrow rather than when the
106
+ window is, and the board stacks (ledger and queue first) when there isn't
107
+ room for both columns.
108
+ - **One toggle style.** Hot first / My order, Trouble / Company and the queue
109
+ grouping all draw like Paperclip's tabs.
110
+ - "Expected every every 1h" on an overdue heartbeat now reads "expected every 1h".
111
+
112
+ ### Paperclip v2026.916.0
113
+
114
+ - **In-progress tasks draw Paperclip's new spinner glyph.** The host replaced the
115
+ rotate arrow with an open circle that turns (and holds still under reduced
116
+ motion);
117
+ Plica's copy of the glyph had fallen behind, so the same task looked
118
+ different in Plica and in Paperclip.
119
+ - **Links into `/tasks` and `/chats` keep their company.** Both are new
120
+ company-scoped routes; Plica did not know them, so a link into one could lose
121
+ its company prefix.
122
+ - **Dialogs respect reduced motion**, as Paperclip's now do.
123
+ - **`pnpm check:vendored`** reports any vendored host file that has drifted from
124
+ the Paperclip checkout. Nothing caught the two above until now.
125
+
126
+ ### The mark
127
+
128
+ - **Plica has its own icon.** A sheet folded down its middle, seen end-on — a
129
+ *plica*, which reads flat as a caret. It replaces the two stock Lucide icons
130
+ that stood in for it: the Telescope on the breadcrumb-bar button and the
131
+ Layers glyph beside the page title. Both surfaces now show the same mark, so
132
+ the button in the host's chrome and the page it opens are recognisably the
133
+ same thing.
134
+ - Drawn as an SVG component rather than shipped as the supplied PNG, so it
135
+ inherits `currentColor` and tracks the text beside it through hover, light
136
+ mode and dark mode. The source artwork and its geometry are kept in
137
+ [`docs/brand/`](docs/brand/) for anyone who has to redraw it.
138
+
139
+ ### The stylesheet coupling
140
+
141
+ - **Plica now warns when it needs rebuilding after a Paperclip upgrade.** Plica
142
+ filters its own utilities against Paperclip's compiled stylesheet once, at
143
+ build time; upgrade Paperclip and that filtering goes stale, and Plica's
144
+ leftover duplicates can override Paperclip's responsive rules and strand the
145
+ whole application in its mobile layout. The symptom shows up in Paperclip's
146
+ own chrome with nothing pointing at Plica, and the fix — `pnpm build` in the
147
+ Plica checkout — is not one anyone would guess. A **Stylesheet stale** badge
148
+ now appears in the Plica header, and its hover text names the fix and shows
149
+ the stylesheet Plica was built against next to the one currently loaded.
150
+ - **It stays quiet unless it is sure.** If the host's stylesheet cannot be
151
+ identified from the page, Plica shows nothing rather than a warning it cannot
152
+ stand behind. Nothing about rendering, data or demo mode changes either way.
153
+
154
+ ### Documentation
155
+
156
+ - **A documentation set with real screenshots**, in [`docs/`](docs/): install,
157
+ configuration, the board, the queue, and troubleshooting. The README is now an
158
+ overview that hands off to them rather than the only page there is.
159
+ - Screenshots are **generated**, by `scripts/capture-screenshots.mjs`, from a
160
+ running instance in demo mode. Refreshing them after a UI change is one
161
+ command instead of a manual session with a cropping tool, which is the only
162
+ version of "keep the screenshots current" that survives contact with a
163
+ release. See [`docs/screenshots/README.md`](docs/screenshots/README.md).
164
+ - Removed the README's claim that a classic layout is available behind a toggle.
165
+ The classic views were retired in `e40e459`; the line documented a feature
166
+ that does not exist.
167
+ - `demoDataUrl` is now documented as accepted-and-ignored on the settings page.
168
+ It is declared in the manifest and rendered by the host, but nothing reads it.
169
+ - Corrected two `PLUGIN_SPEC.md §24` citations in `src/manifest.ts` and
170
+ `src/ui/host/api.ts`. §24 is *Operator UX*; the statement they lean on is in
171
+ the spec's *Current implementation caveats*.
172
+ - Marked the plugin-migration plan as completed and corrected its description of
173
+ what shipped — a `globalToolbarButton` slot, not a navigate launcher in the
174
+ sidebar zone.
175
+
176
+ ## 0.3.0
177
+
178
+ ### Demo mode
179
+
180
+ - **Plica can now serve the whole HUD from a bundled fixture instead of the
181
+ live instance**, so the page can be shown or screenshotted without exposing
182
+ real company names, ticket titles or agent narration. Four invented companies
183
+ with ~40 tickets, ~22 agents, live runs, an overdue routine and a company in
184
+ the red — sized so every surface has something to draw rather than reading as
185
+ an empty prototype.
186
+ - Two switches: `?demo=1` on the URL (sticky for the browser session, and
187
+ superseded as soon as the setting is changed) and a **Demo mode** checkbox
188
+ on the host's plugin settings page,
189
+ which Paperclip renders from the new `instanceConfigSchema` in the manifest.
190
+ Host plugin config is company-scoped and Plica is not, so the box ticked for
191
+ any one company turns the whole page into a demo.
192
+ - The substitution happens at one seam — `request()` in `src/ui/host/api.ts` —
193
+ which every read Plica performs already funnels through. No component knows
194
+ demo mode exists, and none can leak real data by forgetting about it. Writes
195
+ are intercepted too, so approving an approval or answering an interaction
196
+ works in a walkthrough without reaching the server.
197
+ - Fails closed. The page renders a placeholder until the mode is resolved and
198
+ an error if the fixture cannot be loaded, rather than showing real data for a
199
+ frame or falling back to it silently. A **Demo data** badge sits in the
200
+ header the whole time it is on.
201
+ - Addressed by the plugin's row UUID, looked up from `/api/plugins`. The
202
+ plugin-**key** form of the asset route 500s: its `getById` guard reads
203
+ `error.code` while drizzle puts the Postgres `22P02` on `error.cause`, so a
204
+ non-UUID id escapes the guard instead of falling through to `getByKey`.
205
+ - The fixture is a real file at `dist/ui/demo-data.json`, so renaming a company
206
+ is an edit and a reload. Structural changes go through
207
+ `scripts/gen-demo-data.mjs` (`pnpm demo:data`); a test asserts the two agree.
208
+ Timestamps are relative tokens resolved at page load, so the demo never reads
209
+ as months stale.
210
+
211
+ ### Layout
212
+
213
+ - **Live now moved out of the rail and onto a strip across the top of the
214
+ board.** It was the one block whose height tracked the size of the fleet, so
215
+ every run that started or finished shoved the lists below it down the page.
216
+ As a single row of pills its height cannot change at all, and nothing below
217
+ it ever moves. Running agents are a glance, not a list you work through.
218
+ - Each run is now a pill: company, ticket, agent, elapsed. The title and the
219
+ agent's narration moved into the hover, where they cannot wrap a pill onto a
220
+ second line and change the strip's height.
221
+ - No cap and no "+N more" line — the row scrolls sideways, so the header count
222
+ and what you can reach always agree.
223
+ - The rail is a sticky column **capped** at the viewport rather than pinned to
224
+ a fixed height. The fixed height had to guess how much chrome sat above it
225
+ and guessed high, which pushed Routines off the bottom of the screen.
226
+ Routines is now `shrink-0`: it is the one thing that can never be squeezed
227
+ out of view, and Portfolio scrolls inside itself only once the column would
228
+ otherwise overflow.
229
+
230
+ ### Portfolio (was Projects)
231
+
232
+ - The projects rail is now a chart, not a list. The folds, the company
233
+ grouping, the `most open` / `least open` sort and the per-project deep links
234
+ are gone — what people actually read off that rail was the shape of the bars,
235
+ and the rows were not being clicked.
236
+ - One bar per project across every company, **scaled to the largest project**
237
+ so lengths compare down the column rather than only within a row. Three
238
+ segments now: moving, **waiting**, blocked — waiting is the untouched
239
+ remainder the old two-segment bar left as bare track and therefore never
240
+ named.
241
+ - Ordered worst-first (latest overdue → most stuck → biggest), not by deadline.
242
+ A chart read at a glance must put the worst bar under the eye first.
243
+ - **Trouble / By company** toggle in the header, persisted in
244
+ `plica.portfolioSort`. Company order follows the board's own — watched first,
245
+ then hot-first or the sidebar order — so a company sits in the same place in
246
+ both panes, and the worst project still leads inside each company. Gathering
247
+ the bars is the only way to see that one company's whole portfolio is stuck,
248
+ which trouble-order scatters down the column.
249
+ - In company order each block is headed by the company's name and its own
250
+ open / blocked / late figures. The header is **sticky**, because a block can
251
+ be taller than the pane and scrolling past the name would leave a run of bars
252
+ with nothing saying whose they are. The per-row company icon drops away in
253
+ that mode — the header already answers it — and stays in Trouble order, where
254
+ consecutive bars have no shared owner to head.
255
+ - `plica.projectGrouping` and `plica.projectSort` retired; both are cleared on
256
+ load with the other legacy keys.
257
+
258
+ ### Routines
259
+
260
+ - Replaced the week's timetable with **exceptions only**: failed, wedged on a
261
+ blocked issue, or overdue. A schedule you can predict is not information —
262
+ the old list spent its whole height saying twenty routines would fire on
263
+ time and gave the two that broke the same weight as the rest.
264
+ - Healthy routines are a count in the footer. When everything is healthy the
265
+ block is one reassuring line; with no routines at all it says so distinctly.
266
+ - A routine that both failed and ran late is one problem, filed under the
267
+ failure, which is the half that says why.
268
+
269
+ ### Needs you
270
+
271
+ - Age filter chips in the rail header: **All / Today / Yesterday / Last week /
272
+ Old**, persisted in `plica.queueAgeFilter`. A rail carrying a hundred-odd
273
+ items is a wall you stop reading, and the oldest things on it are the least
274
+ likely to still matter.
275
+ - Buckets are **calendar days cut at local midnight**, not rolling hours — an
276
+ item raised at 9pm last night is yesterday's at 8am today. The Age *grouping*
277
+ now uses the same buckets, so the two can never disagree about which pile an
278
+ item is in.
279
+ - Each chip carries the count of the **unfiltered** queue, so an empty bucket
280
+ is distinguishable from a hidden one. Empty buckets stay visible but disabled
281
+ rather than disappearing, so the chips never move under the cursor as items
282
+ age past midnight.
283
+ - Under an age filter, **Later starts open**: narrowing to "Old" is an explicit
284
+ request for that slice, and a rail whose only match is folded away reads as
285
+ empty.
286
+ - The rail's badge and "oldest" now describe what is on screen, not the queue
287
+ behind the filter.
288
+ - Age sort remains a toggle in the rail header (`oldest` / `newest`), persisted
289
+ in `plica.queueSort`. Severity still decides the order first — the toggle only
290
+ flips which of two equally urgent items leads, so grouping by Severity with
291
+ newest first works as one view.
292
+ - Every group in the rail folds from its header, not just Later.
293
+
294
+ ### Internal
295
+
296
+ - `PlicaListControls` factored out of the queue's own header. `PlicaFoldGroup`
297
+ and `PlicaCompanyGroup` are gone with the rail they served — the Portfolio
298
+ chart and the routine exceptions have nothing to fold.
299
+ - The projects rail's grouping and sorting helpers (`groupProjects`,
300
+ `compareProjectEntries`, `projectDueBucket`, `projectHealth`) removed with it;
301
+ a chart ordered worst-first has nothing left for them to choose between.
302
+ - Test timezone pinned to UTC. The queue's age buckets cut at *local* midnight
303
+ by design, so the suite has to agree on which local.
304
+
305
+ ### Project
306
+
307
+ - **Requests have one path now**, written down in `docs/intake.md`: capture,
308
+ deduplicate, clarify, scope, propose a priority, build, review, merge,
309
+ changelog. Two steps are the repository owner's — the priority band and the
310
+ merge — and the document says so rather than leaving it to be discovered.
311
+ - Three issue templates (feature request, bug report, core limitation) produce
312
+ the *Problem / Proposed scope / Acceptance criteria / Open questions* shape
313
+ triage needs, so triage fills gaps instead of restructuring prose. Blank
314
+ issues stay enabled on purpose.
315
+ - The **core limitation** template is the sanctioned exit from the
316
+ no-core-changes rule and the only one: it asks which extension point comes
317
+ closest, why it falls short, and what Plica ships in the meantime — then parks
318
+ the work for the owner rather than routing around core.
319
+ - A state label is now mandatory on every issue, because an issue with no state
320
+ label is the one failure mode nothing else in this repository has an alarm on.
321
+
322
+ ## 0.2.0
323
+
324
+ ### Board
325
+
326
+ - Rebuilt the company ledger around what is waiting on you. Columns are now
327
+ `Need you · Questions · Blocked · Review · Open · Runs/d · Tokens`, with the
328
+ first four counted off the attention feed so they can never disagree with
329
+ their own total. Routines and Spend columns dropped — routines duplicate the
330
+ rail beside the board, and dollars were the least actionable figure on the row.
331
+ - **Capacity strip** replaces the old `0 / 4` count: one square per agent, in
332
+ org order (chief first, each manager followed by their reports), showing
333
+ working / queued / stalled / error / idle. A run that holds a runner while
334
+ reporting nothing for 20 minutes now reads as stalled — previously invisible.
335
+ - Agents that cannot take work (paused, terminated, pending approval) no longer
336
+ occupy squares; drawing them as idle overstated available capacity.
337
+ - Heat is computed and used to order the board, and deliberately never drawn.
338
+ - SCADA-influenced palette: `--plica-live` / `--plica-wait` / `--plica-alarm` /
339
+ `--plica-rest`, low-chroma, one meaning each. Company brand colour is the only
340
+ saturated thing on the board and never encodes a value.
341
+ - Header totals removed, along with the per-company summary fan-out behind them.
342
+
343
+ ### Rails
344
+
345
+ - Routines and Projects group by company, following the board's own company
346
+ order, each foldable to a one-line summary. No more `+n more` truncation.
347
+ - Routines read in calendar order, Sunday through Saturday, and colour by where
348
+ each sits in its cycle: warming toward live over the day before it fires, live
349
+ for the hour it runs, then straight back to rest.
350
+ - Every routine carries an outcome mark for its last run (ok / blocked / failed /
351
+ working / skipped) that links to the issue that stopped it.
352
+
353
+ ### Fixed
354
+
355
+ - **Host navigation could wedge while Plica was open** — the URL changed and the
356
+ view never followed. `needsBreakdown` was rebuilt on every render while sitting
357
+ in an effect dependency array, producing a render loop that starved React
358
+ Router 7's `startTransition`. Memoised, and every `?? []` fallback in the same
359
+ hook replaced with shared constants to close the whole class.
360
+ - Radix modal dialogs unmounted by a queue refresh could strand
361
+ `pointer-events: none` on `<body>`, disabling every click in the app.
362
+
363
+ ### Performance
364
+
365
+ - Polls paced per dataset (5s live, 15s approvals, 30s agents, 60s issues and
366
+ projects) instead of everything at 5s, and none refetch in a hidden tab. The
367
+ issue list — the heaviest payload on the page — was being refetched twelve
368
+ times a minute per company.
369
+ - Dropped the sidebar-badges query entirely; nothing had rendered it since the
370
+ Inbox column came out.
371
+
372
+ ### Removed
373
+
374
+ - The CEO nudge. An overdue heartbeat now offers "Open CEO" instead.
375
+
376
+ ## 0.1.0
377
+
378
+ Initial release.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Nick Allevato
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,213 @@
1
+ # Plica
2
+
3
+ A cross-org HUD for [Paperclip](https://github.com/paperclipai/paperclip). One page that
4
+ answers "what needs me, across every org, right now" — and lets you say *when* you will deal
5
+ with each thing — instead of visiting each org's dashboard in turn.
6
+
7
+ Plica is UI-only. It contributes one page (mounted at `/:companyPrefix/plica`) and a toolbar
8
+ launcher that navigates there. Its worker is a deliberate no-op.
9
+
10
+ ![The Plica page: Orgs, portfolio and routines at left; the Needs-you queue, grouped by when you'll decide, owning the main column](docs/screenshots/plica-page.png)
11
+
12
+ > Every screenshot in this repository is Plica's own [demo mode](docs/configuration.md#demo-mode) —
13
+ > invented orgs, tickets and agents, not a real instance. Try it on your own install: add
14
+ > `?demo=1` to the Plica page's URL (e.g. `/ACME/plica?demo=1`). Nothing you click in demo mode
15
+ > reaches the server.
16
+
17
+ **Documentation:** [install](docs/install.md) · [configuration](docs/configuration.md) ·
18
+ [the board](docs/board.md) · [the queue](docs/queue.md) ·
19
+ [troubleshooting](docs/troubleshooting.md) · [release notes](docs/releases/)
20
+
21
+ ## What it shows
22
+
23
+ **Queue** — the main column. Every item across every org that wants a human, in one list:
24
+ approvals, questions and confirmations awaiting a response, blockers, failed runs, overdue
25
+ heartbeats and routine exceptions. Actions are inline — Approve, Reject, Reply — so you rarely
26
+ need to open the org.
27
+
28
+ By default it groups by **when you said you'd decide**: Today (and anything overdue), Unsorted,
29
+ This week, Alerts, Whenever and Snoozed. New items land in Unsorted with **Today · This week ·
30
+ Whenever** right on the row, and every row has a menu to set a day, snooze or archive it. These
31
+ are Paperclip's own decision-triage records, so a day set in Plica is the day Paperclip's
32
+ Decisions page shows — and it works on stock Paperclip, through its existing web API.
33
+
34
+ ![The queue grouped by when you'll decide: Today, Unsorted, This week](docs/screenshots/queue-by-decide.png)
35
+
36
+ It can also group by severity, org, kind, project or age — by project is how you find the one
37
+ project quietly generating half the noise.
38
+
39
+ **Orgs** — the left column: one line per org with who is working, runs per day and how much is
40
+ waiting on you. Hover a name for everything else (questions, blockers, review, open work, token
41
+ burn, a sparkline of recent runs, the lead agent). Orgs can be watched, sorted by heat, and
42
+ clicked to filter the queue.
43
+
44
+ ![An org's detail card: every figure the line leaves out](docs/screenshots/company-detail.png)
45
+
46
+ Each org's capacity is a row of squares, one per agent — working, stalled, queued, errored or
47
+ idle. Hovering one says who it is and what they are actually doing, which is the thing a bare
48
+ "3 running" count cannot tell you.
49
+
50
+ ![A capacity square's hover card: the agent, their ticket, and what they are doing right now](docs/screenshots/capacity-hover.png)
51
+
52
+ **Portfolio and Routines** — projects by how much is moving, waiting or blocked, and the
53
+ routines that failed or stopped firing.
54
+
55
+ **Briefing** — what changed since your last visit.
56
+
57
+ **On a phone** — the page stacks Orgs, the queue, then Portfolio and Routines, and each queue
58
+ row puts its actions on a line of their own.
59
+
60
+ <img src="docs/screenshots/phone.png" alt="Plica at phone width: the Orgs list above the queue" width="320">
61
+
62
+ Full tours: [the board](docs/board.md), [the queue](docs/queue.md).
63
+
64
+ ## Install
65
+
66
+ Plica is published to npm as `paperclip-plugin-plica`. In Paperclip, open the **Plugin
67
+ Manager** (Settings → Plugins, at `/company/settings/instance/plugins`), click **Install
68
+ Plugin**, and enter `paperclip-plugin-plica` as the **npm Package Name**. Or from the CLI:
69
+
70
+ ```bash
71
+ npx paperclipai plugin install paperclip-plugin-plica
72
+ ```
73
+
74
+ Either way needs an instance admin. The package is prebuilt and carries nothing tied to a
75
+ particular Paperclip build, so upgrading Paperclip later needs nothing from Plica.
76
+
77
+ ### From source
78
+
79
+ For working on Plica, install it from a clone instead:
80
+
81
+ ```bash
82
+ git clone https://github.com/nickallevato/paperclip-plica.git
83
+ cd paperclip-plica
84
+ pnpm install
85
+ pnpm build
86
+ npx paperclipai plugin install /absolute/path/to/paperclip-plica --local
87
+ ```
88
+
89
+ [docs/install.md](docs/install.md) covers both routes: upgrading, the directory layout a clone
90
+ needs for its `link:` dependencies, and how to check the plugin actually loaded.
91
+
92
+ ## Demo mode
93
+
94
+ Plica can serve the whole HUD from a bundled fixture instead of your instance, so the page can
95
+ be screenshotted or demoed without putting real company names, ticket titles or agent chatter
96
+ on screen. Turn it on with `?demo=1` or the **Demo mode** checkbox on the plugin's settings
97
+ page; the header carries a **DEMO DATA** badge the whole time it is on. It fails closed — a
98
+ fixture that will not load is an error, never a silent fall back to real data.
99
+
100
+ Details, including how to edit the fixture:
101
+ [docs/configuration.md](docs/configuration.md#demo-mode).
102
+
103
+ The screenshots in `docs/screenshots/` are demo mode with nothing else done to them, captured by
104
+ `scripts/capture-screenshots.mjs`. See [docs/screenshots/README.md](docs/screenshots/README.md).
105
+
106
+ ## Why Plica subtracts the host's selectors
107
+
108
+ Plica compiles its own Tailwind sheet and injects it via `<style>` appended to `<head>` — after
109
+ the host's. Tailwind emits every class it scans, including ones Paperclip already defines, and a
110
+ duplicate that lands later wins on document order. A stray `.hidden{display:none}` is enough to
111
+ beat Paperclip's `@media(min-width:40rem){.sm\:flex{...}}` and pin the whole app — not just the
112
+ Plica page — in its mobile layout.
113
+
114
+ Ordering cannot fix this, in either direction. Appended last, Plica's duplicates beat the host's
115
+ responsive variants. Inserted first, Plica's `@layer` declarations come before the host's, which
116
+ pushes the host's `base`/`components` layers after `utilities` and breaks spacing app-wide.
117
+ Subtraction is the only approach that works.
118
+
119
+ So at injection, `src/ui/styles.ts` walks the document's other stylesheets through the CSSOM,
120
+ collects every selector they define, and deletes each of Plica's class rules the host already
121
+ has — trimming shared selector lists and dropping `@media`/`@layer`/`@supports` groups left
122
+ empty (`src/ui/lib/host-subtract.ts`). Against Paperclip's real sheet that drops about 480
123
+ selectors; Plica-only classes are untouched. Cross-origin sheets, which the browser will not let
124
+ a page read, are skipped — they are web-font CSS, not host utilities.
125
+
126
+ This used to be done once, at build time, against whichever Paperclip UI build was on the
127
+ builder's disk — which went stale on every Paperclip upgrade and meant rebuilding Plica after
128
+ each one. Subtracting against the sheets the page actually loaded leaves nothing to go stale: a
129
+ Paperclip upgrade needs no Plica rebuild, the build needs no Paperclip UI build, and one
130
+ published package fits whichever Paperclip stylesheet it lands next to.
131
+
132
+ ## Vendored host components
133
+
134
+ `src/ui/host/` holds read-only copies of Paperclip internals Plica depends on — the ui-kit
135
+ primitives, `useCompanyOrder`, API client shapes. They are copies rather than imports because
136
+ Paperclip does not export them to plugins.
137
+
138
+ Keep them byte-identical to their upstream originals apart from import paths. Three documented
139
+ exceptions:
140
+
141
+ - `ui-kit/dialog.tsx` — plain Tailwind positioning, since the host's version leans on theme-only
142
+ CSS variables the plugin sheet does not carry.
143
+ - `useCompanyOrder.ts` — read path only; the host's mutation and `persistOrder` are omitted
144
+ because Plica never reorders.
145
+ - `ui-kit/hover-card.tsx` — a Plica original, not a copy. The host ships no HoverCard component,
146
+ so there is nothing upstream to keep it identical to.
147
+
148
+ Anything else that drifts is a bug. `CompanyPatternIcon.tsx` in particular must match exactly:
149
+ Paperclip draws company avatars with its own copy, so any change here gives one company two
150
+ different identities on screen.
151
+
152
+ `pnpm check:vendored` compares every whole-file copy (and the route-root sets in `util.ts`)
153
+ against the Paperclip checkout, ignoring imports. Run it after each Paperclip upgrade;
154
+ `--diff` prints what moved.
155
+
156
+ ## Development
157
+
158
+ ```bash
159
+ pnpm dev # esbuild watch + CSS rebuild
160
+ pnpm test # vitest — needs a prior `pnpm build` on a fresh clone
161
+ pnpm typecheck # tsc --noEmit
162
+ pnpm build # CSS then bundle
163
+ pnpm check:surface # the no-core-changes guardrail
164
+ pnpm hooks:install # pre-commit: surface check, typecheck, test
165
+ ```
166
+
167
+ Tests run in jsdom, which implements neither `HTMLCanvasElement.getContext` nor navigation. Both
168
+ log "Not implemented" errors during a passing run. That output is expected noise, not failure —
169
+ check the summary line.
170
+
171
+ ## Layout
172
+
173
+ ```
174
+ src/
175
+ manifest.ts plugin id, capabilities, entrypoints
176
+ worker.ts no-op
177
+ ui/
178
+ PlicaHud.tsx root: owns the roster, sort mode, pins, token settings
179
+ PlicaPage.tsx host-mounted page slot
180
+ PlicaToolbarButton.tsx toolbar launcher
181
+ components/ company lines, queue, recent tasks, portfolio, briefing
182
+ lib/ capacity, queue grouping, run derivation, drafts
183
+ host/ vendored Paperclip internals (read-only)
184
+ styles.ts injects the compiled sheet, minus host duplicates, idempotently
185
+ scripts/
186
+ build-css.mjs Tailwind compile (host duplicates are subtracted at runtime)
187
+ capture-screenshots.mjs the images in docs/, from a running instance
188
+ gen-demo-data.mjs the demo fixture
189
+ check-plugin-surface.mjs the no-core-changes guardrail
190
+ docs/ the documentation set
191
+ ```
192
+
193
+ ## Reporting a bug, or asking for a feature
194
+
195
+ Open an issue from one of the three templates — feature request, bug report, or
196
+ core limitation. [docs/intake.md](docs/intake.md) is the whole path a request
197
+ takes from there to a merged change, including the two points where the
198
+ repository owner decides: the priority band, and the merge.
199
+
200
+ You will get an answer either way. A request that turns out to be a duplicate is
201
+ closed pointing at the original and its evidence is moved there first; one that
202
+ is real but not now is *parked*, not closed, and reopens on a sentence.
203
+
204
+ ## Contributing
205
+
206
+ [CONTRIBUTING.md](CONTRIBUTING.md). The rule to read before anything else:
207
+ **Plica never modifies Paperclip core.** Everything lands here, through a plugin
208
+ extension point. If the plugin surface cannot express a change, file it as a
209
+ core limitation rather than working around it — CI enforces this, with no bypass.
210
+
211
+ ## License
212
+
213
+ MIT