diff --git a/.agents/skills/durable-objects/SKILL.md b/.agents/skills/durable-objects/SKILL.md index ad6ae1a438..4d8e60cd12 100644 --- a/.agents/skills/durable-objects/SKILL.md +++ b/.agents/skills/durable-objects/SKILL.md @@ -34,6 +34,9 @@ Fetch the relevant doc page when implementing features. - `./references/rules.md` - Core rules, storage, concurrency, RPC, alarms - `./references/testing.md` - Vitest setup, unit/integration tests, alarm testing - `./references/workers.md` - Workers handlers, types, wrangler config, observability +- `./references/repo-conventions.md` - This repo's Worker/DO conventions: DO call + retries, DO stub helpers, sub-module splitting, IO boundaries, and DB-client + lifecycle Search: `blockConcurrencyWhile`, `idFromName`, `getByName`, `setAlarm`, `sql.exec` diff --git a/.agents/skills/durable-objects/references/repo-conventions.md b/.agents/skills/durable-objects/references/repo-conventions.md new file mode 100644 index 0000000000..b1625f1586 --- /dev/null +++ b/.agents/skills/durable-objects/references/repo-conventions.md @@ -0,0 +1,84 @@ +# Repo Conventions — Worker/DO Services in This Monorepo + +Always bias towards following established patterns in existing services. These +are merely guidelines. + +## DO call retries + +Retry Durable Object stub calls with `withDORetry` from `@kilocode/worker-utils` +(`packages/worker-utils/src/do-retry.ts`) rather than hand-rolling retry/backoff +loops. It creates a fresh stub per attempt (required, since certain errors break a +stub), retries only errors with Cloudflare's documented `.retryable === true` +property, and applies jittered exponential backoff. + +```ts +const result = await withDORetry( + () => env.MY_DO.get(env.MY_DO.idFromName(key)), // fresh stub per attempt + stub => stub.getMetadata(), + 'getMetadata' +); +``` + +Services that need service-local log correlation wrap it with their own logger +bound in, e.g. `services/cloud-agent-next/src/utils/do-retry.ts` and +`services/webhook-agent-ingest/src/util/do-retry.ts` — both are thin +logger-binding adapters over the shared `withDORetry`, not reimplementations. +Prefer this pattern (a local `withDORetry` re-export bound to the service logger) +over calling the base helper directly with no logger, and over copying the retry +loop itself. + +## DO stub helper + +Each DO module must export a `get{ClassName}Stub` helper function (e.g. +`getRigDOStub`) that centralizes how that DO namespace creates instances. Callers +use this helper instead of accessing the namespace binding directly. + +## Sub-modules for large DOs + +When a Durable Object grows beyond a few hundred lines, extract domain logic into +sub-modules under a `/` directory alongside the DO file. For example, +`Town.do.ts` delegates to modules in `town/`: + +``` +dos/ + Town.do.ts # Class definition, RPC methods, alarm loop + town/ + agents.ts # Agent CRUD, hook management + beads.ts # Bead CRUD, convoy progress +``` + +Each sub-module exports plain functions (not classes) that accept `SqlStorage` and +any other required context as arguments. The DO imports them with the +`import * as X` pattern: + +```ts +import * as beadOps from './town/beads'; +import * as agents from './town/agents'; +import * as scheduling from './town/scheduling'; + +// In the DO class: +beadOps.updateBeadStatus(this.sql, beadId, 'closed', agentId); +agents.getOrCreateAgent(this.sql, 'polecat', rigId, this.townId); +await scheduling.schedulePendingWork(this.schedulingCtx); +``` + +This keeps the DO class thin (RPC surface + orchestration) while sub-modules own +the business logic. The `import * as X` pattern makes call sites self-documenting — +you can always tell which domain a function belongs to. + +## IO boundaries + +- Validate data at IO boundaries (HTTP responses, JSON.parse results, SSE + event payloads, subprocess output, upstream responses, persisted session + records) with Zod schemas. Return `unknown` from raw fetch/parse helpers and + `.parse()` in the caller. +- Never use `as` to cast IO data. If the shape is known, define a Zod schema; if + not, use `.passthrough()` or a catch-all schema. + +## DB clients + +See `workers-best-practices` skill (`references/rules.md` → "Repo rule: never +cache DB clients/pools in module scope") for the Hyperdrive/`getWorkerDb` +lifecycle rule shared by all Workers in this repo. Durable Object instance fields +(e.g. a Drizzle/SQLite wrapper over `state.storage`) are exempt — that's +object-local state, not module-scope caching. diff --git a/.agents/skills/specs/SKILL.md b/.agents/skills/specs/SKILL.md new file mode 100644 index 0000000000..e4217c84e2 --- /dev/null +++ b/.agents/skills/specs/SKILL.md @@ -0,0 +1,33 @@ +--- +name: specs +description: Business-rule specs for KiloClaw billing/lifecycle/controller/data model/Composio, MCP Gateway auth, model experiments, Security Agent, subscription center, team/enterprise seat billing, Impact affiliate/referrals, Kilo Pass, organization SSO, Stripe early fraud warnings, and coding plans. Load when you need context about the business requirements that guided the implementation. +--- + +# Business-Rule Specs + +Specs in `.specs/` are context as to the original business intention, rules and +invariants of the domains they cover. Consult them for context and flag inconsistencies +to the user if instructions or changes will cause deviations from the original intent. + +## Index + +| Spec | Governs | +|---|---| +| `.specs/kiloclaw-billing.md` | KiloClaw billing, pricing, invoicing, usage metering, payment flows | +| `.specs/kiloclaw-billing-lifecycle.md` | KiloClaw billing lifecycle — credit-renewal orchestration safety | +| `.specs/kiloclaw-composio.md` | KiloClaw Composio credential provisioning, injection, and sharing | +| `.specs/kiloclaw-controller.md` | KiloClaw controller/machine lifecycle, bootstrap, Docker image | +| `.specs/kiloclaw-datamodel.md` | KiloClaw data model — instance/subscription tables, invariants | +| `.specs/mcp-gateway-auth.md` | Kilo MCP Gateway v1 — protocol surface, ownership, OAuth lifecycle, provider grants, runtime auth | +| `.specs/model-experiments.md` | Model experiment routing, bucketing, lifecycle, prompt retention, and reporting rules | +| `.specs/security-agent.md` | Security Agent Auto Remediation and finding/SLA notification guarantees | +| `.specs/subscription-center.md` | Subscription Center ownership, states, and user-facing behavior | +| `.specs/team-enterprise-seat-billing.md` | Team and Enterprise seat billing, subscription management | +| `.specs/impact-affiliate-tracking.md` | Impact.com affiliate conversion tracking | +| `.specs/impact-referrals.md` | Impact.com Advocate referral programs for KiloClaw and Kilo Pass | +| `.specs/kilo-pass.md` | Kilo Pass states, provider support, credit amounts, eligibility, lifecycle | +| `.specs/organization-sso.md` | Organization SSO enforcement — auth requirements, membership admission/removal, policy inheritance | +| `.specs/stripe-early-fraud-warnings.md` | Stripe Early Fraud Warning enforcement — scope, containment, financial unwinding, remediation | +| `.specs/coding-plans.md` | Coding Plans business rules (RFC 2119 normative language) | + +`.specs/template.md` is the authoring template for new specs, not a governed domain. diff --git a/.agents/skills/workers-best-practices/SKILL.md b/.agents/skills/workers-best-practices/SKILL.md index 6516c8c015..c0480adcfa 100644 --- a/.agents/skills/workers-best-practices/SKILL.md +++ b/.agents/skills/workers-best-practices/SKILL.md @@ -60,6 +60,7 @@ mkdir -p /tmp/workers-types-latest && \ | Queues & Workflows | Move async/background work off the critical path | | Service bindings | Use service bindings for Worker-to-Worker calls — not public HTTP | | Hyperdrive | Always use Hyperdrive for external PostgreSQL/MySQL connections | +| Repo: DB client lifecycle | Never cache DB clients/pools in module scope (Worker or DO); use `getWorkerDb(...)` per use. DO instance fields are fine for object-local state. See `references/rules.md` | ### Observability diff --git a/.agents/skills/workers-best-practices/references/rules.md b/.agents/skills/workers-best-practices/references/rules.md index 1e7b969581..b72924efef 100644 --- a/.agents/skills/workers-best-practices/references/rules.md +++ b/.agents/skills/workers-best-practices/references/rules.md @@ -268,6 +268,36 @@ async fetch(request: Request, env: Env): Promise { **Retrieve**: `/hyperdrive/` for current configuration and supported databases. +### Repo rule: never cache DB clients/pools in module scope (Workers or Durable Objects) + +This repo's Workers and Durable Objects must not cache database clients, pools, or other transport-owning/request-context-bound SDK objects in module scope. Workers reuse isolates across requests, and Durable Object classes in the same Worker can share module memory across object instances — stale module-scope I/O state causes cross-context runtime failures, not just the generic "stale request state" anti-pattern above. + +**Check**: no `const client = new Client(...)` (or Drizzle/pg pool equivalent) declared at module scope, even for "read-only" or "singleton" convenience. + +- Create external database clients through the approved per-use helper, `getWorkerDb(...)`; let Hyperdrive own pooling. +- Only cache pure data or context-independent values in module scope (e.g. static config objects, parsed constants). +- Durable Object **instance fields** are fine for object-local state created from constructor inputs — for example, SQLite/Drizzle wrappers over `state.storage`. That is per-object state, not shared module state. +- If an optimization appears to require module-scope client caching, stop and document why lifetime, transport ownership, binding freshness, and Cloudflare runtime behavior make it safe before implementing it. + +```ts +// Correct: per-request/per-call helper owns client lifecycle +export async function handler(env: Env) { + const db = getWorkerDb(env.HYPERDRIVE.connectionString); + return db.select().from(users); +} +``` + +Anti-pattern: + +```ts +// Module-scope client — shared across isolate reuse and (for DOs) across instances +const db = drizzle(new Client({ connectionString: env.HYPERDRIVE.connectionString })); + +export async function handler() { + return db.select().from(users); // stale/leaked connection risk +} +``` + --- ## Observability diff --git a/.kilo/command/cloud-pr.md b/.kilo/command/cloud-pr.md new file mode 100644 index 0000000000..e0f3e437b9 --- /dev/null +++ b/.kilo/command/cloud-pr.md @@ -0,0 +1,30 @@ +--- +description: Create a pull request following repo conventions +--- + +Create a pull request for the current branch following the repo conventions below. $ARGUMENTS + +Before opening the PR, inspect the branch: review all commits on it (not just the latest), `git status`, `git diff` against the base branch, and remote tracking state. + +## Titles + +- Format: `type(scope): ` (e.g., `feat(auth): add SSO login`) +- Common types: `feat`, `fix`, `refactor`, `docs`, `test`, `chore`, `ci`, `style`, `perf` +- Imperative mood, under 72 characters, no trailing period. + +## Descriptions + +Follow the PR template in `.github/pull_request_template.md`. Every description must include four sections in order: + +1. **`## Summary`** — What changed and why. Outcome-focused, call out architectural changes. +2. **`## Verification`** — Manual verification only. Do not list automated checks such as `pnpm typecheck`, `pnpm test`, `pnpm lint`, `pnpm validate`, CI, or formatting commands here. +3. **`## Visual Changes`** — Before/after screenshots, or `N/A`. +4. **`## Reviewer Notes`** — Risk areas, tricky logic, rollout notes, or `N/A`. + +Do not leave HTML comments from the template. Review all commits on the branch when writing the summary. + +## Workflow + +- Create PRs as **ready for review** by default. Only use `--draft` if explicitly requested. +- When assigning PRs or issues, resolve the GitHub username with `gh api user --jq '.login'`. Never guess usernames. +- Never use `--force`, `--no-verify`, or any other flag that bypasses git hooks without explicit user approval. If a hook or check fails, diagnose and fix it or ask how to proceed — do not silently skip it. diff --git a/.kilo/rules/coding-style.md b/.kilo/rules/coding-style.md index 71ffda8281..160d785ae9 100644 --- a/.kilo/rules/coding-style.md +++ b/.kilo/rules/coding-style.md @@ -5,9 +5,11 @@ - KISS: Be wary of over-abstracting code. Do report and ask about violations of DRY, but don't prematurely generalize. - If trivial, avoid TS classes; use e.g. closures instead - STRONGLY AVOID coding patterns that cannot be statically checked: - - AVOID typescript's "as" operator - - AVOID typescript's null-forgiving "!" - - INSTEAD TRY where possible typescript's "satisfies", or leverage flow-sensitive typing. + - Use `as` casts sparingly, but do not ban them outright. Prefer `satisfies`, discriminated unions, generics, or flow-sensitive narrowing when TypeScript can be made to understand the type naturally. + - A targeted `as` cast is acceptable when code is at a known boundary where TypeScript has lost information that the surrounding control flow guarantees. For example, inside a platform switch, casting `message` to `Message` or `Message` is preferable to adding generic `Record` property helpers just to read known adapter fields. + - Avoid broad casts that hide real uncertainty, especially `as any`, double casts through `unknown`, or casting external/untrusted data without validation. Use runtime validation when the data shape is genuinely unknown, user-controlled, persisted, or coming from an API contract we do not own. + - `as` casts are explicitly permitted inside test files (e.g. `*.test.ts`, `*.spec.ts`, files under `__tests__/`, and other test fixtures/helpers) — they are commonly needed for fixture construction, narrowing partial mocks, and exercising error paths. Production code conventions still apply to non-test code imported by tests. + - AVOID typescript's null-forgiving "!"; prefer explicit checks or flow-sensitive typing. - Prefer clear NAMES (for e.g. variables, functions and tests) over COMMENTS. - ONLY add comments about things that are NOT OBVIOUS in context. diff --git a/AGENTS.md b/AGENTS.md index 191eb43b3c..cdce2d39a6 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -21,14 +21,15 @@ scripts/ CI and one-off scripts ## Domain Context -Before changing domain behavior, read `CONTEXT.md`. -Use canonical terms from `CONTEXT.md` in code, docs, task descriptions, tests, and agent outputs. -Do not introduce synonyms for existing concepts unless updating `CONTEXT.md` first. -Do not duplicate the full context contract inside `AGENTS.md`. +`CONTEXT.md` is the domain-language and ownership contract for the **Code Reviewer** and **Security Agent** domains only (review execution/analytics, Security Findings, Security Sync, notifications, remediation, and their email delivery — mainly `apps/web/src/lib/{code-reviews,security-agent}/`, `services/security-sync/`). It does not cover other areas of the monorepo. + +When working in those domains, read `CONTEXT.md` first and use its canonical terms in code, docs, task descriptions, tests, and agent outputs. Do not introduce synonyms for its concepts without updating `CONTEXT.md` first, and do not duplicate the full contract inside `AGENTS.md`. ## Verification -After making changes, verify your work with the narrowest relevant checks. Avoid running the full `pnpm typecheck` by default; it is slow enough to make development environments unusable. Prefer targeted package checks or `scripts/typecheck-all.sh --changes-only`, and mention in your final response when the full typecheck was skipped for this reason. Run the full suite when appropriate. **Always run `pnpm format` before committing** — CI will reject unformatted code. +Verify your work with the narrowest relevant checks. Prefer targeted package checks or `scripts/typecheck-all.sh --changes-only`. + +**Always run `pnpm format` before committing** | Command | What it checks | |---|---| @@ -38,40 +39,14 @@ After making changes, verify your work with the narrowest relevant checks. Avoid | `pnpm validate` | All three above in sequence | | `pnpm format` | Auto-format with oxfmt | -Target a specific test file: `pnpm test -- `. Run tests for a specific service: `pnpm --filter test`. - **Before running tests**, ensure the test database is running. If there is no active Postgres instance, run `pnpm test:db` first — this starts the Postgres container and applies migrations. You can check whether Postgres is already running with `docker compose -f dev/docker-compose.yml ps postgres`. -## apps/web UI Work - -When editing UI files in `apps/web` — React components, pages, layouts, or styles (`.tsx`/`.css`) — use the `kilo-design` skill. - -## Coding Standards - -- Prefer `type` over `interface`. -- Use `as` casts sparingly, but do not ban them outright. Prefer `satisfies`, discriminated unions, generics, or flow-sensitive narrowing when TypeScript can be made to understand the type naturally. -- A targeted `as` cast is acceptable when code is at a known boundary where TypeScript has lost information that the surrounding control flow guarantees. For example, inside a platform switch, casting `message` to `Message` or `Message` is preferable to adding generic `Record` property helpers just to read known adapter fields. -- Avoid broad casts that hide real uncertainty, especially `as any`, double casts through `unknown`, or casting external/untrusted data without validation. Use runtime validation when the data shape is genuinely unknown, user-controlled, persisted, or coming from an API contract we do not own. -- The above restrictions on `as` do not apply inside test files (e.g. `*.test.ts`, `*.spec.ts`, files under `__tests__/`, and other test fixtures/helpers). `as` casts are explicitly permitted in tests, where they are commonly needed for fixture construction, narrowing partial mocks, and exercising error paths. Production code conventions still apply to non-test code imported by tests. -- Avoid `!` non-null assertions; prefer explicit checks or flow-sensitive typing. -- Avoid mocks in tests; assert on results or check the database for side effects. -- Prefer clear names over comments. Only comment things not obvious in context. -- When the linter flags an unused variable, investigate the root cause — do not blindly prefix with `_`. -- Use existing dependencies before implementing custom solutions. Check `package.json` for what's available. - ## Timestamp Serialization - Drizzle/Postgres `timestamp({ withTimezone: true, mode: 'string' })` rows may surface timestamp text like `2026-04-29 01:16:12.945+00`, which strict ISO validators such as `z.string().datetime()` reject. - Before putting DB-backed timestamp strings into HTTP bodies, queue messages, or other strict JSON contracts, normalize them to UTC ISO with an existing domain serializer or `new Date(value).toISOString()`. Do not forward raw DB timestamp text across contract boundaries. - Keep strict validators unless the receiving contract intentionally accepts a broader format. Add regression fixtures using production-shape Postgres timestamp text when fixing or extending these paths. -## Workers & Durable Objects - -- Do not cache database clients, pools, or other transport-owning/request-context-bound SDK objects in module scope for Cloudflare Workers or Durable Objects. Workers reuse isolates across requests, Durable Object classes in the same Worker can share module memory across object instances, and stale module-scope I/O state can cause cross-context runtime failures. -- Create external database clients through approved per-use helpers such as `getWorkerDb(...)`; let Hyperdrive own pooling. Only cache pure data or context-independent values in module scope. -- Durable Object instance fields are valid for object-local state created from constructor inputs, such as SQLite/Drizzle wrappers over `state.storage`. -- If optimization appears to require module-scope client caching, stop and document why lifetime, transport ownership, binding freshness, and Cloudflare runtime behavior make it safe before implementing it. - ## Database Migrations Schema is in `packages/db/src/schema.ts`. Migrations live in `packages/db/src/migrations/` and are generated by `drizzle-kit` via `pnpm drizzle generate`. @@ -81,78 +56,14 @@ Schema is in `packages/db/src/schema.ts`. Migrations live in `packages/db/src/mi - **After a rebase that conflicts on migration files:** delete all migration files, snapshots, and journal entries that were added on the branch, then re-run `pnpm drizzle generate` to regenerate a clean migration from the current schema diff. Re-append any backfill SQL afterward. - Prefer a single migration per feature branch when the code has not yet been deployed to production. If multiple migrations accumulated during development, squash them by deleting all branch-local migrations and regenerating. -## GDPR & PII - -When adding PII (email, name, IP address, etc.) to the database — whether as a new table or a new column — you **must** also update the GDPR soft delete flow in `softDeleteUser` (`apps/web/src/lib/user/index.ts`) and add a corresponding test in `apps/web/src/lib/user/index.test.ts`. - ## Logging & Sensitive Data Never log tokens, credentials, auth headers, cookies, or webhook secrets. Use `redactSensitiveHeaders` from `@kilocode/worker-utils/redact-headers` when headers must be stored or logged. Do not enable `sendDefaultPii` or `attachRpcInput` in Sentry config. -## Plans +## Stripe Subscription Schedules -When writing implementation plans, always save them to the `.plans/` directory at the repo root. This is the designated location for all planning documents — do not place them elsewhere in the repo. +When using `subscriptionSchedules.create()` with `from_subscription`, Stripe prohibits setting `metadata` in the same call (it copies metadata from the subscription automatically). Set custom metadata (e.g., `origin` tags) in the subsequent `subscriptionSchedules.update()` call instead. ## Git Safety - - **Never** use `--force`, `--no-verify`, or any other flag that bypasses git hooks or safety checks without explicit user approval. - If a hook or check fails, diagnose the issue and either fix it or ask the user how to proceed — do not silently skip it. - -## Pull Requests - -### Titles - -- Format: `type(scope): ` (e.g., `feat(auth): add SSO login`) -- Common types: `feat`, `fix`, `refactor`, `docs`, `test`, `chore`, `ci`, `style`, `perf` -- Imperative mood, under 72 characters, no trailing period. - -### Descriptions - -Follow the PR template in `.github/pull_request_template.md`. Every description must include four sections in order: - -1. **`## Summary`** — What changed and why. Outcome-focused, call out architectural changes. -2. **`## Verification`** — Manual verification only. Do not list automated checks such as `pnpm typecheck`, `pnpm test`, `pnpm lint`, `pnpm validate`, CI, or formatting commands here. -3. **`## Visual Changes`** — Before/after screenshots, or `N/A`. -4. **`## Reviewer Notes`** — Risk areas, tricky logic, rollout notes, or `N/A`. - -Do not leave HTML comments from the template. Review all commits on the branch when writing the summary. - -### Workflow - -- Create PRs as **ready for review** by default. Only use `--draft` if explicitly requested. -- When assigning PRs or issues, resolve the GitHub username with `gh api user --jq '.login'`. Never guess usernames. - -## Specs - -Business-rule specs live in `.specs/`. Before making **any** changes to a domain covered by a spec — including bug fixes, new features, refactors, or reviews — you **must** first read the relevant spec. - -| Spec | Governs | -|---|---| -| `.specs/kiloclaw-billing.md` | KiloClaw billing, pricing, invoicing, usage metering, payment flows | -| `.specs/kiloclaw-billing-lifecycle.md` | KiloClaw billing lifecycle — credit-renewal orchestration safety | -| `.specs/kiloclaw-composio.md` | KiloClaw Composio credential provisioning, injection, and sharing | -| `.specs/kiloclaw-controller.md` | KiloClaw controller/machine lifecycle, bootstrap, Docker image | -| `.specs/kiloclaw-datamodel.md` | KiloClaw data model — instance/subscription tables, invariants | -| `.specs/model-experiments.md` | Model experiment routing, bucketing, lifecycle, prompt retention, and reporting rules | -| `.specs/security-agent.md` | Security Agent Auto Remediation and finding/SLA notification guarantees | -| `.specs/subscription-center.md` | Subscription Center ownership, states, and user-facing behavior | -| `.specs/team-enterprise-seat-billing.md` | Team and Enterprise seat billing, subscription management | -| `.specs/impact-affiliate-tracking.md` | Impact.com affiliate conversion tracking | -| `.specs/impact-referrals.md` | Impact.com Advocate referral programs for KiloClaw and Kilo Pass | - -## Markdown Tables - -Use compact, non-padded markdown tables to avoid merge conflicts. Prettier is configured to skip `*.md` files so it won't re-pad tables. - -**Rules:** -- Separator rows: use `|---|---|` (no spaces around `---`, colons allowed for alignment: `:---`, `---:`, `:---:`) -- Content rows: single space of padding only — `| value |`, not `| value |` - -**Enforcement:** -- `script/check-md-table-padding.ts` checks all tracked `*.md` files -- CI runs this check on every PR that touches markdown files -- To auto-fix: `bun run script/check-md-table-padding.ts --fix` - -## Stripe Subscription Schedules - -When using `subscriptionSchedules.create()` with `from_subscription`, Stripe prohibits setting `metadata` in the same call (it copies metadata from the subscription automatically). Set custom metadata (e.g., `origin` tags) in the subsequent `subscriptionSchedules.update()` call instead. diff --git a/apps/extension/AGENTS.md b/apps/extension/AGENTS.md index f0ce99d0de..e76febd285 100644 --- a/apps/extension/AGENTS.md +++ b/apps/extension/AGENTS.md @@ -83,7 +83,6 @@ Use a narrower subset only when the change is clearly isolated, and say what was ## Code Style -- Prefer `type` over `interface` in new code unless an existing file already uses interface-heavy browser API shapes. -- Avoid `as any`, broad casts, and non-null assertions in production code. Validate extension/browser API responses at the boundary. +- Prefer `type` over `interface` in new code, unless an existing file already uses interface-heavy browser API shapes — validate extension/browser API responses at the boundary rather than casting them. - Do not log tokens, auth headers, cookies, or gateway request bodies that may contain user content. - Keep helpers boring and local until behavior is shared by real callers. diff --git a/apps/mobile/AGENTS.md b/apps/mobile/AGENTS.md index 68581660c8..c43986d272 100644 --- a/apps/mobile/AGENTS.md +++ b/apps/mobile/AGENTS.md @@ -99,7 +99,6 @@ rm -rf "$TMPDIR/metro-cache" "$TMPDIR"/metro-file-map-* ## Code Style - Expo Router requires default exports in `src/app/` — this is the only place default exports are allowed. -- Prefer `type` over `interface`. - Import `View`, `Text`, `ScrollView`, `Pressable`, `TextInput` from `react-native` — NativeWind's Metro plugin rewrites these imports to add `className` support automatically. - Import `Image` from `@/components/ui/image` (a `styled` wrapper around `expo-image`). Lint enforces this. - For UI components (Button, Text with variants, Card, etc.), import from `@/components/ui/`. These are from react-native-reusables (shadcn/ui for RN). diff --git a/services/cloud-agent-next/AGENTS.md b/services/cloud-agent-next/AGENTS.md index 006caea752..562b016820 100644 --- a/services/cloud-agent-next/AGENTS.md +++ b/services/cloud-agent-next/AGENTS.md @@ -131,7 +131,7 @@ This pattern blocks API endpoints from running for external contributors who don ### Runtime Guidelines -- Durable Object calls should be retried using `withDORetry` in `src/utils/do-retry.ts` +- Durable Object calls should be retried using `withDORetry` (this service's logger-bound wrapper is `src/utils/do-retry.ts`). See the `durable-objects` skill (`references/repo-conventions.md`) for the shared `withDORetry` pattern used across services. - Execute commands inside a session context (use `session.exec(...)`, not `sandbox.exec(...)`) ### Testing Standards diff --git a/services/kiloclaw-billing/AGENTS.md b/services/kiloclaw-billing/AGENTS.md index ed052f3e45..6149cc6791 100644 --- a/services/kiloclaw-billing/AGENTS.md +++ b/services/kiloclaw-billing/AGENTS.md @@ -4,6 +4,10 @@ `services/kiloclaw-billing` owns the KiloClaw billing lifecycle worker. +## Specs + +Load the `specs` skill to access the business rules and invariants of the KiloClaw billing service when you need context about the business requirements that guided the implementation. + ## Allowed Writes - This worker is allowed to write KiloClaw billing state in Postgres via Hyperdrive. diff --git a/services/kiloclaw/AGENTS.md b/services/kiloclaw/AGENTS.md index 8d71d814bd..4cd7928b1b 100644 --- a/services/kiloclaw/AGENTS.md +++ b/services/kiloclaw/AGENTS.md @@ -4,6 +4,10 @@ KiloClaw is a Cloudflare Worker that runs per-user OpenClaw AI assistant instances on Fly.io Machines. The CF Worker handles auth, config management, and proxies HTTP/WebSocket traffic to each user's Fly Machine via Fly Proxy. +## Specs + +This service is governed by `.specs/kiloclaw-controller.md`, `.specs/kiloclaw-datamodel.md`, and `.specs/kiloclaw-composio.md`. Read the relevant spec (and load the `specs` skill) before changing controller/machine lifecycle, the instance/subscription data model, or Composio credential handling. + ## Hard Invariants These are non-negotiable. Do not reintroduce shared/fallback paths. @@ -260,13 +264,6 @@ KiloClaw is transitioning from one-instance-per-user to N-instances-per-owner (p - **KiloClawRegistry DO** — SQLite-backed DO that indexes instances per owner (`user:{userId}` or `org:{orgId}`) for routing. Fresh-provision admission reservations are being added as a separate non-routable state surface; never expose pending reservations from normal route-resolution methods. - **Org instances** — `organization_id` links instances to org contexts. Registry entries for an organization may contain several assigned users; any current one-active-instance admission rule must be qualified by assigned user rather than treating the whole organization as one slot. -## Code Style - -- See `/.kilocode/rules/coding-style.md` for project-wide rules -- Prefer `type` over `interface` -- Avoid `as` and `!` -- use `satisfies` or flow-sensitive typing -- No mocks where avoidable -- assert on results - ## Gateway Configuration OpenClaw configuration is built at machine startup by the controller's bootstrap module (`controller/src/bootstrap.ts`): diff --git a/services/mcp-gateway/AGENTS.md b/services/mcp-gateway/AGENTS.md index 59373d9e4a..98a60cf88e 100644 --- a/services/mcp-gateway/AGENTS.md +++ b/services/mcp-gateway/AGENTS.md @@ -13,46 +13,23 @@ The Worker MUST NOT implement first-level OAuth authorization, token, registrati provider callback, JWKS, user-info, config CRUD, assignment CRUD, or app management routes in v1. -## File naming +## Specs -- Add a suffix matching the module type, for example `mcp-gateway.worker.ts`, - `MCPGatewayInstance.do.ts`, `connect.handler.ts`, `routes.schema.ts`, and - `instances.table.ts`. -- Modules that predominantly export a class should be named after that class. -- Keep pure helpers in `lib/` and keep route handlers in `handlers/`. +This service is governed by `.specs/mcp-gateway-auth.md` (the authoritative MCP +Gateway v1 spec — protocol surface, ownership, OAuth lifecycle, provider grants, +runtime auth). Read it (and load the `specs` skill) before changing gateway +behavior. ## HTTP routes -- Define every exposed Hono route in `src/mcp-gateway.worker.ts` so the public - surface is visible in one file. -- Do not mount Hono sub-apps. -- Move route logic into `handlers/*.handler.ts` modules. -- Each handler takes the Hono context and a plain parsed params object. The route - declaration remains the source of truth for path-to-param shape. - Runtime routes are scoped connect resources only: - `/mcp-connect/user/{user_id}/{config_id}/{route_key}` - `/mcp-connect/org/{org_id}/{config_id}/{route_key}` - Protected-resource metadata is the only other public gateway surface owned by this Worker. -## IO boundaries - -- Validate every IO boundary with Zod: MCP messages, route params, query params, - behavior-affecting headers, upstream responses, JSON parse results, SSE payloads, - subprocess output, and persisted session records. -- Raw parse and fetch helpers return `unknown`; callers parse with the relevant - Zod schema. -- Do not use `as` casts for IO shapes. Use schemas, `.passthrough()`, or explicit - catch-all schemas when the shape is intentionally broad. -- The gateway is stricter than Gastown at MCP protocol, header, query, upstream - response, and persisted-session boundaries. ## Hyperdrive and Postgres - -- Use `getWorkerDb(env.HYPERDRIVE.connectionString, { statement_timeout: ... })` - per request or per Durable Object use. -- Never cache pg pools, Drizzle clients, transaction objects, request-scoped state, - or other transport-owning SDK objects in module scope. - Postgres remains the shared system of record for config, route, assignment, identity, instance, and grant state. - The Worker must re-check current Postgres state on every authenticated runtime @@ -64,17 +41,10 @@ routes in v1. deterministic key is `{owner_scope}:{owner_id}:{config_id}:{user_id}`. - Do not introduce a global gateway Durable Object or a config-level DO that serializes all users of a shared org config. -- Every DO module exports a `get{ClassName}Stub` helper, and callers use that - helper instead of accessing the namespace binding directly. -- Keep the DO class thin: RPC surface, alarms, and orchestration only. Move large - domain logic into plain-function submodules under a sibling directory when the - class grows beyond a few hundred lines. - DO cache state is never authoritative for config, assignment, identity, route, or grant eligibility. - If DO SQLite is used, use tracked schema migrations from day one instead of ad hoc `CREATE TABLE IF NOT EXISTS` drift. -- Use table interpolator objects and Zod row schemas for DO SQLite queries instead - of raw table or column strings and unsafe casts. ## Security and streaming