# AGENTS.md Repo-wide agent instructions for `sixb`. ## Scope - This file applies to the whole repository unless a nearer `AGENTS.md` is added later. - Keep one root `AGENTS.md` for now. Add nested files only when a package genuinely needs different instructions. ## Repo Map - `packages/core`: runtime, ontology builders, providers, and validation - `packages/server`: Elysia HTTP/WebSocket API and OpenAPI generation - `packages/ui`: shared React component library; knows nothing about Sixb, so the docs site uses it - `packages/client`: generated typed client artifacts - `packages/cli`: CLI entrypoint for `sixb` - `packages/create-sixb`: zero-dependency project scaffolder and template - `packages/app`: the project app: shell, built-in pages (Home, the agent, object pages, and the Console and Settings sections in `src/pages/`), dev server and build - `models/`: model providers such as Anthropic and Vercel AI Gateway - `connectors/`, `storage/`, `broker/`: integrations and infrastructure providers - `env/`: where a project's environment variables live, such as Doppler - `examples/`: runnable sample projects ## Toolchain - Bun only for package management, scripts, and runtime. Do not use `npm`, `pnpm`, `yarn`, or Vite CLI commands. - TypeScript is `strict`, targets ES2022, uses ESNext modules, and `moduleResolution: "bundler"`. - Formatting and linting are enforced with Biome. - Prefer `rg` and `rg --files` for search. ## Core Commands Repo-wide: ```bash bun install bun run build bun run typecheck bun run test bun run test:e2e bun run test:all bun run check bun run check:fix bun run generate:client bun sixb dev bun create-sixb my-app ``` Targeted: ```bash bun --filter @sixb/core build bun --filter @sixb/core typecheck bun test packages/core/tests/create-sixb.test.ts bun test packages/server/tests/ ``` CI runs these as independent parallel jobs (each does `bun install --frozen-lockfile` first): - `typecheck`: `bun run typecheck` - `build`: `bun run build`, then `bun run typecheck:consumers`, then `bun run test:publish` - `client`: `bun run generate:client`, then `git diff --exit-code` - `test`: `bun run test:ci` - `lint`: `bun run check` - `e2e`: package-scoped matrix jobs for packages with `test:e2e` `test:ci` is `bun test` wrapped in `scripts/ci-guard.ts`, which fails the run at the first 60-second silence and prints the last test file, the process tree, and each thread's kernel wait state. The job's `timeout-minutes` is only a backstop: a job that dies at its own wall clock reports "cancelled" with the log ending mid-stream, which is indistinguishable from every other cause. The guard exists because a `Bun.build()` deadlock once cost five 15-minute runs before anyone found the one named failure buried in the log. Run `test:ci` outside restricted process sandboxes. Its self-tests exercise process inspection and signaling, and several suite fixtures require local process or network access. In Codex, request elevated sandbox permissions before the first full-suite run instead of rerunning after an expected restricted failure. `typecheck` runs `bun check`, Bun's built-in TypeScript 7 checker, over source: the build graph (`-b -p tsconfig.build.json`, every package's `src` once), the test files (`tsconfig.tests.json`), the template and the docs app. The root `paths` map resolves `@sixb/*` to source, so nothing has to be built first. Every call passes `--no-script`: the root `check` script is Biome, and `bun check` runs a package's `check` script instead of the checker when one exists. It emits nothing, so declaration-emit errors surface in `build:types` (`tsc -b`, TypeScript 5.9) in the build job. `typecheck:consumers` checks the examples and the client's ontology-registry type program the way an app is checked: with `tsc`, the version apps install, against the emitted `.d.ts`. It needs a prior `bun run build` (or `build:types`), which is why CI runs it in the build job. The root config maps `@sixb/*` to `packages/*/src`, which is right for workspace development and wrong for consumer typechecks: it pulls the whole framework into each example's program. Examples keep that config as their default because Bun reads it while bundling dev apps. Their typecheck scripts alone pass `--paths null`, which clears the inherited mapping and lets `tsc` resolve each package's `exports.types`. That override applies only there. `apps/docs` stays entirely on the root config because Vercel deploys it with `prepare:docs && next build` and never emits declarations — pointing it at `dist` broke the deployment once already. Anything built or run outside `typecheck:consumers` reads source. ## Architecture - Define ontology types with `defineObjectType`, `prop`, `link`, `action`, and `defineValueType`. - Most runtimes start with `createSixb()`. - `createSixb()` auto-discovers `ontology/`, `actions/`, `datasets/`, `syncs/`, `schedules/`, `pipelines/`, `projections/`, `connectors/`, `rules/`, `workflows/`, `shares/`, and `security/{groups,roles,markings,policies}/`, plus the Agent's `skills//SKILL.md` and project instructions in `SIXB.md`. The `app/` directory is served separately and is not part of `createSixb()` discovery. - `sixb.objects(MyType)` is the typed API for object CRUD, telemetry, links, and actions. - Important domain events include `object.created`, `object.updated`, `object.deleted`, `link.created`, `link.updated`, `link.deleted`, `telemetry.appended`, and `schedule.triggered`. - Convention-based discovery is the normal registration model. - Generated client files live in `packages/client/src/generated/`. - If routes, schemas, or public contracts change, run `bun run generate:client`. ## Export Surfaces - Exports are curated — never re-export something from a barrel just because it exists. If nothing imports it, it does not belong on a public surface; a selector or helper that can only return nothing is dead API. - A package root (`.`) is for app authors: `@sixb/core` exports the authoring API (`define*`, `createSixb`, config types, and the `InMemory*` providers that fill a `createSixb` slot); other packages export only what consumers call (workers export just their `*Worker` class). - These six are the provider contracts a third party implements: `@sixb/core/{broker,queues,sandboxes,lake-storage,blob-storage/server,auth/strategy}`. - `@sixb/core/storage` is broader: it is both the read/run-history contract and where the in-memory storage implementations live. A third-party storage provider is not supported in 0.1.x — the materialization contract is still moving. - A type that appears in a public interface signature must be exported from the same subpath as the interface, or the interface cannot be implemented from outside. - `@sixb/core/internal/*` is for this repo's packages only — no compatibility promise. Nothing in `docs/`, `examples/`, `templates/`, or `apps/` may import from it. - Export types freely (users need them to annotate their own code); keep runtime values minimal. Connectors export all their wire types on purpose. - A package may import a sibling; it must never absorb one. Every bare specifier its JavaScript and TypeScript import — in `src` and in `dist` — has to be a declared `dependencies`/`peerDependencies`/`optionalDependencies` entry, and `test:publish` rejects the rest in both directions. `tsc` cannot: the root `paths` map resolves an undeclared `@sixb/*` import to source, so it type-checks and then the bundler inlines it. `scripts/package-boundaries.ts` holds the rule and explains the failure. - It must not resolve a sibling to a second copy either. Bun applies the nearest `tsconfig.json` `paths` map to every import it resolves, `node_modules` included, so a built module inherits whatever map is above it: the build writes `dist/tsconfig.json` to stop that lookup, and it ships in the tarball because the map that reaches our `dist` is the consumer's. The same rule is why the root `paths` map has to keep naming exactly the file each subpath's `exports.bun` names — `test:publish` rejects it when it stops. A duplicated module is silent: the custom-app dev bundle once held two generated SDK clients, one configured and one answering to the page origin. - Stylesheets served from `src` sit outside that check and stay a manual call. A bare `@import` in CSS is resolved by the consumer's Tailwind — relative to the stylesheet, then up through `node_modules` — not by the module resolver. `@sixb/ui` takes `tailwindcss` as an optional peer for exactly that reason: `@sixb/ui/globals.css` opens with `@import "tailwindcss"`, and only the consumer can satisfy it. ## Code Style - Biome uses 2 spaces, LF endings, 100 column width, double quotes, ES5 trailing commas, and no semicolons unless required. - Use `import type` and `export type` for type-only imports and re-exports. - Prefer explicit public types, but preserve inference where Sixb's typed APIs are designed to carry it. - Avoid `any`; narrow `unknown` instead of unchecked casts. - Validate inputs early and throw clear, actionable errors. - Package-prefixed error messages such as `[Sixb] ...`, `[SixbServer] ...`, or `[RokuTV] ...` are preferred. - The framework never speaks in the user's logger. Report terminal failures through `onError` and write everything else to a prefixed `console.*`. A swallowed `events.append()` is a lost trigger edge: emit through `events.emit(input, { source })` when the work that produced the events has already succeeded, and `events.append(input)` when the caller owns the outcome. Never write a bare `catch` around an append. - Keep builders and definitions declarative; avoid unnecessary indirection around ontology setup. - Preserve the existing visual language in `packages/app` (and `packages/ui`) and keep both desktop and mobile behavior working. - The code should be well-spaced and easy to read. ## Tests - Test framework is `bun:test`. - Tests live under `/tests/`. - Fast tests use `*.test.ts`. - E2e tests use `*.e2e.ts` and run through `bun run test:e2e`. - Prefer deterministic tests with temp directories, explicit cleanup, and fixed timestamps. - `bun test` prints only when a file finishes, so a test's runtime is a silence. The unit suite's longest legitimate silence is a few seconds; `test:ci` fails at 60. A `*.test.ts` that can legitimately go quiet for longer belongs in `*.e2e.ts` with its own bound, not in the unit suite. - Drive the bundler through a child process with a bound, not `Bun.build()` in the test process. Bun's bundler has deadlocked on hosted runners, a wedged bundler stays wedged for the life of the process, and the per-test timeout stops being a bound once it does — it fired for the deadlocked build and then not at all for the next bundler call, which hung until the job's wall clock. `packages/core/tests/published-artifacts.e2e.ts` shows the shape. The in-process exception is the dev-server cases in `packages/app/tests/createCustomApp.test.ts`, which serve Bun's HTML entry because that bundling *is* the behavior under test — it is why `test:ci` runs behind a guard at all. - A guard is proven by removing it. Before landing a test that locks in a fix, revert the fix and watch the test fail — a test that passes either way locks in nothing, and reads for years as if it did. Say in the test how to reproduce that check, and say it there when a toolchain version is what makes the failure reachable. - No `Bun.spawnSync` in a test. Inside `bun test --parallel` it sometimes never sees its child exit, so the test dies at its timeout or the worker spins beside a zombie until the CI guard kills the run. Spawn with async `Bun.spawn` and a kill timer, or call the code in-process when the process boundary is not what the test is about. - Don't wait on real time. A test that sleeps through a backoff, a deadline or a "nothing arrives" window pays for it on every run and flakes under load. Drive the timer with `jest.useFakeTimers()`, or wait for the event the code emits; restore real timers in `afterEach`, because a fake-timer test that times out never reaches its own `finally`. A production option that exists only to shorten a test's wait is a test-only seam — don't add one. - Await a rejection that might never settle as `await promise.catch((error) => error)`, not `expect(promise).rejects`: on Bun 1.4.4 the latter hangs the whole file past the per-test timeout, so a regression shows up as a guard stall instead of a named failure. - A negative check must be able to fail. Asserting that a spy was not called before the async work that would call it could run, or that nothing arrived during a quiet window, passes either way. Assert on the durable record, or publish what should be filtered out first so the first thing delivered proves the filter. - One owner per contract. Provider semantics belong to the shared suites in `packages/core/src/testing`, a helper's behavior to the helper's tests, a guard enforced for every route to that guard; a provider, resource or route suite proves only what is specific to it. Size fixtures to just past the page or chunk boundary they cross. - Run targeted tests first, then broader checks when shared behavior changes. ## Contribution Flow - `CONTRIBUTING.md` describes the repo's idea lifecycle, async handoff, and merge flow. - Rough ideas are acceptable. Help shape them toward the clearest outcome and the simplest implementation. - Issue and PR title patterns are: - `Idea: ` - `Task: ` - `: ` - Ideas use the `idea` label and exactly one lifecycle label: `status:draft`, `status:review`, `status:approved`, or `status:completed`. - AI-authored changes must still be cleaned up, verified, and human-readable before review. ## Local Drafts - `/.local/` is a gitignored scratchpad for local-only working notes. - Keep longer-running product or architecture notes in `/.local/bible.md`. - Put draft ideas, draft specs, and issue writeups in `/.local/drafts/`. - Treat `/.local/` files as staging material that will usually become GitHub issues, not committed repo docs. - Only create or commit tracked docs when the content is ready to be shared, referenced, and maintained in the repository. ## Documentation - Write documentation in English. - Public docs address developers discovering Sixb: what they need to do and what behavior to expect. Explain features without requiring knowledge of the internal architecture. Include technical details and limitations only when they affect usage or a developer's decision. - Document internals close to the code: execution flow, responsibilities, invariants, failure handling, and recovery. Use a focused README for mechanisms spanning multiple files; comments for local constraints. - Explain what is not obvious from the code. Keep documentation concise, current, and free of duplicated explanations. - Keep change history, implementation plans, and future work in PRs or local drafts. ## Working Norms - Prefer focused, minimal diffs that match nearby code. - Read the nearest `package.json`, source files, and tests before editing shared behavior. - Update docs and tests when behavior or public APIs change. - Do not switch package managers or add alternate tooling without explicit instruction. - Do not revert unrelated dirty-worktree changes. - Avoid destructive git operations unless explicitly requested. ## Quick References - `packages/core/src/runtime/`: runtime entrypoints - `packages/core/src/ontology/`: ontology builders and types - `packages/server/src/routes/`: server routes - `packages/client/src/generated/`: generated client output - `packages/app/src/`: the app (built-in pages in `pages/`, sections listed in `sections/catalog.ts`, the agent in `agent/`, shared components in `packages/ui/src/`)