Saltearse al contenido

Monorepo Setup

Esta página aún no está disponible en tu idioma.

This example shows how to configure Contextia for a monorepo with multiple packages that share a single knowledge layer. One .contextia/ directory at the repository root governs specs, decisions, and norms across all packages.

The project is a SaaS platform with a shared API, a web frontend, a mobile backend-for-frontend (BFF), and a shared library of domain types and utilities.

platform/
├── .contextia/
│ ├── config.yaml
│ └── system/
│ ├── identity.md
│ ├── specs/
│ │ ├── SPEC-020.md # User registration (cross-package)
│ │ ├── SPEC-021.md # Notification delivery
│ │ └── SPEC-022.md # Shared validation rules
│ ├── rationale/
│ │ ├── DEC-020.md # Monorepo over polyrepo
│ │ └── DEC-021.md # Shared types package
│ └── norms/
│ ├── NORM-MONO-001.md # Cross-package import rules
│ ├── NORM-API-001.md # API response format (scoped to packages/api/)
│ └── NORM-WEB-001.md # Component patterns (scoped to packages/web/)
├── packages/
│ ├── api/ # FastAPI backend
│ │ ├── src/
│ │ └── tests/
│ ├── web/ # React frontend
│ │ ├── src/
│ │ └── tests/
│ ├── mobile-bff/ # Mobile backend-for-frontend
│ │ ├── src/
│ │ └── tests/
│ └── shared/ # Shared types and utilities
│ ├── src/
│ └── tests/
├── package.json # Workspace root (or pyproject.toml, etc.)
└── turbo.json # Build orchestration

The key configuration choice in a monorepo is listing all package source paths:

.contextia/config.yaml
project:
name: platform
languages:
- python
- typescript
source_paths:
- packages/api/src/
- packages/web/src/
- packages/mobile-bff/src/
- packages/shared/src/
annotations:
prefix: "@"
comment_syntax:
python: "#"
typescript: "//"
check:
ignore_patterns:
- "packages/*/tests/**"
- "packages/*/node_modules/**"
- "packages/*/.next/**"
- "packages/*/dist/**"
.contextia/system/identity.md
---
project: platform
version: "1.0"
---
## What is this
Platform is a SaaS product with four packages in a monorepo:
- **api**: FastAPI backend serving REST endpoints.
- **web**: React + TypeScript dashboard for browser clients.
- **mobile-bff**: Lightweight backend-for-frontend optimized for mobile clients.
- **shared**: TypeScript types, validation schemas, and utility functions used by all packages.
## Architecture
Monorepo managed with Turborepo. Each package has independent build and test pipelines.
The shared package is a build dependency of api, web, and mobile-bff.
Cross-package communication:
- web -> api: REST over HTTPS
- mobile-bff -> api: Internal gRPC calls
- All packages import types from shared
## Conventions
- Shared types are the single source of truth. Never duplicate a type from shared into a package.
- API changes must update the shared types first, then consumers.
- Each package has its own norms (component patterns for web, endpoint patterns for api).
- Cross-package norms apply to all packages.

Some behaviors span multiple packages. For example, user registration involves the API (creates the account), the web app (registration form), and shared (validation schemas):

.contextia/system/specs/SPEC-020.md
---
id: SPEC-020
title: User registration
description: End-to-end user registration spanning API, web frontend, and shared validation.
status: approved
decisions:
- DEC-021
norms:
- NORM-MONO-001
paths:
- packages/api/src/auth/registration.py
- packages/web/src/pages/RegisterPage.tsx
- packages/web/src/components/RegistrationForm.tsx
- packages/shared/src/schemas/user.ts
---
## Objective
Allow new users to register with email and password. Validation rules are
defined once in the shared package and enforced in both frontend and backend.
## Behaviors
WHEN a user submits the registration form with valid data
THEN the web frontend validates locally using shared schemas
AND sends a POST request to the API
AND the API validates again using the same shared schemas
AND creates the user account
AND returns a success response with the user profile.
WHEN the email is already registered
THEN the API returns HTTP 409 with error code "email_taken"
AND the web frontend displays the error message.
WHEN the password does not meet complexity requirements
THEN validation fails in both frontend and backend with the same error message
AND the shared schema defines the complexity rules.
.contextia/system/rationale/DEC-021.md
---
id: DEC-021
title: Shared types package for cross-package consistency
status: accepted
date: 2025-10-01
specs:
- SPEC-020
- SPEC-022
---
## Context
Multiple packages need the same data types (User, Task, Notification).
Duplicating types leads to drift -- the API accepts fields the frontend
does not send, or validation rules diverge.
## Decision
Create a `shared` package containing:
- TypeScript type definitions for all domain entities.
- Zod schemas for validation (used by web and mobile-bff).
- Python dataclasses generated from the TypeScript types (for api).
## Consequences
- Single source of truth for data shapes.
- Shared package becomes a build dependency for all others.
- Changes to shared require rebuilding all dependent packages.
- Need a generation step to keep Python types in sync with TypeScript.

Norms can apply to specific packages using the paths field:

.contextia/system/norms/NORM-API-001.md
---
id: NORM-API-001
title: API response format
status: active
paths:
- packages/api/src/**
---
## Rules
1. All endpoints MUST return the envelope format: `{ data, error, meta }`.
2. Error responses MUST include an error code (snake_case string) and a human-readable message.
3. List endpoints MUST support pagination with `offset` and `limit` query parameters.
4. All timestamps MUST be ISO 8601 in UTC.
.contextia/system/norms/NORM-WEB-001.md
---
id: NORM-WEB-001
title: Component patterns
status: active
paths:
- packages/web/src/**
---
## Rules
1. Components MUST be functional with hooks. No class components.
2. Each component directory contains: Component.tsx, Component.module.css, index.ts.
3. Components MUST NOT import directly from other packages. Use the shared package.
4. Page-level components live in src/pages/. Reusable components live in src/components/.
5. All user-facing text MUST use the i18n translation function, never hardcoded strings.
.contextia/system/norms/NORM-MONO-001.md
---
id: NORM-MONO-001
title: Cross-package import rules
status: active
paths:
- packages/**
---
## Rules
1. Packages MUST only import from `shared` for cross-package types. No direct imports between api, web, and mobile-bff.
2. The `shared` package MUST NOT import from any other package.
3. All cross-package types MUST be defined in `packages/shared/src/`.
4. Runtime dependencies between packages are forbidden. Use API calls for cross-service communication.
packages/shared/src/schemas/user.ts
// @spec SPEC-020
// @spec SPEC-022
import { z } from 'zod';
export const registrationSchema = z.object({
email: z.string().email('Invalid email format'),
password: z
.string()
.min(8, 'Password must be at least 8 characters')
.regex(/[A-Z]/, 'Password must contain an uppercase letter')
.regex(/[0-9]/, 'Password must contain a number'),
name: z.string().min(1, 'Name is required').max(100),
});
export type RegistrationInput = z.infer<typeof registrationSchema>;
packages/api/src/auth/registration.py
# @spec SPEC-020
# @norm NORM-API-001
# @norm NORM-MONO-001
from shared.schemas import RegistrationInput, validate_registration
from src.auth.service import create_user
async def register(data: RegistrationInput) -> dict:
"""Create a new user account after shared-schema validation."""
errors = validate_registration(data)
if errors:
return {"data": None, "error": {"code": "validation_failed", "details": errors}, "meta": {}}
user = await create_user(data)
return {"data": user.to_dict(), "error": None, "meta": {}}
packages/web/src/components/RegistrationForm.tsx
// @spec SPEC-020
// @norm NORM-WEB-001
// @norm NORM-MONO-001
import { registrationSchema, type RegistrationInput } from '@platform/shared';
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
export default function RegistrationForm() {
const { register, handleSubmit, formState: { errors } } = useForm<RegistrationInput>({
resolver: zodResolver(registrationSchema),
});
const onSubmit = async (data: RegistrationInput) => {
const response = await fetch('/api/auth/register', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data),
});
// handle response...
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
{/* form fields */}
</form>
);
}

When an agent works on a task that involves registration:

Terminal window
contextia context TASK-030

The context includes artifacts from all relevant packages, plus the cross-package norms. The agent sees the full picture — shared schema, API endpoint, and web form — and understands the constraints that span all three.