The handbook
OverviewAgentsCultivatorsDataHub templates
Develop

Develop: Overview

AcasoOS is the foundational layer for building agents and the data foundation inside a company. The platform ships the runtime — tenancy, identity, memory, pipelines, the agent loop, the console — and client work happens on a small set of extension surfaces. You compose on top of the OS; you do not fork it.

This section is written for two audiences with the same needs: human engineers, and coding agents implementing AcasoOS for a client. Every instruction names exact paths and commands so it can be executed without tribal knowledge.

AcasoOS is domain-general. Client implementations span HR, sales, support, client-facing end users, third-party partner networks, and more — the platform privileges none of them. Everything domain-flavored in the tree today (the examples/hr_skills and examples/perf_evidence templates, the example agents) is reference material: copy it to start your own work, activate it only in dev/demo tenants, and never ship it as part of a client implementation.

The development model

One monorepo (acaso-os) is the single source of truth. All client-facing code — including code bespoke to a single client — lives in that repo, in namespaced locations, and ships inside the same versioned release. There are no client forks: a fork drifts, misses security fixes, and breaks the upgrade path. Bespoke code is isolated by namespace, not by repository.

Two deployment models consume those releases:

  • Shared multi-tenantmany clients on acaso-hosted infrastructure, isolated per row by Postgres RLS. A new client is a new tenant. See Provision a new client.
  • Standalone single-tenantthe whole stack in the client's own cloud, one tenant per instance, same code path and same container image. See Standalone deployment and Upgrades.

Where development happens

These are the blessed extension surfaces. Anything outside them is core platform and changes via the normal spec process (specs/), not during client work.

SurfaceLocation in the repoActivated by
Agents (client-bespoke)packages/agenthub/src/agenthub/custom/<client_slug>/Explicit ACASO_AGENT_MODULES env var
Cultivator Kinds (client-bespoke)packages/datahub/src/datahub/templates/custom/<client_slug>/cultivators/Tenant binding (only the matching tenant can bind)
DataHub templates (productized catalog)packages/datahub/src/datahub/templates/<family>/<name>/tenant_template_state via the admin API
Cultivator Kinds (catalog)packages/datahub/src/datahub/templates/<family>/<name>/cultivators/Tenant binding via POST /v1/cultivators
End-user apps (L3)packages/apphub/ (@acaso/react SDK)npm install in the client app
Examples — agents (copy, never ship)packages/agenthub/src/agenthub/examples/Dev-default ACASO_AGENT_MODULES only
Examples — templates + Kinds (copy, never ship)packages/datahub/src/datahub/templates/examples/Explicit activation, dev/demo tenants only

No catalog template ships in-tree yet — sales/*, eng/*, hr/* families land via their own spec cycles. Until then, every real client surface is either bespoke (custom/<client_slug>) or pure configuration.

Configuration-level customization needs no code at all and is the first thing to reach for: per-tenant agent overrides (model, disabled tools), versioned system prompts, cultivator bindings (model override, prompt append, scope narrowing), tenant Connections for MCP, and roles/scopes. See Workflows: Per-tenant agent overrides and Workflows: Manage cultivators.

Repo map:

acaso-os/
├── packages/
│   ├── shared/      # acaso_shared — auth, tenancy/RLS, scope matching, @tool
│   ├── datahub/     # L1 — primitives, graphium memory, pipelines, templates, cultivators
│   ├── agenthub/    # L2 — agent runtime, chat, admin API, examples/, custom/
│   ├── apphub/      # L3 — @acaso/react SDK
│   └── web/         # Management console (Next.js static export)
├── specs/           # Feature specifications — read before non-trivial work
├── docs/            # ADRs, runbooks, in-repo authoring guides
├── infra/           # docker/, terraform/standalone-*/, helm/
└── Makefile         # Source of truth for every workflow command

Non-negotiable rules

These hold for every line of client code. They are enforced by review, CI (the tenancy lint), and in several cases by the database itself.

  1. Every tenant-table access goes through tenant_scope / request_scope (acaso_shared.tenancy.rls). The app connects as the non-privileged acaso_app role, so RLS fires on every query — code that forgets to bind a tenant sees zero rows, not other tenants' rows.
  2. The LLM never sees tenant_id. Tools pull it from the request ContextVar (acaso_shared.tenancy.context.current_tenant_id), never from a model-visible argument.
  3. Never write to the AGE graph directly. Go through datahub.pipelines.nodes.write_graph_node / write_graph_edge so the relational tables and the graph stay consistent.
  4. Cultivators propose; the framework writes. A Cultivator returns StatementProposals and never calls ingest_context itself — the Scope Validator sits between the model and the database.
  5. Scope strings are built by code from trusted resolver output, never assembled by the LLM and never copied from raw event payloads.
  6. Never connect application code via ADMIN_DATABASE_URL. That role is for migrations and DDL only; using it at runtime defeats the isolation posture.
  7. Structured errors are CustomError(status_code, message, error_code)anything else surfaces as a generic 500.
  8. New tenant-scoped tables ship in the same PR as their RLS policy and a migration. CI's tenancy lint blocks violations.

Quality gates

Run all of these before considering any change done. CI runs the same set on every PR.

bash
make lint          # ruff check
make format_check  # ruff format --check
make type_check    # pyright
make test          # full pytest suite (unit + integration)

For console changes (packages/web/):

bash
pnpm --filter @acaso/web type-check
pnpm --filter @acaso/web test

Tests follow the AAA pattern — # Arrange, # Act, # Assert comment blocks — and live under each package's tests/unit/ and tests/integration/, mirroring the source tree. Commits follow Conventional Commits with package scopes (feat(datahub): ..., fix(agenthub): ...); branches follow simplified GitFlow (feature/* off develop).

For coding agents

If you are an automated coding agent implementing AcasoOS for a client, work in this order:

  1. Read CLAUDE.md at the repo root — conventions, commands, architecture.
  2. Read the umbrella spec specs/001-acaso-os-platform/spec.md for anything non-trivial.
  3. Read the authoring guide for the surface you are extending: Develop: Agents, Develop: Cultivators, or Develop: DataHub templates. Each has an in-repo counterpart under docs/ with the same content in greater depth.
  4. Copy the worked example for that surface rather than starting from a blank file.
  5. Run every quality gate above. A task with a failing gate is not done.

Definition of done for any client-development task: code in the blessed location, tests in AAA shape, all four make gates green, scopes reviewed (who can see/run the thing), and — for anything touching storage — the tenancy rules above verifiably honored.

PreviousWorkflowsReview the audit logNext DevelopAgents