Please also reference the following rules as needed. The list below is provided in TOON format, and @ stands for the project root directory.
rules[4]:
- path: @.agents/memories/core-engineering.md description: Performance-first engineering and self-review gates for Octane framework fundamentals applyTo[5]: packages/octane/src/,packages/app-core/src/,packages/vite-plugin-octane/src/,packages/rspack-plugin-octane/src/,packages/rsbuild-plugin-octane/src/**
- path: @.agents/memories/cursor-cloud.md description: "Cursor Cloud VM setup: Node PATH, targeted test/typecheck, example dev servers" applyTo[2]: .cursor/**,.cursor/environment.json
- path: @.agents/memories/testing.md description: Octane test quality and observation-boundary rules applyTo[5]: /.test.,/.spec.,/tests/,/_fixtures/,benchmarks/**
- path: @.agents/memories/tsrx-authoring.md description: "Full .tsrx authoring reference: components, text holes, events, control flow, refs" applyTo[1]: **/*.tsrx
As this project's AI coding tool, you must follow the additional conventions below, in addition to the built-in functions.
Octane is Dominic Gannaway's successor to Inferno: a React-shaped UI framework
with hooks, memo, context, portals, Suspense, and transitions, compiled ahead
of time from .tsrx. The runtime, compiler, SSR, hydration, and large test suite
work, but this is alpha and APIs still move.
Trust the source over any summary, this file included:
packages/octane/src/runtime.ts: the client runtime. It is long and heavily commented, and those comments are the design spec.packages/octane/src/runtime.server.tsandsrc/server/: SSR.docs/ssr.mddocuments the public surface.packages/octane/src/compiler/: the.tsrxcompiler.packages/octane/src/index.tsandconstants.ts: the public client API.docs/differences-from-react.md: the divergence contract.docs/packages.md: the generated package inventory, checked by CI.
Fix defects in the package that owns the behavior and add the regression there. Do not hide framework defects behind app workarounds, weak tests, generated output, or test-only behavior; retain the integration scenario as end-to-end evidence.
Branch, PR, issue, bug, and audit procedures live in skills. Load one when its trigger first arises, even if it is a later step you chose:
create-a-pr: before any branch, commit, changeset, or PR.handle-issue: a GitHub issue number or link.bug-hunter: a failing test, a regression, or behavior that differs from expectation.octane-core-extend: before editingpackages/octane/src.performance-audit: a change that can move render, SSR, hydration, compiler output, or bundle cost.octane-react-library-port: a new or existing@octanejs/*binding.react-library-port: legacy compatibility trigger; immediately followoctane-react-library-port.authoring-tsrx: writing a new.tsrxfile.triage: the owning area is unclear.
Each skill is .rulesync/skills/<name>/SKILL.md, with a generated per-tool copy;
read that path directly if your tool cannot load a skill by name.
New tasks use a dedicated worktree/non-default branch. Primary checkout and
local main/master are read-only.
A pushed PR is not done. Run current-head CI; fix failures until relevant checks
pass. If draft CI skips, mark ready unless asked not to. Never claim done before
green CI. Preserve <!-- CURSOR_SUMMARY -->…<!-- /CURSOR_SUMMARY -->; see
create-a-pr.
Octane looks like React but differs deliberately. Check
docs/differences-from-react.md before changing any of these:
- Hooks are keyed by compiler-assigned call-site slot, not call order, so a hook
may sit behind a condition or after an early return. A slot-keyed hook in a
plain JS loop is a compile error: use the keyed
@fordirective or a child component.use()anduseContextare exempt. - An omitted dependency array is inferred by the compiler, not a bug. An explicit
array keeps React's exact behavior and is never rewritten;
nullmeans "run every render". useStateanduseReducerreturn three members:[state, update, getState].- Events are native and delegated. There is no synthetic
onChange:onInputis the per-keystroke handler and nativechangefires on blur. Do not add a synthetic layer.OCTANE_NATIVE_TEXT_ONCHANGEis migration guidance, not an instruction to rename callbacks, selects, or checkbox/radio handlers. - Controlled
value/checkedmatch React's semantics exactly, minus the synthetic layer.defaultValue/defaultCheckedare the uncontrolled escape. - The keyed reconciler is LIS-based, not
lastPlacedIndex. Final DOM and survivor identity are guaranteed; the set of physically moved nodes is not. use()starts provably-independent fetches together and suspends once per stratum. React runs the same code as a waterfall. Do not "fix" fetch-start timing, batch replay counts, or prefetch behavior toward React.class/classNamecompose clsx-style, so an array yields"a b". React coerces it to"a,b".- Refs are plain props:
ref={cb},ref={obj}, orref={[a, b]}. There is noforwardRef. lazy()also accepts a bare component, and Suspense/ViewTransition may be wrapped in it.- The first
root.render()mounts synchronously, androot.render(App, props)is supported alongsideroot.render(<App />). - No class components, Server Components, StrictMode double-invoke, or legacy
ReactDOM.renderroots.
Read a nearby .tsrx file first. The parts with no JavaScript equivalent:
function f() @{ … }is shorthand for returning JSX. The@{ … }scope ends with exactly one output node.- Dynamic text needs a cast,
{expr as string}, unless the expression is provably a string. A bare{expr}is a renderable hole, not text. - Template control flow uses directive blocks:
@if/@else,@for (const x of xs; key x.id)/@empty,@switch/@case/@default, and@try/@pending/@catch. Plain JS control flow stays in setup.
Full reference: .rulesync/rules/tsrx-authoring.md.
Never write declare module '*.tsrx' in a published package's src/. It
silences .tsrx resolution rather than fixing it, so every import it covers
becomes any, including the package's own exported components. It is ambient, so
it ships in the tarball and applies to any program that includes it.
pnpm tsrx-decls:check enforces this.
Typecheck any program containing .tsrx with tsrx-tsc --noEmit, never plain
tsc. Octane-owned .tsx files carry a leading /** @jsxImportSource octane */
pragma. Use OctaneNode for renderables, never React.ReactNode.
Ship every importable .tsrx, .tsx, .ts, and .js module as authored and
point package exports at that source. Never publish Octane compiler output; the
consuming application compiles the source with its own toolchain.
pnpm test # full Vitest run
pnpm typecheck
pnpm typecheck:files [path...] # defaults to staged and unstaged files
pnpm sync
pnpm format:files [path...] # defaults to staged and unstaged files
pnpm format:files:check [path...] # defaults to staged and unstaged files
pnpm format:check # optional repo-wide gateBefore any push, run pnpm sync and commit its generated changes.
Scoped typecheck and Prettier commands default to staged and unstaged Git diffs;
explicit files or directories override that default. format:files writes and
format:files:check is read-only. Use repo-wide checks only when needed.
pnpm test runs package prechecks, then one root Vitest invocation for every
project in vitest.config.js; it does not fan out through package test
scripts. Root config uses silent: true. While diagnosing, pass
--silent=false for all console output or --silent=passed-only for failing
tests. CLI options override the config.
For binding parity test setup, follow docs/react-parity-testing.md and the
octane-react-library-port skill.
Add a changeset for user-facing package changes; stay on the patch track while
Octane is 0.x. Runtime, compiler, scheduler, reconciler, SSR/hydration, and build
pipeline changes follow .rulesync/rules/core-engineering.md.
Never mutate a parsed AST during compilation: rewrites are copy-on-write. Tests deep-freeze adopted parser ASTs, so an in-place write throws at the offending line.
Generated agent files come from .rulesync/rules/: edit those and run
pnpm rules:generate; never hand-edit a generated file. This root rule becomes
CLAUDE.md, AGENTS.md, GEMINI.md, .github/copilot-instructions.md, and
.cursor/rules/project.mdc. The other rules carry globs, so agents that
support path-scoped rules load them only when you open a matching file.
Cursor Cloud VM setup is .rulesync/rules/cursor-cloud.md.