Repository navigation
ADR Index
Canonical source:
docs/13-adr/adr-index.md— this page is rendered from it on every publish; edit the source.
This document records the key architectural decisions made for the ClawAI platform, their context, rationale, and consequences. Each ADR follows the format: Context, Decision, Consequences, Status.
See also: docs/adrs/ for earlier ADRs written during initial development.
Date: 2025-Q1 Status: Accepted
ClawAI integrates with multiple AI providers, manages user auth, stores conversation history, handles file processing, logs audit events, and runs local AI models. These domains have different scaling profiles, different data stores, and different failure modes. A monolithic NestJS application would become a maintenance burden as features grow.
Split the backend into 13 independently deployable NestJS microservices, each owning a single bounded context:
- auth -- Authentication, authorization, user management
- chat -- Conversation threads, messages, context assembly, AI execution
- connector -- Cloud provider configuration, health, model sync
- routing -- Routing decisions, policies, Ollama-assisted routing
- memory -- Memory records, extraction, context packs
- file -- File upload, storage, chunking
- audit -- Audit events, usage tracking
- ollama -- Local model management, generation
- health -- Aggregated health monitoring
- client-logs -- Frontend log collection
- server-logs -- Backend log aggregation
- image -- Image generation
- file-generation -- AI-driven document generation
- Positive: Independent deployability; each service can be updated, scaled, or restarted without affecting others. Clear ownership boundaries. Different databases per service prevent coupling. Failure isolation -- a crash in the audit service does not affect chat.
- Positive: Teams can work on different services in parallel without merge conflicts.
- Negative: Operational complexity -- 17 services to monitor, deploy, and debug. Distributed tracing needed (X-Request-ID implemented).
- Negative: Network overhead for inter-service communication. Context assembly requires HTTP calls to memory and file services.
- Negative: Development environment requires Docker Compose with 22+ containers.
Date: 2025-Q1 Status: Accepted
Microservices that share a database are coupled at the data layer -- schema changes in one service can break another. We need strong data isolation between services while supporting relational queries, ACID transactions, and vector similarity search (for memory/embeddings).
Each service that needs relational storage gets its own PostgreSQL database instance:
-
claw_auth,claw_chat,claw_connectors,claw_routing,claw_memory(with pgvector),claw_files,claw_ollama,claw_image,claw_filegen
MongoDB is used for services with document-oriented workloads:
-
claw_audit(audit logs, usage ledger),claw_client_logs,claw_server_logs
- Positive: Complete schema independence. Each service evolves its schema via Prisma migrations without affecting others.
- Positive: pgvector extension available per-database for services that need vector similarity (memory service).
- Positive: MongoDB TTL indexes handle automatic log cleanup without affecting relational databases.
- Negative: 8 PostgreSQL + 3 MongoDB instances consume significant resources (~2GB RAM minimum for databases alone).
- Negative: No cross-database joins. Data that spans services requires HTTP or RabbitMQ communication.
- Negative: Backup and monitoring complexity scales linearly with database count.
Date: 2025-Q1 Status: Accepted
Services need to communicate for workflows like: chat publishes a message, routing processes it, chat receives the decision and executes. Some communication must be synchronous (context assembly fetches from memory service), but most event flows are inherently async and benefit from decoupling, retry logic, and failure isolation.
Use RabbitMQ with a topic exchange (claw.events, durable) for all asynchronous inter-service communication. Use HTTP for synchronous data fetching where a response is needed immediately.
-
RabbitMQ:
message.created,message.routed,message.completed,connector.synced,user.login, etc. - HTTP: Chat service fetching memories, file chunks, and context packs during context assembly.
Implement DLQ (Dead Letter Queue) with 3 retries and exponential backoff for all consumers.
- Positive: Temporal decoupling -- publisher and consumer do not need to be available simultaneously.
- Positive: Retry with DLQ handles transient failures without losing messages.
- Positive: Topic routing allows flexible event subscription (e.g., audit service subscribes to multiple event types).
- Positive: Load leveling -- consumers process at their own pace.
- Negative: Eventual consistency -- routing decisions are not instant (typically <100ms, but can be up to 10s with Ollama router).
- Negative: Debugging distributed event flows is harder than tracing a single HTTP call. Requires correlation IDs and structured logging.
- Negative: RabbitMQ is another infrastructure dependency to operate and monitor.
Date: 2025-Q1 Status: Accepted
ClawAI's core value proposition is intelligent routing with privacy guarantees. Some data must never leave the user's machine. Additionally, the routing engine needs a fast, local AI model to classify messages and select providers in AUTO mode. Using a cloud API for routing would add latency, cost, and a privacy paradox (sending data to the cloud to decide if it's safe to send to the cloud).
Run Ollama locally as the AI runtime for:
-
Routing:
gemma3:4bclassifies messages and selects optimal provider (temp=0, Zod-validated output). -
Memory extraction:
gemma3:4bextracts facts, preferences, instructions, and summaries from completed messages. - Fallback chat: Local models serve as the ultimate fallback when cloud providers are unavailable.
- Privacy-sensitive requests: LOCAL_ONLY and PRIVACY_FIRST modes route exclusively through Ollama.
Auto-pull 5 models on startup: gemma3:4b, llama3.2:3b, phi3:mini, gemma2:2b, tinyllama.
- Positive: Zero data leaves the machine for local-only operations. True privacy guarantee.
- Positive: No API cost for local operations. Routing decisions are free.
- Positive: No internet dependency for core routing logic.
- Positive: Configurable model roles allow swapping models without code changes.
- Negative: Requires significant local resources (~10GB disk for models, 4-8GB RAM during inference).
- Negative: Local model quality is lower than cloud models (4B params vs 100B+).
- Negative: Cold start latency (10-30s to load a model into memory).
- Negative: No GPU acceleration in Docker by default (requires nvidia-docker).
Date: 2025-Q1 Status: Accepted
NestJS traditionally uses class-validator with decorators for DTO validation. However, class-validator has limitations: no runtime type inference, verbose decorator syntax, no composability, and the validated output type must be manually synchronized with the class definition.
Use Zod for all input validation across all services. Define schemas as z.object() declarations, infer TypeScript types from schemas using z.infer<>. Use a custom ZodValidationPipe in NestJS to integrate with the standard validation pipeline.
// DTO pattern
export const CreateThreadSchema = z.object({
title: z.string().min(1).max(200),
routingMode: z.nativeEnum(RoutingMode),
});
export type CreateThreadDto = z.infer<typeof CreateThreadSchema>;- Positive: Single source of truth -- schema defines both validation rules and TypeScript type.
- Positive: Runtime validation matches compile-time types automatically.
- Positive: Composable -- schemas can extend, merge, and pick from each other.
- Positive: Used in both frontend (form validation) and backend (DTO validation) for consistency.
- Positive: Better error messages out of the box.
- Negative: Not the NestJS default -- requires custom pipe integration.
- Negative: Team members familiar with class-validator need to learn Zod patterns.
- Negative: Some NestJS Swagger/OpenAPI generators expect class-validator decorators.
Date: 2025-Q1 Status: Accepted
The message flow requires the chat service to send a message to the routing service for provider selection, then receive the result to execute the AI call. Two approaches were considered: (1) synchronous HTTP call from chat to routing, or (2) event-driven via RabbitMQ.
Use event-driven routing:
- Chat publishes
message.createdevent. - Routing consumes event, makes decision, publishes
message.routedevent. - Chat consumes
message.routed, proceeds with execution.
- Positive: Chat and routing services are fully decoupled. Either can be restarted independently.
- Positive: Routing decisions are recorded as events, creating an audit trail.
-
Positive: Multiple consumers can react to
message.created(e.g., future analytics service). - Positive: Routing service can be replaced or upgraded without touching chat service.
- Negative: Additional latency (RabbitMQ publish + consume + publish + consume adds ~10-50ms).
- Negative: More complex error handling -- what if routing never responds? (Handled by timeout + heuristic fallback.)
- Negative: Debugging requires following events across services via correlation IDs.
Date: 2025-Q1 Status: Accepted
AI model responses should stream to the frontend token-by-token for a responsive UX. Two options: WebSocket (bidirectional, persistent connection) or Server-Sent Events (unidirectional, HTTP-based).
Use Server-Sent Events (SSE) for streaming AI responses from the chat service to the frontend.
- Positive: SSE works over standard HTTP -- compatible with Nginx reverse proxy without special configuration (beyond buffering disabled).
- Positive: Automatic reconnection built into the SSE protocol.
- Positive: Simpler server implementation (no WebSocket upgrade, no connection state management).
- Positive: Works with standard HTTP authentication and CORS.
- Negative: Unidirectional only (server to client). Client-to-server communication still uses HTTP POST.
- Negative: HTTP/1.1 has a 6-connection-per-domain limit in browsers (not an issue with HTTP/2).
- Negative: Less efficient than WebSocket for very high-frequency bidirectional messaging (not our use case).
Date: 2025-Q1 Status: Accepted
With 17 backend services on different ports, the frontend cannot manage connections to each individually. We need a single entry point that routes requests to the correct service based on URL path.
Use Nginx as a reverse proxy on port 4000. All API requests go through Nginx, which routes based on URL prefix:
-
/api/v1/auth/*-> auth-service:4001 -
/api/v1/chat-threads/*-> chat-service:4002 -
/api/v1/connectors/*-> connector-service:4003 - (20+ route mappings total)
Configure Nginx with SSE support (proxy_buffering off, chunked transfer encoding).
- Positive: Frontend connects to a single URL (port 4000) for all API calls.
- Positive: Nginx handles SSL termination, request buffering, and static file serving.
- Positive: SSE support with proper buffering configuration.
- Positive: Easy to add rate limiting, IP allowlisting, or caching at the proxy layer.
- Negative: Nginx is an additional component to configure and maintain.
- Negative: Route configuration is in nginx.conf, separate from application code -- must be updated when adding new endpoints.
- Negative: Nginx becomes a single point of failure (mitigated by its high reliability and simple configuration).
Date: 2025-Q1 Status: Accepted
ClawAI has 17 backend services, 1 frontend, and 5 shared packages that all need to share enums, types, and utilities. Options: polyrepo (each service in its own repository), monorepo with Turborepo/Nx, or monorepo with npm workspaces.
Use a monorepo with npm workspaces. All 18 packages live in a single repository under apps/ and packages/.
-
Positive: Single
npm installinstalls all dependencies across all packages. -
Positive: Shared packages (
shared-types,shared-constants,shared-rabbitmq,shared-auth) are workspace dependencies -- changes are immediately available to all consumers. - Positive: Atomic commits -- a change to a shared type and all its consumers can be in one commit.
- Positive: Unified CI/CD pipeline (lint, typecheck, test, build all at once).
- Positive: No need for package publishing or version management for internal packages.
-
Negative: Large repository size;
npm installis slower than single-package install. - Negative: CI runs all checks even when only one service changed (no incremental builds without Turborepo).
- Negative: All developers need the full repo; cannot clone just one service.
Date: 2025-Q1 Status: Accepted
The browser EventSource API is the standard way to consume SSE streams. However, EventSource has a critical limitation: it does not support custom HTTP headers. ClawAI requires the Authorization: Bearer <token> header on all API requests, including SSE connections.
Use the fetch API with ReadableStream to consume SSE streams instead of the native EventSource API. Implement a custom SSE parser that reads the stream, splits on \n\n delimiters, and extracts data: fields.
-
Positive: Full control over request headers --
Authorizationheader is sent with SSE requests. -
Positive: Can include any custom headers (e.g.,
X-Request-IDfor correlation). - Positive: Works identically across all modern browsers.
- Positive: Can implement custom retry logic and connection management.
- Negative: No automatic reconnection (must implement manually).
-
Negative: More code than
new EventSource(url)-- requires stream reading, parsing, and error handling. - Negative: Must handle edge cases: partial chunks, multi-line data fields, keep-alive comments.
| ID | Decision | Status | Key Driver |
|---|---|---|---|
| 001 | Microservices (17 services) | Accepted | Failure isolation, independent deployment |
| 002 | PostgreSQL per service | Accepted | Data isolation, independent schemas |
| 003 | RabbitMQ for async | Accepted | Reliability, retry/DLQ, decoupling |
| 004 | Ollama local AI | Accepted | Privacy, cost, routing independence |
| 005 | Zod over class-validator | Accepted | Type inference, composability |
| 006 | Event-driven routing | Accepted | Decoupling, auditability |
| 007 | SSE over WebSocket | Accepted | Simplicity, HTTP compatibility |
| 008 | Nginx reverse proxy | Accepted | Single entry point, SSE support |
| 009 | npm workspaces monorepo | Accepted | Shared code, atomic changes |
| 010 | fetch-based SSE | Accepted | Auth header support |
| 018 | Universal webhook receiver | Accepted | One signed entry-point per provider |
| 019 | Auto-suggest scheduler | Accepted | Cron + advisory locks across replicas |
| 020 | Suggestion factory pipeline | Accepted | Single entry-point for events → queue |
| 021 | Write-action adapter pattern | Accepted | Uniform executeWriteAction per adapter |
| 022 | HTML email sanitisation | Accepted | DOMPurify + iframe sandbox; service-token /upload-internal |
| 023 | Calendar providers | Accepted | GoogleCalendar + OutlookCalendar adapters; MEETING object |
| 024 | Inbox + pgvector search | Accepted | Cross-provider inbox + memory-service embeddings table |
| 025 | Digest dashboard | Accepted | Hourly cron + advisory lock + Intl.DateTimeFormat tz match |
| 026 | User-pref intersection | Accepted | Most-restrictive-wins (admin > user) |
| 027 | Memory learning loop | Accepted | Heuristic v1; LLM classifier v1.x |
| 028 | IMPL_PROMPT handoff | Accepted | Workspace ↔ chat/agent bridge with secret scanner |
| 029 | Capability framework | Accepted | Generalises agent + workspace approvals |
| 040 | Router model registry | Accepted | Canonical model identity store (Phase 1) |
| 041 | Cost confidence annotation | Accepted | EXACT/ESTIMATED/UNKNOWN; uncertainty penalty |
| 042 | RoutingDecisionV2 Zod schema | Accepted | Strict output contract for the router (Phase 7) |
| 043 | Route-only contract | Accepted | Filter router-only models before scoring |
| 044 | Learned scores split table | Accepted | Per-(profile, domain, taskFamily) bounded updates |
| 045 | Persisted circuit breakers | Accepted | DB-backed; survives container restart |
| 046 | Simulator same code path | Accepted | Phase 13 dry-run uses production evaluator |
| 047 | 14-dim scoring weights | Accepted | Per-RoutingMode weight vectors sum to 1 |
| 048 | Workflow vs. model decision | Accepted | Workflow selection inside router (Phase 9) |
| 049 | Local TLS everywhere (mkcert) | Accepted | One install command, end-to-end HTTPS incl. service-to-service |
| 050 | Critic as sibling plan feature of Judge | Accepted |
allowCriticReview flag + user-selectable critic model + parse-failed marker |
| 051 | Narrow workspace VIEW + CONNECT permissions for USER | Accepted |
WORKSPACE_VIEW + WORKSPACE_APP_CONFIG_VIEW; partial-relax over admin-only |
| 052 | Shared RichPromptTextarea + use-sticky-bottom-scroll hook |
Accepted | One autosize textarea everywhere; one auto-follow scroll behaviour everywhere |
| 053 | File retention sweeper + ZIP archive guardrails | Accepted | Nightly cron sweep + 4 ZIP bomb hard caps + tmpfs sandbox |
| 054 |
file_delivery_records extracted from JSON |
Accepted | Per-model delivery records → typed table; 30-day dual-write window |
| 055 | Canonical AI authority hierarchy | Accepted | CLAUDE.md > rules/00 > context/arch+stack > rules > skills > context+memory > .ai > routers |
| 056 | Generated .ai/ knowledge layer |
Accepted | 19 manifests + BOOTSTRAP + workspace AGENTS.md, all derived from source |
| 057 | Deterministic context resolver | Accepted | Lexical/structural retrieval, no external AI dependency |
| 058 | Compact AI routers, not mirrors | Accepted | ~90-line per-tool routers; canonical wins on conflict |
| 059 | Split ESLint architecture + plugin | Accepted (plugin) / Proposed (root split) | Tested custom rules now; root-config decomposition deferred to its own slice |
| 060 | Affected-workspace validation | Accepted | Diff → owning workspace + dependents; root changes stay local-scoped |
| 061 | Git-hook policy — no --no-verify |
Accepted | Hooks call the affected lane; bypass recommendations removed + scanned |
| 062 | Testing-runner retention | Superseded by ADR-099 | Keep Jest (backend) + Vitest (frontend) + Playwright (E2E); no forced migration |
| 063 | Coverage targets | Accepted | ≥95%/90% branches target, ratcheted in, 100% branch on pure critical logic |
| 064 | Refund ledger and entitlement policy | Accepted | Partial preserves access; cumulative full refund revokes; DB-locked aggregate |
| 065 | Immutable invoice documents and durable delivery | Accepted | DB-immutable facts; transactional delivery intent; owned PDF download; shared SMTP adapter |
| 066 | Purpose-constrained checkout sessions | Accepted | One callback path; DB-enforced subscription/setup purpose invariant |
| 067 | Owner-token Redis locks for scheduled jobs | Accepted | NX lease + atomic owner compare/delete; bounded idempotent work |
| 068 | Session-bound user tokens | Accepted | Hashed refresh rotation, replay revocation, strict JWT claims, one-time sign-out |
| 069 | Router learned-score retirement | Accepted |
RouterModelProfile/RouterTopicProfile is the one production learning system; RouterLearnedScore retired dead-but-harmless |
| 070 | Workspace-tier hierarchical priors | Accepted | Workspace tier only (user tier deferred, no userId concept exists); inert until chat-service threads a real workspace id |
| 071 | Discovery stylesheet and feed content negotiation | Accepted |
xml-stylesheet on every discovery document; feeds pick their type from Accept with Vary: Accept
|
| 072 | AI answer-engine crawler policy and the comparison cluster | Accepted | Named crawler groups sharing one allow/disallow pair; /llms.txt; /compare/* on fixed axes, translated, no fabricated review markup |
| 073 | Super-administrator authority | Accepted | One pure scope predicate + one DB actor read; per-scope self-exemption; no JWT claim; three refusal codes |
| 074 | Plan signup flag and popular badge | Accepted |
isPopular + nullable unique popularKey; migration never writes isDefault; both flags stay behind their own endpoint |
| 075 | Public share assets | Accepted | A share owns copies of its images; unscanned images cost ad and index eligibility, never readability |
| 076 | Chat stream durability | Accepted | The chat SSE bus stays in-process; the database plus polling is the record, and chat-service runs exactly one replica |
| 077 | chat-service horizontal scaling | Accepted | The stream bus and Stop move to Redis so chat-service runs 4 replicas; deploys roll one replica at a time |
| 078 | PAYG connector credit | Accepted | Credit IS the promoted monthlyProviderCostCeilingMicroUsd; 1 weighted token == 1 micro-USD, so a third column would be a third name for one number |
| 079 | Auth learns model prices over a cached internal call | Accepted | routing-service stays the price owner; auth caches 300 s, busted by routing.model_cost.published; fails closed |
| 080 | One reservation, not two | Accepted |
RESERVE_QUOTA_LUA 7 to 9 windows + 3 WeightedUsageRecord columns; no CreditReservation table, no second atomicity domain |
| 081 | Retire the routing cost-budget scaffold | Accepted (supersedes routing stream R.4) | Never registered, no schema, SCAFFOLD-R4 throw, 7 unguarded handlers; per-user spend capping is the auth wallet |
| 082 | PAYG classification grain | Accepted | Server-side in auth only, at PROVIDER grain rolled up from Connector.isPayAsYouGo; never in shared-entitlements
|
| 083 | Credit top-up as a third checkout purpose | Accepted (amends ADR-066) | Third CHECK branch; fixed server-priced packages; price-to-credit ratio a column seeded at 0.60, never 1:1 |
| 084 | SEO clusters fan out from one dynamic route | Accepted | One [topic] segment + order array per cluster; all five layers fan out; sitemap-coverage learns dynamic segments; Lighthouse samples on PR |
| 086 | The Context Composer, and why relevance may never remove a message | Accepted | Relevance ranks, only the token budget removes; turns not messages; maxTokens is output-only; every generation emits a context manifest |
| 087 | Cross-thread retrieval, and why it is off by default | Accepted | Opt-in per thread, never read when off; two stages; a coined identifier is the precision gate; ranks on message evidence, not the title |
| 088 | The chat composer sizes itself, and the drag handle is gone | Accepted | Row range replaces a fixed pixel height; reuses RichPromptTextarea; composer stays in normal flow so rule 36 clearance never applies |
| 089 | Client telemetry ships as batches to a second endpoint | Accepted |
POST /client-logs/batch + insertMany({ordered:false}); collapse repeats to an occurrences count; DEBUG stops at the network in production; telemetry exempt from the auth-refresh flow |
| 090 | The model picker opens at the current choice, and its trigger always names it | Accepted | cmdk's value is the highlight, so seeding it on open IS scroll-to-selection; item value becomes the id with the label in keywords; a narrow trigger shows a short label instead of nothing |
| 091 | A URL the user writes is opened, not searched for | Accepted | Direct fetch before search in the research pipeline; pasted pages outrank discovered ones; the prompt now states what was ATTEMPTED and which tools ran |
| 092 | Multi-page site crawl reuses FetchService, no new fetch path | Accepted |
SITE_CRAWL workflow; robots.txt/sitemap.xml/pages all go through the same FetchService; sitemap first, homepage links only when it's thin; crawled pages are source:'fetch', no new schema |
| 093 | Mid-generation crawl retrieval bypasses the callProvider chokepoint |
Accepted |
get_crawled_page reuses the previously-unwired runOllamaCloudToolLoop, Ollama Cloud only; resolved from metadata.research.bundle in memory, no network call; bypasses callProvider's single hold because the loop takes its own hold per turn |
| 094 | Headless-browser rendering is a fallback INSIDE FetchService | Accepted |
HeadlessFetchAdapter (Playwright/Chromium) retries a thin plain-fetch result, never a second fetch path; every in-page subrequest gets the same anti-SSRF check the top-level navigation gets, not just the initial URL |
| 095 | Attachments are extracted to text, and the text is a column | Accepted |
processFile had no caller from upload; extracted_text is its own column, not a chunk rejoin; no bulk backfill (it would poll forever), legacy rows heal on use; OOXML read in-memory under ADR-053-equivalent bounds |
| 096 | A login tells you why, only after you have proved the account is yours | Accepted | Password verified FIRST, before any account-state branch, so unknown-address and wrong-password are identical in body AND cost (burnPasswordVerification); EMAIL_NOT_VERIFIED/ACCOUNT_SUSPENDED are safe to name only post-verification; registration lands on /check-email, never /login; every auth email localised to 13 languages |
| 097 | Display FX is a separate subsystem from settlement FX | Accepted | Three amounts named and never merged — CANONICAL (immutable USD price), DISPLAY (current rate, commercially rounded, fails OPEN to USD) and SETTLEMENT (frozen FxQuote, safety margin, fails CLOSED); DisplayFxRate carries no quoteId/expiresAt/safetyMarginBps so it cannot reach a gateway adapter; display currencies are a separate, wider allowlist than billing currencies |
| 098 | AUTO research is an AI-driven, narrated loop that runs after the request returns | Accepted | A planner (admin's ordered models, next model on a bad reply) chooses answer/crawl/search/crawl-then-search and is asked again after a crawl; runs after the POST with message.created in finally; chat calls research over an internal service-token route (the user route 403'd every non-admin); one shared URL detector with bare domains; every step narrated to a deduped Redis log and stored as metadata.narration
|
| 099 | Vitest in every workspace; Playwright stays for E2E | Accepted | Supersedes ADR-062. All 20 apps + 6 packages run vitest run since 63e7f7806 (2026-09-17); backend configs use unplugin-swc for decorator metadata; coverage thresholds in vitest.config.ts; Playwright unchanged for E2E; gates stay runner-agnostic |
| 100 | The AUTO router picks from exposed models; every prompt fits its window | Accepted | Candidates = admin-EXPOSED chat models (health + plan filtered, ACTIVE first, round-robin by provider, cap 30) instead of 4 ACTIVE Gemini-heavy rows; OLLAMA_CLOUD planner calls ollama.com with the connector key (prod has no ollama-service); one knownContextWindow table in shared-utilities; research/files/packs/memories get 45/25/10/5% of the window; fast path escalates on length
|
| 101 | Every container log goes to one store, shipped read-only by Vector | Accepted |
x-claw-logging anchor (json-file, 20m x5, claw.service label) on every service; Vector reads json-file logs READ-ONLY (never the Docker socket); /server-logs/ingest/containers parses pino/Nest/plain into real levels; the public log-write routes now need the service token |
| 102 | Read-only ops tokens are the no-SSH channel into production logs | Accepted | auth-service mints claw_ops_ tokens (SHA-256 stored, shown once, LOGS_READ only, 1-90 day expiry, 10 per admin, revocable, use-counted); GET /api/v1/ops/logs* on server-logs with Authorization: Ops; guard verifies via auth internal route, caches 30 s, fails closed; the Ops scheme never opens a session route |
| 103 | File requests need a file word; file writers are admin-managed; file jobs are owner-only | Accepted |
detectFileIntent (whole words, negation, question guard, strong vs soft words, 41 cases) replaced substring verb+format; FILE_WRITER assistant role replaced 3 hard-coded models; cloud router records routerModel and every AUTO answer says who routed; retry/events owner-only, internal routes service-token |
| 104 | Generated files download through an owner-only proxy and expire after an hour | Accepted |
/file-generations/:id/assets/:assetId/download (owner guard, streamed, attachment + no-store + nosniff, sanitised name, no storageKey in JSON); 1-hour expiresAt, 410 FILE_EXPIRED, 5-min sweep deletes bytes; rebuild from saved text; first-ever init migration (prod had no tables) |
| 105 | Any AI answer exports as a file through the file pipeline, with no model call | Accepted | md/txt built in the browser; html/docx/pdf via POST /file-generations/export (provider EXPORT, no tokens, ADR-104 link); expand dialog rendered/raw; JSON body limit tied to DTO bounds by a spec (100kb default failed every file over ~100k chars as a 500); prompt bound 4,000 → 100k to match chat |
| 106 | Sessions survive many tabs and windows, and "Remember me" sets the session's lifetime | Accepted | 30 s refresh-reuse grace issues a sibling instead of revoking the family; one refresh across tabs (Web Locks) that adopts a token another tab got; storage is the truth (setUser reads it, storage events rehydrate); only 400/401/403 end a session; rememberMe → session.persistent (7 d vs 12 h + browser-close) |
| 107 | Answers render through one Markdown parser, and PDFs are typeset by Typst | Accepted | parseMarkdownDocument → typed blocks for every format; PDF via Typst (answer text only as escaped string literals, empty workspace, Noto fonts in image); DOCX real runs/lists/tables/bidi; HTML title+dir; CSV/JSON from the first table; pdfkit removed |
| 108 | Answers export as spreadsheets, slide decks and zip bundles | Accepted | XLSX written as SpreadsheetML (inline strings, no formulas, sheets per table); PPTX via pptxgenjs; ZIP of README + code files + table CSVs with zip-slip-safe paths; CSV formula-injection guard; chat detects formats by whole words and gives the writer per-format instructions; whole-fence unwrap bug fixed |
| 109 | The AI names its files, from its own title | Accepted | writer opens with a # title; deriveFileIdentity → title (humanTitle), description (first paragraph), unicode filename; request fallback; RFC 5987 filename*; card shows title + description; no extra model call |
| 110 | AI-written files have a daily allowance per plan; exports are free | Accepted | FILE_GENERATION feature key (Free 15, Starter 30, Plus 75, Pro 200 a day, Team+ uncapped); first metered feature that refuses: reserve before the model writes, consume/release after; refusal is a translated in-chat notice; fail-open if auth is down |
| 111 | What the model matrix changed about writing files | Accepted | Found by F4 (1,500 real files) + F5 pentest: one Content-Disposition builder (non-Latin downloads 500d), no INSTRUCTION memories in CSV/JSON writers, CSV as a Markdown table, names cleaned of what file-service refuses, not-found is 404 |
| 112 | A revoked session stops at the guard, not at the token's expiry | Accepted | TD-033: auth-service publishes revoked session ids to Redis with the access lifetime as TTL; a second global SessionRevocationGuard in all 17 services asks, fails open, and only ever refuses |
| 113 | Prometheus holds operational metrics; health-service exports them | Accepted | One Prometheus container, never published, scraping health-service's existing fan-out over TLS; 30-day retention tied to the log TTL by a test; the snapshot is cached so a scrape is not a fan-out; a metric carries only allowlisted labels |
| 114 | Every archive format opens through 7-Zip compiled to WASM; ZIP keeps node-stream-zip | Accepted | 7z/RAR/tar/gz/bz2/xz via 7z-wasm (no native addon, lists before extracting, -smemx, passwords for A3); one shared bomb/traversal/link policy for both engines; streams bounded while writing; magic bytes decide what an upload is, so an octet-stream archive is expanded and a lying archive MIME is rejected; LGPL + unRAR (read-only use complies) |
| 115 | Grafana sits behind the admin session, not its own login | Accepted | auth-service mints claw_grafana (httpOnly, Secure, Lax, Path=/grafana, ≤ access-token life) for ADMIN only, signed with a key derived from JWT_SECRET; nginx auth_request checks it and overwrites X-WEBAUTH-EMAIL; Grafana auth.proxy only, no built-in admin, never published; same-origin XSS exposure is TD-041 |
| 116 | Compare's judge scores every lane in one comparative call | Accepted | ONE judge call ranks every completed lane, anonymised A/B/C in a seeded shuffle, on a shared 0-10 scale, parsed against strict cross-field invariants; UNAVAILABLE/SKIPPED never picks a fake winner; fair max-min truncation to the judge's window; single hold via the billing chokepoint (compare-judge workflow); single-lane critic still runs per lane, its score never reaches the judge |
| 117 | OpenAI-compatible connector presets are one registry and one adapter | Accepted |
CONNECTOR_PRESETS in @claw/shared-utilities is the only place a preset's base URL/display name/links live; one OpenAICompatibleAdapter for 15 new providers; explicit enum values (not a generic OPENAI_COMPATIBLE + presetKey) to keep existing provider-keyed tables working; base URL resolved once in getExecutionConfig; repo test enforces no duplicate URL/name outside the registry |
| 118 | Orchestration lanes share the grounding signal, not just the evidence text | Accepted | Compare/consensus/escalation merged research evidence into systemPrompt as prose but never set researchRequested/researchEvidence, so hasResearchGrounding() never fired and the final-user-turn reminder (proven necessary 2026-09-11) silently never reached any orchestration lane; new injectResearchEvidenceIntoContext (replaces 3 duplicate copies) sets researchGroundingInjected; shared "do not invent" instruction now in both evidence-block builders' non-empty branch; consensus synthesis now prefers cited lanes over longer uncited fabrications when the run had evidence |
| 119 | File and image requests work in every routing mode | Accepted | A picked model got text only (0/52 live): routing now detects file/image intent before every non-AUTO handler; MANUAL_MODEL's model writes its own file (fileWriter), local-only modes use local writers; Unicode intent in 13 locales; writer <thought> blocks stripped; separator-less pipe tables repaired |
| 120 | ClawAI owns multimodal orchestration | Accepted | The selected model is not the media executor: chat-service reads per-model image/audio/video capability from the connector catalog (ModelCapabilityClient, shared modelMatchKey), resolves every attachment per lane (resolveAttachmentDelivery), and the one decision drives both the payload (no image_url to a model that cannot see) and the FileDeliveryMode record; UNKNOWN falls back to the old provider-level behaviour; compare budgets for its smallest lane |
| 121 | Every research fetch goes through a DB-gated escalation chain | Accepted | robots.txt (RFC 9309, now with */$) on every fetch; chain official API → plain → impit TLS → Patchright → Crawl4AI/FlareSolverr/Firecrawl sidecars (compose profiles, DB-disabled) → Jina Reader → labelled Wayback copy; refusals (401/451) stop, captcha/429/404/robots-unreachable go archive-only; ≤6 attempts, ≤60 s, per-host politeness; every redirect hop SSRF-checked; one Readability extraction; fetch.served log + fetch_jobs.servedBy; Crawlee not adopted |
| 122 | Media features are plan-gated server-side at the executing service | Accepted | Free keeps image understanding, voice notes and 60 s video; paid tiers get allowImageGeneration, allowHelperVision, allowTextToSpeech and 600 s (maxVideoSeconds, null unlimited / 0 disabled); image-service refuses 403 PLAN_FEATURE_DISABLED before any row, hold or provider call on generate/retry/retry-alternate; chat turns it into a translated notice; helper vision keeps OCR + note; fail closed |
| 123 | Release version bumps are not deploy changes | Accepted | deploy-prod.sh drops a package.json/package-lock.json whose only diffs are release versions (own version, lockfile workspace versions, exact @claw/* pins) via a python3 JSON compare, fail-safe without python; frontend manifest exempt (baked APP_VERSION); BuildKit cache bounded before every build and on every exit, not only after health |
| 124 | Provider-key credit: pre-flight cap, one retry, no provider text | Accepted | OpenRouter 402 "can only afford N" + raw body with key-hash URL reached the user: one classifier (toProviderHttpFailure) for every provider hop maps credit refusals to PROVIDER_CREDIT_EXHAUSTED (503, translated, chain continues) and never forwards URL-carrying text; hosted default output 16,384 not ~31.7k; OpenRouter key credit read via preset creditHeadroom (connector-service, 60 s cache, service-token route) and priced from routing rate card to cap max_tokens before the PAYG hold (fail open); one retry at 90% of N as a distinct hold |
| 125 | Provider refusals recover at the chokepoint, for every provider | Accepted | Groq/Ollama output-limit, OpenRouter :free 429, OpenAI/Anthropic/Gemini credit outages hit users (some as raw JSON via compare/consensus Error: {…}): classifier adds output-limit and rate-limit outcomes and account-wide credit; one retry per turn (stated ceiling / 1.5 s backoff / 90% N), own hold :provider-retry; output ceilings stored on connector_models (catalog + learned, only lowered) and pre-clamped via the models-snapshot; 10-min half-open in-process breaker skips a provider whose account is empty; lane errors sanitized |
| 126 | Every AI, one workspace: ClawAI is positioned as a full AI workspace | Accepted | Owner decision 2026-09-26: slogan "Every AI, one workspace", four pillars (workspace, pay-as-you-go, teams, local-first); one canonical home (product-vision.md) and a code-traced flagship catalog (flagship-features.md); teams copy bounded by what exists — no SSO, no multi-member team billing; no price changes |
| 127 | Memories and context packs are stored whole and retrieved by relevance | Accepted | A 256-char redaction preview was stored as the memory; type stripped on update; 50K limits + 100 KB body cap; packs reached chat only if attached; REDACTED memories retrieved as null and big ones head-cut. Now: length-preserving masking, 250K, owner-scoped for-chat route with USER-scope auto-apply, relevance-ranked markdown chunks inside the rule-51 shares |
| 128 | Free local speech: one CPU container, offered as the LOCAL provider | Accepted |
speech (speaches 0.8.3-cpu, local-ai profile, no port) serves OpenAI-compatible STT + TTS; models installed by the compose entrypoint (no lazy pull: uninstalled = 404); LOCAL reuses the OpenAI adapters, is last in STT and read-aloud order, offered only while /health answers, and unmetered via PAYG_EXEMPT_PROVIDERS; LOCAL_SPEECH_BASE_URL; image-only so no workspace/ci entry |
| 129 | Runtime V2 turns offer the admitted tools natively | Accepted | The native tool transport was built and never used: agent turns asked for JSON in text and gpt-oss scored 0/8. Turns now declare the admitted catalog; a native call wins over text under the same schema + admitted-tool checks; unmappable native calls get the repair turn; over-budget catalogs fall back instead of failing |
| 130 | Branch lineage, and a branch family never feeds itself | Accepted | Branch copy lost metadata/attachments, cost and createdAt (undefined order), reset privacy switches, and cross-thread retrieval let a branch read its source's post-fork messages. Now: branched_from_thread_id/branched_from_message_id/branch_root_thread_id (plain ids), full-field copy, switches inherited, whole branch family excluded from cross-thread retrieval, owner-scoped GET /chat-threads/:id/lineage + lineage strip |
| 131 | Quotes are structured, provenance-checked, and assembled per request | Accepted | Select text in one message, Quote, chips in the composer, quotes: [{sourceMessageId, text}] (at most 3 x 2,000) on POST /chat-messages; each id checked under the thread (404 QUOTE_SOURCE_NOT_FOUND); stored in metadata.quotes, content unchanged; every prompt builder wraps quoted turns with withQuotedContext; routing and memory relevance see the quote |
| 132 | Regenerate with a chosen model, and edit without losing history | Accepted | Regenerate takes an optional {routingMode AUTO/MANUAL_MODEL, provider, model} and now passes assertCanSendMessage + carries allowedModels/modelAccessMode like a new message (it skipped both); "Try again with…" reuses the composer's ModelPicker; branch takes cut: INCLUDE/BEFORE; "Edit in a new branch" branches BEFORE the message and prefills the edited text without sending |
| 133 | Inline citations link only through the evidence the model was shown | Accepted |
[n] rendered as plain text; the transcript is not the list the model saw. Now the answer stores metadata.citations (the prompt's own numbering, from the same context), a remark plugin links [n] only for a stored index (never in code), only http(s) opens, memo compares citations by value |
| 134 | A model decides "remember this / add this to my context", and asks which pack | Accepted | Owner direction 2026-09-30, supersedes ADR-127's "no model path": multilingual pre-filter → planner model JSON verdict (memory type+sentence, pack source/name, both); saves before the answer, model confirms from a platform note; metadata.contextSave card with deep links; unnamed pack + existing packs → "which pack?" card, POST /chat-messages/:id/context-save claimed atomically; keyword path kept as fallback |
| 135 | Term discounts are per-plan settings that derive the price versions | Accepted | Owner direction 2026-09-30: quarterly 10% / semiannual 15% / yearly 20% off by default, per-plan basis-point columns an admin edits; longer-term PlanPriceVersion rows are derived from the monthly price and minted in one transaction (publishPriceSet); a hand-typed longer-term price is refused; existing plans re-priced once by a seeder; the badge is read off the stored prices |
| 136 | Every model is told, invisibly, where it is running | Accepted | Owner direction 2026-09-30: a hidden platform-identity block first in every assembled system message (never a memory or context item, never in a receipt or API response, address from PUBLIC_SITE_URL, budgeted in the window); a question about the app crawls the platform's own site; coding-agent threads still to do |
| 137 | Video generation is its own metered surface, priced per second | Accepted (built, run live 2026-09-30) | Owner direction 2026-09-30: PaygSurface.VIDEO, videoSeconds x videoPerUnitMicroUsd (Veo 3.1 Lite/Fast/Standard, Grok Imagine Video), seed v11, OpenAI Sora not offered (API shut down 2026-09-24); image-service jobs, chat routing and the video bubble ship with it |
| 138 | A per-user prompt library in chat-service | Accepted | Saved prompts with {{variables}}, tags and favourites; prompt_templates owned by chat-service, scoped by JWT user (foreign id = 404), 200-per-user cap, server extracts/validates variables only, nginx route chat-prompt-templates
|
| 139 | PAYG credit unlocks image and video generation | Accepted | Owner direction 2026-10-01: a Free-plan user with credit may generate images and video, paid from credit; hasPaygCredit in entitlements, only allowImageGeneration is credit-unlockable |
| 140 | The free-trial length is a per-plan setting | Accepted | Owner direction 2026-10-01: Plan.trialDurationDays 1-3650 editable in the admin dashboard, admin can add trial days and set a user to Free for N days, days left shown in the banner and pricing |
| 141 | Public and authenticated feedback are two APIs | Accepted | Owner direction 2026-10-01: POST /feedback/public (no login, name+email, honeypot, per-IP and per-email limits, no uploads) and the guarded POST /feedback (saved with the user's full name); admin sees source, name, email |
| 142 | Free accounts get monthly requests on credit connectors | Accepted | Owner direction 2026-10-01: Plan.creditConnectorFreeRequestsPerMonth (Free = 10, null unlimited, 0 off), credit first then allowance, chat-type surfaces only, atomic per-user per-provider UTC-month counter, count given back on release |
| 142 | The runner version policy is staged, fail-open and unsigned | Accepted (staged) | Owner decision 2026-10-01: minRunnerVersion/allowedRunnerPlatforms/runnerPolicyMode (off default, report, enforce)/requireVersionReport on org policy; report never rejects, enforce rejects only a definite violation, any read error fails open; self-reported, attestation waits for the signing-key decision |
| 143 | Per-routine secrets are encrypted, write-only and bound to their routine | Accepted (server side built; runner pending) | Owner direction 2026-10-01 (F099 step 2): routine_secrets ciphertext (AES-256-GCM, ENCRYPTION_KEY, per-row nonce, AAD routine+owner+name), owner CRUD with write-only values and a foreign routine = missing 404, delivered only in the runner claim response (WEBHOOK runs need an owner opt-in), output scrubbed on completion; threat model
|
| 144 | A narrower mobile device token class |
Accepted (backend built; no native client) | Owner direction 2026-10-01 (F097): DeviceTokenClass.MOBILE in agent-service (where device tokens live), scopes runs:read/approve/cancel only, signed with a key derived from JWT_SECRET, default-deny @MobileRoute() allow-list of ten routes, per-device revoke, 600 s / 7 d / 30 d lifetimes, agent.mobile_action audit |
| 146 | Local model inference requires a login or the service token | Accepted | Owner decision 2026-10-02: ollama generate/chat, llama.cpp v1/chat/completions/v1/completions and llama.cpp pull-progress SSE take a user JWT or Authorization: Service <INTER_SERVICE_AUTH_TOKEN> (@AllowServiceToken(), opt-in per route); /internal/ollama/* and /internal/llamacpp/* are service-token only (ServiceTokenGuard); health stays public; every chat/routing/memory/workspace caller sends the token |
| 147 | Per-route rate limits on sign-in and sign-up | Accepted | Owner direction 2026-10-02: login 10/15 min per IP+address and 30 per IP, register 5/h per IP and 3/h per address, refresh 60/min; hashed Redis keys, 429 + Retry-After, no lockout, fail open; successful login refunds; global throttler keyed by user or real client IP (trusted proxy only), service token skips |
| 150 | Runtime clients get a plan-gated, metered, capped crawl | Accepted (backend built; tool pending) | Coding-agent audit 2026-10-02: /research/crawl/runs (crawl + extract) for user tokens; ResearchAccessGuard plan unlock before anything, owner-scoped 404, DB-level caps (runtime_crawl_configs, no env), existing WEB_FETCH meter, truthful FAILED runs; threat model
|
| 151 | A picked model that fails gets a labelled substitute, then three suggestions | Accepted | Owner direction 2026-10-02: routing-service ranks pickedModelSubstitutes (same provider first, pricier last and marked), chat-service tries at most two after a provider failure only (never after 402/403/429), labels the answer (metadata.pickedModelFallback), and when all fail stores PICKED_MODEL_FAILED with three untried suggestedModels the bubble offers as regenerate buttons plus the model picker |
| 152 | Attachments are always read, whatever the user types | Accepted | Owner direction 2026-10-02: the planner and the final model always see every attachment (bounded digest, helper description for blind models), earlier files stay in follow-ups, scanned PDFs are OCR'd page by page, no second helper charge for earlier images |
| 153 | Anthropic prompt caching, billed from the cache-write rate only when it exists | Accepted | Owner approved 2026-10-02: native Anthropic /v1/messages transport with cache_control behind a per-model catalog switch (default OFF, admin API, no env var); a cache write is billed from cacheWritePerMillionMicroUsd only when that row exists (else plain input), the PAYG hold is sized for the premium, integer micro-USD throughout |
| 154 | Inline generation mentions are text; a named model gets the prompt without the directive | Accepted | Owner direction 2026-10-03: generationRequestText guards image/file/video detection, namedModelPrompt + namedModelNotice on message.routed, save needs real material |
| 155 | Every chat surface is one pipeline | Accepted | Owner direction 2026-10-03: Compare/Consensus/Escalation/seven labs/judge are surfaces over normal chat; one thread resolver, shared DTO fragments, one composer spread, guard spec (rule 59) |
| 156 | QA evidence is mandatory and machine-checked | Accepted | Owner direction 2026-10-03: 15 QA lanes per batch recorded in docs/qa-evidence, tools/qa/evidence.mjs + CI job qa-evidence, QA-WORKFLOW.md entry point (rule 60) |
| 157 | Runtime managed connector providers | Accepted | Additive provider-definition catalog for OpenAI-compatible runtime providers; provider key flows through connector snapshots, routing and chat; built-in enum identities and encrypted credentials stay intact |
| 158 | The model list is virtualised and the picker owns its highlight | Accepted | |
| 159 | ClawAI Threads uses publication and generation services | Accepted | |
| 160 | Threads account deletion propagates through a durable event | Accepted | |
| 161 | A used-up credit model falls back to an included model, with a notice | Accepted | |
| 162 | The free allowance is a count and a meter, and it does not cover expensive models | Accepted | |
| 163 | Product tours are data, point at data-tour elements, and never act for the person |
Accepted | |
| 164 | User notifications live in auth-service and are requested by event | Accepted | |
| 165 | Provider keys are only served to services and prices are synced from providers | Accepted | |
| 166 | Web Push is an opt-in channel of user notifications | Accepted | |
| 167 | Thread generation progress is an owner-only step log | Accepted | |
| 168 | Save requests are proposed before they are saved | Accepted | |
| 169 | Observability is split into pages and lists every user | Accepted | |
| 170 | AGENTS.md is the canonical router, gates are scoped and strict, docs move with code | Accepted | |
| 171 | Plugins are a separate service with a curated catalog | Accepted | |
| 172 | Plugin installs are owner-scoped, consent is a stored grant, credentials are write-only | Accepted | |
| 173 | Threads assumes a 32k window for an unknown model and fails only on overflow | Accepted | |
| 174 | Threads refuses what cannot succeed before it queues a job | Accepted | |
| 175 | Threads model calls are bounded by silence, not by the clock | Accepted | |
| 176 | Thread generation runs jobs concurrently and fairly, and names the real cause of a failure | Accepted | |
| 177 | Threads Board search is a transactional Postgres projection with trigram fuzzy matching | Accepted | |
| 178 | The Board is a portal surface; SEO stays on /threads/[slug] | Accepted | |
| 179 | User MCP servers are SSRF-guarded and consent-gated | Accepted | |
| 180 | Threaded comments, comment votes, mentions and community notifications | Accepted | |
| 181 | Thread generation heals itself by failure class and a retry resumes at the failed phase | Accepted | |
| 182 | All-threads sitemaps list every language under each app locale | Accepted | |
| 183 | Threads production bug batch: prose-only safety scan, script-aware topics and slugs, health-aware teams, resolved markers, a wider queue | Accepted | |
| 184 | Own MCP servers reach chat through the pinned snapshot | Accepted | 2026-10-15 |
| 185 | The in-chat consent card parks the turn on a stored, argument-bound request | Accepted | 2026-10-15 |
| 186 | Plugins are imported from GitHub or an upload as inert, reviewed documents | Accepted | 2026-10-15 |
| 187 | Admins curate the catalog and decide user servers from the database | Accepted | 2026-10-15 |
| 188 | Reader change requests are answered by the owner, told to the reader and credited once applied | Accepted | 2026-10-15 |
| 189 | Thread articles are typeset by a guarded format stage and rendered as rich, safe HTML | Accepted | 2026-10-16 |
| 190 | Thread author preferences are arbitrated by the Judge | Accepted | 2026-10-25 |
| 191 | Unavailable Judge results are never verified | Accepted | 2026-10-25 |
| 192 | GitHub repository crawls stay inside the requested repository | Accepted | 2026-10-25 |
| 193 | A failed Thread review names its real cause and fills evidence gaps once | Accepted | 2026-10-27 |
| 194 | The owner can accept disclosed evidence gaps and the article says so | Accepted | 2026-10-27 |
| 195 | Research uses short targeted queries, accepted gaps are verified, and reviewers stay independent | Accepted | 2026-10-27 |
| 196 | The Node.js runtime floor is enforced at build and at deploy | Accepted | 2026-10-30 |
| 197 | A plan limits the size and cost of one platform-paid call | Accepted | 2026-10-30 |
| 198 | Model pickers show the plan decision and dim what it excludes | Accepted | 2026-10-30 |
| 199 | Aggregator models are priced, and the low plans stop short of the dearest classes | Accepted | 2026-10-31 |
ClawAI Wiki · source-controlled under wiki/ · generated/mirrored from the current repository knowledge layer.
ClawAI Wiki
Product
- Product-Vision
- Flagship-Features
- Business-Overview
- Requirements-Register
- Drift-Log
- Runtime-Managed-Connector-Providers
- Threads
- ADR-Index
Architecture
- System-at-a-Glance
- Architecture-Map
- System-Architecture
- Message-Flow
- End-to-End-Data-Flow
- Routing-Engine
- Event-Bus
- Security-Architecture
Platform
- Backend-Services
- Frontend-Architecture
- Shared-Packages
- Database-Reference
- API-Reference
- Provider-Catalog
- Memory-and-Context-Architecture
- File-Support-Architecture
- Research-and-Web-Crawling
Runtime & Ops
- Docker-Guide
- Nginx-Reference
- CI-CD-Pipeline
- Logging-and-Observability
- Operational-Runbooks
- Troubleshooting
Quality
Engineering OS
- AI-Native-Engineering-OS
- Generated-Manifests
- Rules-Catalog
- Skills-Catalog
- Context-Layer
- Engineering-Memory
- Tooling-and-Automation
- Scripts-and-Operations
- ESLint-and-Architecture-Enforcement
- Extended-Work-Skills
Agents
Reference