PAP-10256 Team Catalog installed-state server signal (originHash compare)

Add GET /api/companies/:companyId/teams/catalog/installed. The service reads
each company agent's metadata.paperclip.catalogTeam provenance, groups by
catalogId, and compares the installed originHash against the live catalog
team's contentHash to compute present/outOfDate. Drives the Team Catalog
INSTALLED group + out-of-date badge from a real server signal (PAP-10238
§3.2 + §5).

- shared: InstalledCatalogTeam type
- server: listInstalledCatalogTeams + readCatalogTeamProvenance helper, route
  guarded by assertCompanyAccess
- tests: provenance parsing, out-of-date/current/removed/none cases, route
  authz + wiring

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
DottaandPaperclip committed 2026-06-05 03:00:04 +00:00
1 parent 06df8231bf
commit d507ce342d
7 files changed
+294

No files matched your search

+1
View File
@@ -341,6 +341,7 @@ export type {
CatalogTeamSkillPreparation,
CatalogTeamImportPreviewResult,
CatalogTeamInstallResult,
InstalledCatalogTeam,
AgentSkillSyncMode,
AgentSkillState,
AgentSkillOrigin,
+1
View File
@@ -98,6 +98,7 @@ export type {
CatalogTeamSkillPreparation,
CatalogTeamImportPreviewResult,
CatalogTeamInstallResult,
InstalledCatalogTeam,
} from "./teams-catalog.js";
export type {
AgentSkillSyncMode,
@@ -179,3 +179,31 @@ export interface CatalogTeamInstallResult {
skillPreparations: CatalogTeamSkillPreparation[];
warnings: string[];
}
/**
* Server-computed installed-team state for a company. Surfaced by
* `GET /api/companies/:companyId/teams/catalog/installed` and consumed by the
* Team Catalog UI to render the `INSTALLED · N` group, per-row out-of-date
* badges, and the detail header "Update available" chip (design
* [PAP-10238 §3.2 + §5]).
*
* `outOfDate` is true when at least one installed agent carries a
* `metadata.paperclip.catalogTeam.originHash` that differs from the catalog
* team's current `contentHash`. `present` is false when the installed team no
* longer resolves to a catalog entry (e.g. removed from the package) — in that
* case the comparison is unknown and `outOfDate` stays false.
*/
export interface InstalledCatalogTeam {
catalogId: string;
catalogKey: string | null;
/** True when the installed catalogId still resolves to a current catalog team. */
present: boolean;
/** Current catalog `contentHash` for this team, or null when not present. */
currentContentHash: string | null;
/** Distinct `originHash` values recorded across installed agents. */
installedOriginHashes: string[];
/** Number of non-terminated agents carrying this team's provenance. */
agentCount: number;
/** True when a present team has at least one stale installed originHash. */
outOfDate: boolean;
}