Skip to content

Projects

A project is the unit of attribution. Every session, conversation, and meeting attaches to exactly one project (or to none, until it’s classified). Projects roll up to categories — seven values that match how the team actually thinks about work — and, where useful, drill down to subcategories for finer breakdowns.

The taxonomy lives in codika-os-app/seeds/projects.yaml and is materialized into the projects and project_path_prefixes tables by npm run db:sync:projects. There is one source of truth; no parallel hand-curated lists.

CategoryWhat it coversSubcategories
clientAnything done for an external client. One project per client; slug = client folder name.knowledge | product (set on the session, not the project row)
productExternal products Codika operates (Propale, Slideless, etc.).
toolInternal tools the team operates (codika-os, bot factory, env vault).
platformThe core Codika platform — the flagship app, supporting services, infra, libs, templates, marketplaces.app | cloud-run | infra | libs | template | marketplace
growthSales and content work — playbooks, campaigns, carousels, outbound.content | strategy | outreach
knowledgeCompany-OS and workspace reading/writing that isn’t a deliverable.company-os | general
privatePersonal sandboxes. Excluded from company rollups.

Each category exists because removing it would collapse a distinction the dashboard needs to surface:

  • client is the leverage story for revenue-bearing work. Splitting it from internal categories is the whole point of the dashboard.
  • product isolates Codika’s own products from client engagements — different economics, different planning cadence.
  • tool captures the internal apps that keep the agency running but aren’t products. Lumped into platform, they’d inflate the platform leverage number.
  • platform is the engine. Subcategories (app / cloud-run / infra / libs / template / marketplace) let us see where platform work is concentrated this month without minting six top-level categories.
  • growth keeps outbound and content visible as their own line item. Without it, every campaign session would land in knowledge:general and disappear.
  • knowledge is the catch-all for reading, exploring, writing strategy. Splitting company-os (the agents-org plugin) from general lets us tell “agent infrastructure” apart from “everything else workspace-rooted.”
  • private is opt-out: anything under private/projects/ is captured locally but never rolls up to the team view.

Subcategories live on different rows depending on whether the project is single-scope or multi-scope:

  • Single-scope projects (like codika-appplatform:app) carry the subcategory on the project row itself. Every session attributed to codika-app is implicitly subcategory = 'app'.
  • Multi-scope projects (every client project) carry subcategory = NULL on the project row. The subcategory is decided per session, stored in sessions.attribution_subcategory. The path classifier reads it from the matched project_path_prefixes row: editing a file under agency/crm/codika-clients/clients/creafid/ emits subcategory = 'knowledge'; editing under agency/projects/creafid/ emits subcategory = 'product'.

This is what lets a client like Creafid show “20h knowledge, 50h product” on a single donut without splitting it into two projects.

Most projects are reached by path matching — the classifier scans paths_touched, finds the longest matching prefix in project_path_prefixes, and picks the owning project. But some work doesn’t touch the workspace at all. Outbound campaign prep happens in Lemlist, Apollo, LinkedIn — no files. Workspace-rooted reading sessions might touch a dozen places without landing decisively on any.

For those cases the taxonomy ships two prompt_only: true projects:

  • growth:outreach — Lemlist/Apollo/LinkedIn pipeline work. No path prefixes. The agent classifier picks it when prompt evidence describes outbound, lead enrichment, or content scheduling.
  • knowledge:general — workspace-rooted exploration that doesn’t fit anywhere more specific. Path-matched against workspace/ with priority: low, plus picked by the agent classifier when nothing more specific applies.

prompt_only projects are invisible to the path classifier; they exist only so the agent-inline classifier (see Classification) has a slug to land on.

seeds/projects.yaml declares one or more discovery rules per category. The seeder walks them and mints projects + path prefixes accordingly.

Rule shapeWhat it does
root + leaf: direct-childLists the named workspace folder; mints one project per direct subdirectory. Slug = bare folder name. legacy_paths are also rewritten with the same leaf and attached as legacy path prefixes (so historical sessions still attribute).
flat_pathMints exactly one project at the given path. Used for marketplace, knowledge:company-os, etc.
path_prefixesMints one project rooted at multiple explicit paths. Used for growth:strategy (playbooks + campaigns).
prompt_onlyMints a project with no path prefixes. Reached only by the agent classifier.

The seeder also lints before writing. Two prefixes can’t belong to two different projects. Every client:knowledge slug must exist as client:product (and vice versa) — the parity rule. Slugs must be kebab-case.

ColumnPurpose
slugStable, machine identifier. Kebab-case. Unique per (category, slug). Never change it — URL routes (/tracking/projects/<slug>) and the CLI classifier depend on it.
display_nameLong-form label, e.g. “Creafid”.
aliasShort chip label shown in the UI. Defaults to display_name if not set.
colorPalette key (indigo, teal, rose …). Resolved to HSL at render.
categoryOne of the 7. Required.
subcategorySet on single-scope projects (e.g. app for codika-app). NULL for multi-scope (client) projects.
client_slugReferences clients(slug). Non-null iff category = 'client'.
prompt_onlytrue for projects with no path prefixes (only reached by the agent classifier).
org_scope'company' or 'private'. Private projects are excluded from company-level rollups.
is_activeSoft-delete flag. Inactive projects still resolve for historical sessions but don’t appear in pickers.

Path prefixes live in project_path_prefixes. Each row carries its own subcategory (which the classifier uses for sessions.attribution_subcategory) and an is_legacy flag for prefixes kept around to attribute pre-reorg history.

Sourced from seeds/projects.yaml. One row per discovery rule; the slug column shows either a fixed project slug or <leaf> for direct-child rules that mint one project per subdirectory.

CategorySubcategoryDiscoverySlug
clientknowledgeagency/crm/codika-clients/clients/*<leaf>
clientknowledgeagency/crm/codika-clients/prospects/*<leaf>
clientproductagency/projects/*<leaf>
productproducts/*<leaf>
toolagency/tools/*<leaf>
platformappcore/app/*<leaf>
platformcloud-runcore/cloud-run/*<leaf>
platforminfracore/infra/*<leaf>
platformlibscore/libs/*<leaf>
platformtemplatetemplates/*<leaf>
platformmarketplaceagency/plugins/codika-product-marketplacemarketplace
growthcontentworkspace/contentcontent
growthstrategyworkspace/openclaw/docs/playbooks + workspace/openclaw/docs/campaignsstrategy
growthoutreachprompt_onlyoutreach
knowledgecompany-osagency/plugins/codika-agents-orgcompany-os
knowledgegeneralworkspace (priority: low)general
privateprivate/projects/*<leaf>

Each codika-os project is a 1:1 candidate for a Linear project. The schema doesn’t yet store the link (no linear_project_id column), but the slug/category model was chosen to match Linear’s nomenclature so the join is straightforward when the sync ships. See Known follow-ups below.

  • Linear syncprojects.linear_project_id column to associate each project with its Linear project. Sessions → Linear issues falls out once it’s wired.
  • Per-session subcategory KPIs — the dashboard currently surfaces category-level rollups; per-subcategory tiles (e.g. “client knowledge vs product time”) are a follow-up.
  • In-app project edits — alias/color editing is still seeds-side. An admin form is planned.