Skip to main content

kind: Project

What it is

kind: Project is the declarative representation of a Crewship project — the workspace-scoped container that groups missions, tracks delivery health, and assigns a lead agent. Use it in a manifest when you want crewship apply to create new projects, keep an existing roadmap aligned with version-controlled YAML, or round-trip workspace state through crewship export for backup and migration. One YAML document maps to exactly one row in the projects table.

YAML schema

apiVersion: crewship/v1            # only crewship/v1 is supported
kind: Project
metadata:
  name: Q2 Roadmap                 # required — human-facing label, also feeds the server's slug derivation
  slug: q2-roadmap                 # required — workspace-unique idempotency key
  description: All Q2 deliverables # optional — long-form description, stored in projects.description
spec:
  color: "#3B82F6"                 # optional — six-digit hex, default "blue" applied by server
  status: planned                  # optional — one of: planned, active, completed, archived
  priority: medium                 # optional — one of: low, medium, high, urgent
  health: on_track                 # optional — one of: on_track, at_risk, off_track
  target_date: "2026-06-30"        # optional — YYYY-MM-DD calendar date
  lead_agent_slug: pepa            # optional — slug of an Agent in the same workspace
Every field except apiVersion, kind, metadata.name, and metadata.slug is optional. Empty fields are treated as “leave server-side default in place” — they do not overwrite values an operator changed via the UI.

Examples

Minimal — slug + name only

apiVersion: crewship/v1
kind: Project
metadata:
  name: Migration backlog
  slug: migration-backlog
spec: {}
crewship apply creates the project with server defaults: color=blue, status=backlog, priority=none, health=on_track.

Realistic — all common fields

apiVersion: crewship/v1
kind: Project
metadata:
  name: Q2 Roadmap
  slug: q2-roadmap
  description: |
    Every Q2 deliverable, from the auth rework through the
    sidecar V2 cutover. Owner: Pepa.
spec:
  color: "#3B82F6"
  status: active
  priority: high
  health: on_track
  target_date: "2026-06-30"
  lead_agent_slug: pepa

Cross-kind FK — Milestone referencing a Project

---
apiVersion: crewship/v1
kind: Project
metadata: { name: Q2 Roadmap, slug: q2-roadmap }
spec: { priority: high, target_date: "2026-06-30" }
---
apiVersion: crewship/v1
kind: Milestone
metadata: { name: v1.0 launch, slug: v1-launch }
spec:
  project_slug: q2-roadmap         # FK by slug, resolved at apply time
  target_date: "2026-06-15"
  status: planned
The Apply pipeline runs Projects (Phase 3) before Milestones (Phase 5), so the slug resolves cleanly even on a from-scratch deploy.

CLI reference

The existing crewship project ... subcommands cover ad-hoc admin tasks; the manifest is the recommended path for repeatable deploys.
CommandDescription
crewship apply --file projects.yamlCreate/update projects to match the manifest
crewship apply --file projects.yaml --dry-runShow the plan without mutating
crewship apply --file projects.yaml --replace --yesDestructive recreate (Delete-then-Create per project)
crewship export workspaceEmit a multi-doc YAML containing every project (and other kinds)
crewship project listExisting CLI surface — list projects in the workspace
crewship project get <slug>Existing CLI surface — print one project’s full row
crewship project delete <slug>Existing CLI surface — delete by slug

REST endpoint mapping

Manifest fieldPOST body fieldDB column
metadata.namenameprojects.name
metadata.slugslugprojects.slug (server re-derives from name)
metadata.descriptiondescriptionprojects.description
spec.colorcolorprojects.color
spec.statusstatusprojects.status
spec.prioritypriorityprojects.priority
spec.healthhealthprojects.health
spec.target_datetarget_dateprojects.target_date
spec.lead_agent_sluglead_id (resolved from slug) + lead_type: agentprojects.lead_id, projects.lead_type
Endpoints touched:
VerbPathWhen
GET/api/v1/projectsPlan phase — slug-based lookup against existing rows
GET/api/v1/agentsPlan phase — resolve lead_agent_slug to lead_id
POST/api/v1/projectsApply phase, Action=Create
PATCH/api/v1/projects/{id}Apply phase, Action=Update
DELETE/api/v1/projects/{id}Apply phase, Action=Delete (only in ApplyReplace mode)

Validation rules

  • apiVersion must equal crewship/v1.
  • kind must equal Project.
  • metadata.name is required and non-empty.
  • metadata.slug is required and non-empty.
  • spec.status, when set, must be one of planned, active, completed, archived.
  • spec.priority, when set, must be one of low, medium, high, urgent.
  • spec.health, when set, must be one of on_track, at_risk, off_track.
  • spec.color, when set, must match ^#[0-9A-Fa-f]{6}$.
  • spec.target_date, when set, must parse as YYYY-MM-DD.
  • spec.lead_agent_slug, when set, must reference an Agent present in either the declared manifest or the workspace’s existing agents.
Validation is executed before any HTTP call, so a malformed document fails fast without partial mutations.

Apply behavior

Default — ApplyUpsert

  1. Fetch GET /api/v1/projects and look up the slug client-side (the server has no ?slug= filter).
  2. If not found: emit Action=Create and POST /api/v1/projects with the full body.
  3. If found and identical: emit Action=Unchanged (no HTTP call).
  4. If found and drifted: emit Action=Update and PATCH /api/v1/projects/{id} with only the drifted fields. Fields the manifest leaves empty are skipped — they never overwrite a non-empty server value.

ApplyStrict

Same as Upsert except step (2) is the only allowed outcome. If a project with the same slug already exists, apply fails with already exists. Use this in CI for “expect-fresh-deploy” pipelines.

ApplyReplace

For every declared project that already exists remotely, emit Action=Delete (DELETE /api/v1/projects/{id}) before the Create. New projects skip the Delete and behave like Upsert. This mode also unlinks every mission whose project_id pointed at the old row, because the handler’s DELETE path wraps the operation in a transaction that nulls the FK.

Round-trip via export

crewship export workspace calls ExportProjects, which:
  1. Issues GET /api/v1/projects for every project in the workspace.
  2. Issues GET /api/v1/agents once and builds an id → slug map.
  3. Maps each row into a ProjectDocument:
    • Server columns id, workspace_id, created_at, updated_at, issue_count, done_count, progress, and icon are dropped (manifest doesn’t model server-managed state).
    • lead_type=agent + a known lead_id resolves back to spec.lead_agent_slug. User-leads (lead_type=user) and unknown agent ids are dropped silently — manifest only models agent leads.
    • Empty description / target_date collapse to omitted YAML keys via omitempty.
  4. Sorts the result by slug so repeated exports produce identical files (clean git diffs).
crewship export emits the project as a standalone YAML doc separated by --- from other kinds; --split-dir <dir> writes it as project-<slug>.yaml instead.

See also

  • Milestone — references projects via spec.project_slug.
  • RecurringIssue — pre-creates issues into a template.project_slug.
  • TriageRule — can route incoming issues into a project via actions.assign_to_project_slug.
  • SavedView — filters its result set by filter.project_slug.
  • SPEC-2 §1 Project (.claude/context/specs/SPEC-2-manifest-complete.md) — implementation contract.