Adding a feature
The path of a new feature through the Colden kit, from the endpoint to the screen: the vertical slice, its tests, the OpenAPI document and the SDK, the page, and the documentation, with the agent skills that do each step.
A feature is built as one vertical slice of the API, then the screen that calls it. The order below keeps every step checkable: the API answers before the web calls it, the types the web uses are generated from the API, and the tests come with the code rather than after it. The kit's agent skills do the same steps; this guide is the map for people.
The order of work
| Step | Produces | Proven by |
|---|---|---|
| 1. Decide | whose data, who may call it, what it records | the answers written in the plan or the change request |
| 2. Data | the entity, its configuration and a migration | tools/ef-migrate.sh check and the model tests |
| 3. Slice | request, validator, handler, endpoint | the slice's integration tests |
| 4. Contract | the OpenAPI document and the SDK types | tools/openapi-check.sh and the SDK check |
| 5. Screen | the page, its components and messages | the web's unit tests and, for a flow, a journey |
| 6. Documentation | the architecture file, the guide, the changelog | the link check and the reviewer |
1. Decide what it is
- Whose data. An organization's data lives under
/v1/organizations/{organizationId}/...and is tenant-scoped; a person's own under/v1/me/...; the platform's under/v1/admin/...for super-admins (tenancy, authorization). - Who may call it. A member, an admin, an owner, a super-admin with a second factor, a program with an API key and a scope.
- What it records. The audit action, as
noun.verb(the audit log), and the message other parts react to (messaging and jobs). - What it changes for buyers. An endpoint, a configuration key, a table, a component or a Helm value is a public surface: the changelog names it, and a breaking change gets an upgrade note.
2. The data
A new table is an entity in packages/core/Colden.Core/Domain/<Area>/ with its configuration in Persistence/Configurations/: snake-case names, a UUID v7 key made by the application, organization_id and the tenant filter for an organization's rows. tools/ef-migrate.sh add <Name> generates the migration; read its Up and Down before committing it, and split a change the running release would trip over into expand, migrate and contract across releases.
3. The slice
One folder per area in apps/api/Colden.Api/Features/<Area>/: a file per action with its request record, its FluentValidation validator and its handler, and the area's endpoint group that maps the routes with their policy, operation id, summary and response types. Organization endpoints hang off the organization route group, which checks the membership and sets the tenant; errors are RFC 9457 problem details; changes and their messages commit together through the outbox.
Its tests sit in apps/api/tests/Colden.Api.Tests/Features/<Area>/ and run the API against PostgreSQL in a container: the success path, each validation rule, 401 and 403 for the wrong callers, 404 for another organization's data, and the audit entry (testing).
4. The contract
Building the API regenerates packages/sdk/openapi/colden.v1.json; pnpm --filter @colden/sdk generate regenerates the TypeScript types from it. Commit both with the slice: the pipeline fails while either is stale, and the web cannot call an operation the document does not describe.
5. The screen
The page is a server component under apps/web/app/(app)/[org]/ that reads through the SDK; the interactive parts are client components in apps/web/components/<area>/. Forms use react-hook-form with zod and show the API's field errors on their fields. Components come from @colden/ui with Colden DS tokens only, and every string is a message in en-US.json and ro-RO.json. Unit tests use Vitest; a flow customers walk gets a Playwright journey with an accessibility check (tests/e2e/README.md).
6. The documentation
The same change updates the architecture file for any new slice, table, queue, message, flow or configuration key, the guide of the area, and CHANGELOG.md under today's CalVer heading. pnpm docs:links checks the links between the Markdown files; node apps/web/scripts/sync-docs.mjs refreshes the docs site's copy of a changed guide.
Work that is not a slice
- A schema change on its own follows step 2 with its tests.
- An external service (a provider, a third-party API, an inbound webhook) sits behind an abstraction with a fake for tests and a contract test against the real service; email is the model.
- A configuration key follows "Adding a key" in configuration.
- Recurring or queued work is a message and a handler in the worker, and a job when it runs on a schedule (messaging and jobs).
With an agent
The repository carries Claude Code skills for these steps, which an agent loads when the change matches:
| Skill | For |
|---|---|
| add-feature | steps 1 and 3 to 6: the slice, its tests, the contract, the screen, the documentation |
| add-migration | step 2: the model, the migration and its down path |
| add-integration | an external service |
| add-plan | a plan of several such changes, with its checker |
Review an agent's change as you would a person's: the tests it added must fail without the code, and the diff stays inside the slice and the files the steps name.