Files
paperclip-docs/docs/reference/cli/teams.md
Aron PrinsandClaude Opus 5 325c9eb819 seo: hand-write titles and descriptions for all 192 pages (#115)
## Why

The build derived `<title>` from the **sidebar label** and the
description from the **first paragraph clipped to 220 characters**. Both
are the wrong source. A sidebar label is written for a 240px-wide nav
that supplies its own context; a search result has none.

On production that produced:

- **7 pages titled "Overview"**, and 32 pages sharing a title with
another page
- **109 of 193 descriptions cut off mid-word**, and 174 over 160
characters (where Google truncates)

```
/guides/welcome/key-concepts/
  BEFORE (220):  ...and if you misunderstand them early, the product wi
```

Near-identical titles are the weak-differentiation signal behind
"crawled — currently not indexed", which is the exact symptom on this
property.

## What changed

`seo_title` and `seo_description` frontmatter on **every one of the 192
pages**, written by hand against each page's real content and optimised
for the click — lead with what the reader can do, name the concrete
things covered, no keyword stuffing, no repeating the title.

| | Before | After |
|---|---|---|
| Duplicate titles | 32 pages | **0** |
| Duplicate descriptions | — | **0** |
| Descriptions cut mid-word | 109 | **0** |
| Descriptions over 160 chars | 174 | **0** |
| Title length (incl. suffix) | up to "Overview" | **26–60** |
| Description length | 31–240 | **122–158** |

**No prose was rewritten.** I measured content overlap between the
colliding pairs first — only 17–23%, so these were always distinct pages
wearing the same label, not duplicate content. The `docs/` diff is **564
insertions, zero deletions**.

### Examples

```
BEFORE — all seven identical:  "Overview | Paperclip Docs"
AFTER:  "Adapters Overview"      "API Overview"        "CLI Overview"
        "Deployment Overview"    "Bundled Skills"      "Optional Skills"
        "Experimental Features"

BEFORE — both identical:  "Issues | Paperclip Docs"
AFTER:  "Issues: Creating and Tracking Work"   /guides/day-to-day/issues/
        "Issues API"                           /reference/api/issues/
```

```
/guides/getting-started/five-minute-path/
BEFORE (220):  The shortest route from a running Paperclip instance to an agent that has
               just completed work for you. After Paperclip is installed, the path takes
               about 5 minutes: create a company, hire a CEO agent, approve its first s
AFTER  (131):  Install Paperclip, create a company, hire a CEO agent, and approve its
               first strategy — a working AI company in about five minutes.
```

## Plumbing

- **`site/build-release.mjs`** prefers the authored fields; the derived
path stays only as a fallback the CI gate prevents ever reaching.
`<title>`, `<meta name="description">`, and the `TechArticle` JSON-LD
all read from the same source.
- **`site/app.js`** uses the same fields, so a client-side transition
cannot leave a different title on screen than the crawler was served.
Verified in-browser: navigating `/guides/day-to-day/issues/` →
`/reference/api/issues/` swaps to exactly the server-rendered values, no
JS errors.
- **`scripts/verify-seo-metadata.mjs`** (new) enforces presence,
uniqueness, length, complete-sentence endings, and the
frontmatter-safety rules. Wired into `docs:test` and CI as its own step.

I negative-tested the gate rather than assuming it works — it correctly
fails on a duplicate title, a mid-word description, and a missing field,
and passes again once restored.

## Automating it going forward

`skills/sync-docs/SKILL.md` now:

- gains an **"Authored SEO metadata"** section: why the fields exist,
the enforced rules as a table, and how to actually write them (title
names the subject not the nav position; description writes for the
click; never generate mechanically from the first paragraph — *that is
the fallback the gate exists to prevent*).
- requires both fields in the **page-authoring instruction** given to
every subagent, with guidance on when to update an existing description
versus leave it.
- runs `docs:test:seo-metadata` and `docs:test:crawlable-links` as
**fail-loud gates in Phase 7** before commit.
- documents all six build gates in the files/gates section.

So once this lands, a new page cannot ship without hand-written
metadata, and a title collision cannot reach production.

## Verified

All six verifiers pass:

```
Static route verification passed
Asset fingerprint verification passed
Hierarchical Skills nav verification passed
Skill source block verification passed (11 skill pages, 1 control page)
Crawlable-link contract passed: 40886 internal links across 194 pages resolve in one hop
Authored-SEO contract passed: 192 pages carry a unique, hand-written seo_title and seo_description
```

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 5 (1M context) <[email protected]>
2026-08-25 22:51:42 +02:00

8.3 KiB

paperclip_version, seo_title, seo_description
paperclip_version seo_title seo_description
v2026.609.0 CLI Teams Commands Install a ready-made starter group — agents, projects, tasks, and the skills they need — from the catalog Paperclip ships with the app.

Teams Commands

A team in the catalog is a ready-made starter group — a small set of agents, projects, tasks, and the skills they need — that Paperclip ships with the app. Use paperclipai teams when you want to browse those team templates, inspect what one would create, preview the import against a real company, and then install it. Think of it as the team-shaped equivalent of the skills catalog: instead of one reusable procedure, you get a whole working unit you can drop into a company in one step.

There are two kinds of catalog teams. Bundled teams ship inside the app and are the everyday starting points (a core executive team, a product-engineering pod, a product-design team). Optional teams are extras you reach for when you need them, and some of them pull in skills from external sources. The --kind filter on the browsing commands switches between the two.

Tip: Add --json to any command to print the raw API result for scripting. Browsing commands need no company context, but list, preview, and install operate against a company and accept --company-id <id> (and respect your selected profile). See common options.


Browse before you install

The browsing commands read the catalog without writing anything to a company. There is no company context required to browse, search, or inspect.

paperclipai teams browse --kind bundled --category company-defaults
paperclipai teams search "engineering" --kind bundled
paperclipai teams inspect core-exec-team
Command Use
browse List catalog teams, optionally filtered by --kind, --category, or --query.
search <query> Same as browse but takes the search text as a positional argument.
inspect <catalogRef> Show one catalog team in full — its agents, projects, tasks, required skills, and file manifest.

Browse and search filters:

Flag Use
--kind <kind> Filter by catalog kind: bundled or optional.
--category <slug> Filter by catalog category slug.
--query <text> Free-text search (browse only; search uses its positional argument).

A <catalogRef> is a catalog team ID, key, or a unique slug. Inspecting a team before you install it is the best way to see exactly which agents and projects will land in the company.

inspect can also print a single file out of the team package instead of the manifest detail:

Flag Use
--file <path> Print a specific catalog team file (for example TEAM.md) instead of the manifest detail. In human mode the raw content is written to stdout so you can pipe it.

List with installed status

teams list is browse with a company lens: it shows each catalog team alongside whether it is already installed in a given company, and whether the installed copy has drifted out of date from the current catalog version. It requires a company context.

paperclipai teams list --company-id <company-id>
paperclipai teams list --kind optional --company-id <company-id>
Flag Use
--kind <kind> Filter by catalog kind: bundled or optional.
--category <slug> Filter by catalog category slug.
--query <text> Free-text search.

Each row reports the team's installed status — not installed, installed, out of date, or installed-but-missing — together with how many agents and projects it carries. A team is flagged out of date when an installed agent's recorded origin differs from the catalog team's current content, which is your cue to reinstall the newer version.


Preview an install

Before you commit, teams preview runs the import on the server and shows you the plan without writing anything. It reports what would be created, which skills the team needs and how they would be prepared, and any warnings or errors. It requires a company context.

paperclipai teams preview core-exec-team --company-id <company-id>
paperclipai teams preview product-engineering \
  --target-manager-slug cto \
  --collision-strategy rename \
  --company-id <company-id>
Flag Use
--target-manager-agent-id <id> Existing agent ID that the catalog's root agents should report to.
--target-manager-slug <slug> Portable manager slug that the catalog's root agents should report to.
--agent <slug> Only preview the selected agent slug. Repeat for several.
--collision-strategy <strategy> How to handle existing matches: rename, skip, or replace.
--name-override <slug=name> Override an imported entity's name. Repeat for several.
--selected-file <path> Restrict the preview to a selected portable file. Repeat for several.
--allow-external-sources Allow GitHub, URL, or skills.sh skill sources declared by the catalog team.
--allow-unpinned-optional-sources Allow optional-team external skill sources that are not pinned to a commit.
--allow-local-path-sources Development only: allow local-path skill sources declared by the catalog team.

The two manager options let you slot a team in under your existing org chart: instead of the team's root agents standing alone, they report to an agent you already have. Use --target-manager-agent-id when you know the exact agent ID, or --target-manager-slug for a portable slug.


Install a team

teams install does what preview shows: it imports the team into the company, creating its agents, projects, tasks, and preparing its required skills. It requires a company context and accepts everything preview does, plus a few install-only flags.

paperclipai teams install core-exec-team --company-id <company-id>

paperclipai teams install product-engineering \
  --target-manager-slug cto \
  --adapter-override senior-coder=claude_local \
  --company-id <company-id>

install shares all of preview's flags (--target-manager-agent-id, --target-manager-slug, --agent, --collision-strategy, --name-override, --selected-file, and the three --allow-* source flags) and adds:

Flag Use
--secret-value <key=value> Provide a value for a secret env input the team declares. Repeat for several.
--adapter-override <slug=type> Adapter type override for an imported agent slug — for example senior-coder=claude_local. Repeat for several.
--request-approval-on-forbidden When the install is denied because you lack agents:create permission, create a board approval request instead of exiting with the raw 403.
--approval-issue-id <id> Issue ID to link to the fallback approval request. Defaults to PAPERCLIP_TASK_ID when that is set.

Catalog teams ship without a fixed adapter per agent on purpose, so the install lets you choose the runtime. Use --adapter-override <slug=type> to pin a specific agent to an adapter such as claude_local or codex_local; agents you do not override are resolved at import time.

When you do not have permission to create agents

Installing a team creates agents, which requires the agents:create permission. If the server denies the install for that reason, you would normally see a 403. Pass --request-approval-on-forbidden (or run inside a Paperclip task, where PAPERCLIP_TASK_ID is set) and the CLI instead files a board approval request describing the install you attempted, so a board operator can approve it. Any secret values you passed are stripped from the stored request and redacted in the returned result.


See also