[codex] Add teams catalog extraction (#7550)

Fixes #7551

## Thinking Path

> - Paperclip is the control plane for AI-agent companies, and reusable
company/team setup is part of making those companies faster to launch.
> - The teams catalog work introduces app-shipped team templates that
can be browsed, previewed, and installed into a company.
> - Catalog installation crosses several contracts: bundled package
contents, shared API types, server import/install behavior, CLI
workflows, and the board UI.
> - Agents also need a safe path through catalog installs: scoped
company selection, explicit source policy, approval fallback for agent
creation, and preserved catalog provenance.
> - This pull request extracts the completed teams catalog branch into
one reviewable PR on top of `public-gh/master`.
> - The benefit is a reusable teams catalog foundation with server, CLI,
package, docs, and hidden UI surfaces kept in sync.

## What Changed

- Added the `@paperclipai/teams-catalog` package with bundled/optional
team definitions, generated manifest, validators, catalog builder tests,
and migration notes.
- Added shared teams catalog types/validators plus server routes and
services for listing, previewing, and installing catalog teams.
- Integrated catalog install with company portability, skill/source
policy checks, provenance metadata, origin hashes, target-manager
reparenting, and installed/out-of-date detection.
- Added CLI `teams` commands and agent-safe company selection behavior,
including `company current` and approval fallback for forbidden
agent-run installs.
- Added hidden Team Catalog UI/API/query surfaces, Storybook fixtures,
and targeted UI tests while keeping the UI route out of primary
navigation.
- Added docs for CLI/company/teams catalog behavior and removed
generated screenshot artifacts from the PR diff.

## Verification

- `pnpm exec vitest run cli/src/__tests__/company.test.ts
cli/src/__tests__/teams.test.ts
packages/teams-catalog/src/catalog-builder.test.ts
packages/teams-catalog/src/shipped-catalog.test.ts
server/src/__tests__/agent-permissions-service.test.ts
server/src/__tests__/company-portability.test.ts
server/src/__tests__/company-skills-service.test.ts
server/src/__tests__/teams-catalog-routes.test.ts
server/src/__tests__/teams-catalog-service.test.ts
server/src/__tests__/teams-catalog-install-no-overrides.test.ts
ui/src/lib/company-routes.test.ts ui/src/pages/TeamCard.test.tsx
ui/src/pages/TeamCatalog.test.tsx
ui/src/pages/useInstallTeamCatalogEntry.test.tsx`
- `pnpm --filter @paperclipai/shared typecheck && pnpm --filter
@paperclipai/teams-catalog typecheck && pnpm --filter paperclipai
typecheck && pnpm --filter @paperclipai/server typecheck && pnpm
--filter @paperclipai/ui typecheck`
- Confirmed branch is rebased onto `public-gh/master` (`78dc3625a`) and
`public-gh/master` is an ancestor of `HEAD`.
- Confirmed PR diff excludes `pnpm-lock.yaml`, `.github/workflows/*`,
generated screenshot images, and screenshot helper scripts.

## Risks

- Medium review surface: this crosses package generation, shared
contracts, server install behavior, CLI, docs, and hidden UI code.
- Catalog install behavior creates agents/projects/tasks/skills and must
keep company scoping, permissions, source policy, and provenance checks
strict.
- `pnpm-lock.yaml` is intentionally excluded per repo policy;
CI/default-branch automation owns lockfile refresh.
- The Team Catalog UI is included but hidden from primary navigation, so
future enablement should re-check visual QA before exposure.

> 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`.
>
> ROADMAP checked: this aligns with reusable companies/templates and
plugin-adjacent onboarding work. This PR packages work already developed
on the Paperclip task branch for review.

## Model Used

- OpenAI Codex, GPT-5 series coding agent in this Paperclip session;
exact runtime context window was not exposed. Used shell, git, `gh`, and
local test/typecheck tooling.

## 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 run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] If this change affects the UI, I have included before/after
screenshots, or documented why screenshots are intentionally omitted
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] I will address all Greptile and reviewer comments before
requesting merge

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Dotta
2026-06-05 07:55:49 -10:00
committed by GitHub
parent 3657854e5e
commit fff3832a01
80 changed files with 12449 additions and 470 deletions
+128
View File
@@ -126,6 +126,25 @@ import { Identity } from "@/components/Identity";
import { IssueReferencePill } from "@/components/IssueReferencePill";
import { MembershipAction } from "@/components/MembershipAction";
import { IssueOutputSection } from "@/components/issue-output/IssueOutputSection";
import {
EnvInputsList,
ExternalSourcesList,
RequiredSkillsList,
StepSkillPlan,
StepSourcePolicy,
TeamCard,
TeamHierarchyPreview,
TeamRow,
} from "@/pages/TeamCatalog";
import {
currentInstalledState,
onboardingTeams,
optionalTeam,
outOfDateInstalledState,
sampleSkillPreparations,
sampleTeam,
warnTeam,
} from "@/pages/TeamCatalog.fixtures";
import type { IssueWorkProduct } from "@paperclipai/shared";
/* ------------------------------------------------------------------ */
@@ -221,6 +240,24 @@ function SubSection({ title, children }: { title: string; children: React.ReactN
);
}
// Onboarding seam (design §6 + §12.5): the TeamCard tile in its "Pick a starter
// team" 3-col grid, with the first defaultInstall tile selected.
function TeamCardShowcase() {
const [selectedId, setSelectedId] = useState(onboardingTeams[0]?.id ?? null);
return (
<div className="grid max-w-2xl gap-4 md:grid-cols-2 lg:grid-cols-3">
{onboardingTeams.map((team) => (
<TeamCard
key={team.id}
team={team}
selected={team.id === selectedId}
onSelect={() => setSelectedId(team.id)}
/>
))}
</div>
);
}
/* ------------------------------------------------------------------ */
/* Color swatch */
/* ------------------------------------------------------------------ */
@@ -259,6 +296,9 @@ export function DesignGuide() {
{ key: "status", label: "Status", value: "Active" },
{ key: "priority", label: "Priority", value: "High" },
]);
const [allowExternal, setAllowExternal] = useState(false);
const [allowUnpinned, setAllowUnpinned] = useState(false);
const [allowLocalPath, setAllowLocalPath] = useState(false);
return (
<div className="space-y-10 max-w-4xl">
@@ -1417,6 +1457,94 @@ export function DesignGuide() {
{/* ============================================================ */}
{/* ICON REFERENCE */}
{/* ============================================================ */}
{/* TEAM CATALOG */}
{/* ============================================================ */}
<Section title="Team Catalog">
<p className="text-sm text-muted-foreground">
Components from the Team Catalog browse/install surface (<code className="font-mono text-xs">/teams-catalog</code>).
Fixtures are shared with the Storybook stories.
</p>
<SubSection title="TeamRow (browse list)">
<div className="w-[28rem] rounded-md border border-border">
<div className="px-3 py-2 text-[11px] font-semibold uppercase tracking-wide text-muted-foreground">
Bundled · 1
</div>
<TeamRow team={sampleTeam} selected onSelect={() => {}} />
<div className="px-3 py-2 text-[11px] font-semibold uppercase tracking-wide text-muted-foreground">
Optional · 2
</div>
<TeamRow team={optionalTeam} selected={false} onSelect={() => {}} />
<div className="px-3 py-2 text-[11px] font-semibold uppercase tracking-wide text-muted-foreground">
Installed · 2
</div>
<TeamRow team={sampleTeam} selected={false} onSelect={() => {}} installed={outOfDateInstalledState} />
<TeamRow team={warnTeam} selected={false} onSelect={() => {}} installed={currentInstalledState} />
</div>
<p className="mt-2 text-xs text-muted-foreground">
Installed teams collapse under <code className="font-mono">INSTALLED · N</code>; an out-of-date
install (server <code className="font-mono">originHash</code> catalog <code className="font-mono">contentHash</code>)
shows the amber <code className="font-mono"></code> badge (PAP-10256).
</p>
</SubSection>
<SubSection title="TeamCard (onboarding grid)">
<p className="text-xs text-muted-foreground">
Square tile for the onboarding &ldquo;Pick a starter team&rdquo; grid. Selected tile gets{" "}
<code className="font-mono">ring-2 ring-ring</code>. Drives the{" "}
<code className="font-mono">useInstallTeamCatalogEntry</code> simplified flow.
</p>
<TeamCardShowcase />
</SubSection>
<SubSection title="TeamHierarchyPreview">
<div className="max-w-md">
<TeamHierarchyPreview team={sampleTeam} />
</div>
</SubSection>
<SubSection title="RequiredSkillsList">
<div className="max-w-xl">
<RequiredSkillsList skills={sampleTeam.requiredSkills} />
</div>
</SubSection>
<SubSection title="EnvInputsList">
<div className="max-w-xl">
<EnvInputsList inputs={sampleTeam.envInputs} />
</div>
</SubSection>
<SubSection title="ExternalSourcesList">
<div className="max-w-xl">
<ExternalSourcesList sources={sampleTeam.sourceRefs} />
</div>
</SubSection>
<SubSection title="Source policy step (StepSourcePolicy)">
<div className="max-w-xl rounded-md border border-border p-4">
<StepSourcePolicy
team={warnTeam}
allowExternalSources={allowExternal}
allowUnpinnedOptionalSources={allowUnpinned}
allowLocalPathSources={allowLocalPath}
onChange={(key, value) => {
if (key === "external") setAllowExternal(value);
if (key === "unpinned") setAllowUnpinned(value);
if (key === "localPath") setAllowLocalPath(value);
}}
/>
</div>
</SubSection>
<SubSection title="Skill plan step (StepSkillPlan)">
<div className="max-w-xl rounded-md border border-border p-4">
<StepSkillPlan team={sampleTeam} preparations={sampleSkillPreparations} />
</div>
</SubSection>
</Section>
{/* ============================================================ */}
<Section title="Common Icons (Lucide)">
<div className="grid grid-cols-4 md:grid-cols-6 gap-4">