invoices, extracted into plugins/invoices — the platform half

Ninth extraction, third resurrection. Switched off since 2026-08-13 with the
permission reduced to api: [], so assertPermissionTotality was satisfied, the
server booted clean, and every panel request 404'd. Closes the same data hazard
as jellyfin and soulseek: invoiceshelf/schema was commented out of the drizzle
barrel.

Named `invoices`, not `invoiceshelf`. The appName is THE id — API prefix,
frontend route, table prefix, sidecar suffix, install key — and before
extraction the API (/api/invoiceshelf) and the screen (/invoices) had drifted
apart, which was harmless only while they were separate hardcoded strings. The
route is the part a person sees, and the permission key and label were already
`invoices`, so the API prefix was the odd one out. Cost three call sites and a
table rename to invoices_accounts, which was free only because the table
existed on no server — verified before deciding, not after.

members.test.ts re-anchored again, this time to caldav and memos: services with
no extraction planned. That assertion has now been re-pointed four times by
services leaving for plugins/, which is the whole reason installer.test.ts was
made literal earlier tonight.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-19 03:47:54 +00:00
co-authored by Claude Opus 5
parent ee321f49fd
commit f566fda0b2
49 changed files with 26 additions and 8443 deletions
-15
View File
@@ -184,21 +184,6 @@ export const CATALOGUE: CatalogueEntry[] = [
{ key: 'secret', label: 'Access token', type: 'secret', required: true },
],
},
{
id: 'invoiceshelf',
ui: { name: 'Invoices', icon: 'Receipt', color: '#0891b2', rootRoute: '/invoices', routes: ['/invoices'] },
process: 'officer-invoiceshelf',
label: 'Invoices',
summary: 'InvoiceShelf — clients, estimates and invoices',
members: 'accounts',
modes: ['existing', 'provisioned'],
permission: 'invoices',
composeTemplate: 'invoiceshelf',
existingFields: [
{ key: 'url', label: 'InvoiceShelf URL', type: 'url', required: true },
{ key: 'secret', label: 'API token', type: 'secret', required: true },
],
},
// Vault was here until 2026-08-18, when it became `plugins/vault/`.
//
// Deleting the entry is REQUIRED, not tidying — the trap `EXTRACTING-A-PLUGIN.md` records as having
+5 -2
View File
@@ -18,8 +18,11 @@ describe('what adding a member would do', () => {
describe('which installed services have work to do', () => {
it('skips single-tenant daemons entirely', () => {
const ids = servicesNeedingMemberProvision(['invoiceshelf', 'vnc', 'memos']).map((e) => e.id);
expect(ids.sort()).toEqual(['invoiceshelf', 'memos']);
// caldav and memos provision accounts; vnc has no user concept. Both survivors are services with no
// extraction planned — this assertion has been re-pointed four times by services leaving for
// `plugins/`, so it now names ones that are staying.
const ids = servicesNeedingMemberProvision(['caldav', 'vnc', 'memos']).map((e) => e.id);
expect(ids.sort()).toEqual(['caldav', 'memos']);
});
it('ignores services that are not installed', () => {