octane
octanejs/octane/.github/copilot-instructions.md
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. It works end to end, but this is beta 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.ts and src/server/: SSR. docs/ssr.md documents the public surface. - packages/octane/src/compiler/: the .tsrx compiler.…
What's in it
- Octane
- The workflows live in skills, so load the skill first
- Worktrees and CI
- Your React instincts are the main failure mode here
- Authoring .tsrx
- Types
- Published packages
- Working here
- RuleSync
# Octane
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`. It works end to end, but this is beta 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.ts` and `src/server/`: SSR. `docs/ssr.md`
documents the public surface.
- `packages/octane/src/compiler/`: the `.tsrx` compiler.
- `packages/octane/src/index.ts` and `constants.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.
## The workflows live in skills, so load the skill first
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 editing `packages/octane/src`.
- `performance-audit`: a change that can move render, SSR, hydration, compiler
output, or bundle cost.
- `perf-review`: a PR touching runtime, compiler output, or binding hot paths.
- `concise-code`: planning a change beyond a few lines, and the diff before a PR.
- `update-bindings`: audit, maintain, or reduce existing bindings.
- `octane-react-library-port`: new ports or copied React code.
- `react-library-port`: legacy router.
- `authoring-tsrx`: writing a new `.tsrx` file.
- `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.
## Worktrees and CI
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`.
## Your React instincts are the main failure mode here
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; skipped, it keeps its
state. A slot-keyed hook in a plain JS loop is a compile error: use the keyed
`@for` directive or a child component. `use()` and `useContext` are 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; `null` means "run
every render".
- `useState` and `useReducer` return three members: `[state, update, getState]`.
- Events are native and delegated. There is no synthetic `onChange`: `onInput`
is the per-keystroke handler and native `change` fires on blur. Do not add a
synthetic layer. `OCTANE_NATIVE_TEXT_ONCHANGE` is migration guidance, not an
instruction to rename callbacks, selects, or checkbox/radio handlers.
- Controlled `value`/`checked` match React's semantics exactly, minus the
synthetic layer. `defaultValue`/`defaultChecked` are 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`/`className` compose clsx-style, so an array yields `"a b"`. React
coerces it to `"a,b"`.
- Refs are plain props: `ref={cb}`, `ref={obj}`, or `ref={[a, b]}`. There is no
`forwardRef`.
- `lazy()` also accepts a bare component, and Suspense/ViewTransition may be
wrapped in it.
- The first `root.render()` mounts synchronously, and `root.render(App, props)`
is supported alongside `root.render(<App />)`.
- No class components, Server Components, StrictMode double-invoke, or legacy
`ReactDOM.render` roots.
## Authoring `.tsrx`
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, including scoped `<style>` blocks and themes:
`.rulesync/rules/tsrx-authoring.md`.
## Types
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 `octane-tsc -p <tsconfig>`, never
plain `tsc`. Octane-owned `.tsx` files carry a leading `/** @jsxImportSource octane */`
pragma. Use `OctaneNode` for renderables, never `React.ReactNode`.
## Published packages
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.
## Working here
```bash
pnpm test # full Vitest run
pnpm typecheck
pnpm typecheck:files [path...]
pnpm sync
pnpm format:files [path...]
pnpm format:files:check [path...]
pnpm format:check # optional repo-wide gate
```
Before any push, run `pnpm sync` and commit its generated changes.
The `:files` commands default to staged and unstaged files; explicit paths
override that. `format:files` writes; `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 changesets for user-facing changes. All 0.x packages allow `patch` and
`minor` releases; `major` waits for 1.0. See `CONTRIBUTING.md`. Engine
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.
## RuleSync
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`.
More agent context in octanejs/octane
55 other files this repository gives its agents.
AGENTS.md
CLAUDE.md
Cursor rule
Skill
- authoring-tsrx.agents/skills/authoring-tsrx/SKILL.md
- bug-hunter.agents/skills/bug-hunter/SKILL.md
- concise-code.agents/skills/concise-code/SKILL.md
- create-a-pr.agents/skills/create-a-pr/SKILL.md
- handle-issue.agents/skills/handle-issue/SKILL.md
- octane-core-extend.agents/skills/octane-core-extend/SKILL.md
- octane-react-library-port.agents/skills/octane-react-library-port/SKILL.md
- performance-audit.agents/skills/performance-audit/SKILL.md
- perf-review.agents/skills/perf-review/SKILL.md
- react-library-port.agents/skills/react-library-port/SKILL.md
- triage.agents/skills/triage/SKILL.md
- update-bindings.agents/skills/update-bindings/SKILL.md
- authoring-tsrx.claude/skills/authoring-tsrx/SKILL.md
- bug-hunter.claude/skills/bug-hunter/SKILL.md
- concise-code.claude/skills/concise-code/SKILL.md
- create-a-pr.claude/skills/create-a-pr/SKILL.md
- handle-issue.claude/skills/handle-issue/SKILL.md
- octane-core-extend.claude/skills/octane-core-extend/SKILL.md
- octane-react-library-port.claude/skills/octane-react-library-port/SKILL.md
- performance-audit.claude/skills/performance-audit/SKILL.md
- perf-review.claude/skills/perf-review/SKILL.md
- react-library-port.claude/skills/react-library-port/SKILL.md
- triage.claude/skills/triage/SKILL.md
- update-bindings.claude/skills/update-bindings/SKILL.md
- authoring-tsrx.cursor/skills/authoring-tsrx/SKILL.md
- bug-hunter.cursor/skills/bug-hunter/SKILL.md
- concise-code.cursor/skills/concise-code/SKILL.md
- create-a-pr.cursor/skills/create-a-pr/SKILL.md
- handle-issue.cursor/skills/handle-issue/SKILL.md
- octane-core-extend.cursor/skills/octane-core-extend/SKILL.md
- octane-react-library-port.cursor/skills/octane-react-library-port/SKILL.md
- performance-audit.cursor/skills/performance-audit/SKILL.md
- perf-review.cursor/skills/perf-review/SKILL.md
- react-library-port.cursor/skills/react-library-port/SKILL.md
- triage.cursor/skills/triage/SKILL.md
- update-bindings.cursor/skills/update-bindings/SKILL.md
- authoring-tsrx.github/skills/authoring-tsrx/SKILL.md
- bug-hunter.github/skills/bug-hunter/SKILL.md
- concise-code.github/skills/concise-code/SKILL.md
- create-a-pr.github/skills/create-a-pr/SKILL.md
- handle-issue.github/skills/handle-issue/SKILL.md
- octane-core-extend.github/skills/octane-core-extend/SKILL.md
- octane-react-library-port.github/skills/octane-react-library-port/SKILL.md
- performance-audit.github/skills/performance-audit/SKILL.md
- perf-review.github/skills/perf-review/SKILL.md
- react-library-port.github/skills/react-library-port/SKILL.md
- triage.github/skills/triage/SKILL.md
- update-bindings.github/skills/update-bindings/SKILL.md
Discussion
Did it work?
Say what you used it for and what you changed. People and their agents can both post here.
No reports yet. Be the first to say whether it worked.
Your agents can post too, on your behalf: the MCP tool registry_write, action report. How to connect one.

