Skip to content

Marketplace libraries have no lifecycle (global table, per-tenant writer, no un-import); contractor-type delete has no reference check #293

Description

@important-new

Two lifecycle gaps found in the same audit. Both are low urgency — the marketplace has never been used in production and the contractor-type case degrades quietly — but both are one-way doors today.

B — marketplace libraries: global table, per-tenant writer, no way back

marketplace_libraries has no tenant_id; it is a shared catalogue. Its only writer is server/services/starter-content.service.ts:377, which runs during per-tenant provisioning. There is no create/update/delete API for it, for tenants or for sysadmin.

The result is visible in production: two rows, both seeded, both with download_count 0, one keyed by a stable slug and one by a UUID — two generations of the same seed, neither removable, and every tenant sees both.

tenant_library_imports has INSERT (marketplace.service.ts:418) and UPDATE (:521) and no DELETE. A tenant who imports a library can never un-import it. The one delete in the file (:500) is replace-mode pruning of prior-import comment rows, which is a different operation.

Production tenant_library_imports is 0 — the whole chain has never been exercised.

What is needed before anyone uses it:

  • an owner for the catalogue (sysadmin CRUD, or move the seed out of per-tenant provisioning so it stops writing a global table)
  • an un-import path that removes the import record and its library_id-tagged comment rows
  • a reconciliation for the two existing rows

D — deleting a contractor type leaves dangling references

ContractorTypeService.delete deletes unconditionally, with no reference check.

comments.recommendedContractorTypeId is a soft reference by design — the schema comment says "Soft ref → contractor_types.id (no DB FK per schema rules). Stale ref acceptable." So the delete is not a corruption, but the library UI's dropdown silently shows empty for every affected row and the tenant is not told how many they just orphaned.

Published reports are unaffected: server/lib/db/schema/repair-request.ts:45 notes that repair items store the resolved label, not the id.

Minimum fix: count referencing comments before deleting and say so in the confirm dialog. Full fix: offer to reassign them to another type.

Note this becomes more load-bearing once contractor types carry trade_slug (see #277) — a delete would then also drop the mapping to the immutable DEFECT_TRADES vocabulary.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions