Default to using Bun instead of Node.js.
- Use
bun <file>instead ofnode <file>orts-node <file> - Use
bun testinstead ofjestorvitest - Use
bun build <file.html|file.ts|file.css>instead ofwebpackoresbuild - Use
bun installinstead ofnpm installoryarn installorpnpm install - Use
bun run <script>instead ofnpm run <script>oryarn run <script>orpnpm run <script> - Use
bunx <package> <command>instead ofnpx <package> <command> - Bun automatically loads .env, so don't use dotenv.
Bun.serve()supports WebSockets, HTTPS, and routes. Don't useexpress.bun:sqlitefor SQLite. Don't usebetter-sqlite3.Bun.redisfor Redis. Don't useioredis.Bun.sqlfor Postgres. Don't usepgorpostgres.js.WebSocketis built-in. Don't usews.- Prefer
Bun.fileovernode:fs's readFile/writeFile - Bun.$
lsinstead of execa.
Use bun test to run tests.
import { test, expect } from "bun:test";
test("hello world", () => {
expect(1).toBe(1);
});Use HTML imports with Bun.serve(). Don't use vite. HTML imports fully support React, CSS, Tailwind.
Server:
import index from "./index.html"
Bun.serve({
routes: {
"/": index,
"/api/users/:id": {
GET: (req) => {
return new Response(JSON.stringify({ id: req.params.id }));
},
},
},
// optional websocket support
websocket: {
open: (ws) => {
ws.send("Hello, world!");
},
message: (ws, message) => {
ws.send(message);
},
close: (ws) => {
// handle close
}
},
development: {
hmr: true,
console: true,
}
})HTML files can import .tsx, .jsx or .js files directly and Bun's bundler will transpile & bundle automatically. <link> tags can point to stylesheets and Bun's CSS bundler will bundle.
<html>
<body>
<h1>Hello, world!</h1>
<script type="module" src="./frontend.tsx"></script>
</body>
</html>With the following frontend.tsx:
import React from "react";
import { createRoot } from "react-dom/client";
// import .css files directly and it works
import './index.css';
const root = createRoot(document.body);
export default function Frontend() {
return <h1>Hello, world!</h1>;
}
root.render(<Frontend />);Then, run index.ts
bun --hot ./index.tsFor more information, read the Bun API docs in node_modules/bun-types/docs/**.mdx.
Anti-pattern. OMC's ralph and ultrawork skills are driven by a "boulder
never stops" stop hook that fires on every assistant turn regardless of elapsed
real time. When the actual blocker on progress is a long-running subprocess the
harness cannot track (a --scip reindex, a multi-minute test suite, an
external CI run, a remote queue), each boulder tick still demands a response —
turning a genuine wait into a rapid poll loop that burns tokens without
producing progress.
Symptoms observed in the 2026-05-28 session:
- Stop hook reinforces "continue working" while the only sensible action is waiting for an out-of-band process.
- Cancellation via
/oh-my-claudecode:cancelclears state files but the hook may keep firing for one or more turns from stale skill-active reinforcements. - The "skill-active" state file (
skill-active-state.json) is the usual culprit when the hook keeps firing afterstate_clearreports no state to clear; clear it explicitly as the final step of any cancel.
Convention. When task progress depends on a background subprocess the harness can't track:
- Exit ralph/ultrawork explicitly rather than emitting wait-loop
responses. Invoke
/oh-my-claudecode:canceland return control to the user, who can re-invoke ralph after the subprocess finishes. - Don't sleep inside the loop to mask the wait — that just delays the same token-spending tick.
- Use
run_in_background: truefor any process that the harness can track (Bun's tool will notify on completion); reserve cancel-and-resume for genuinely external waits. - Always clear
skill-activestate as the final step of any cancel —state_clear(mode="ralph")does not clear it on its own.
Upstream issue (recommended follow-up). The real fix lives in OMC's ralph
skill: after N consecutive assistant turns with no tool calls or only
status-check calls, exponentially back off the boulder cadence. Filing an
issue against oh-my-claudecode is tracked separately — see bead
MetaCoding-4jw for the discussion.
Anti-pattern. In the 2026-05-28 session, three executor agents reindexed
into /tmp sandboxes (correctly, to avoid lock contention with the running
serve process) but their summary reports said "Reindex completed" without
surfacing that the data lived in /tmp/metacoding-9le, not the user's
production $ORCHESTRATORS_ROOT/.metacoding/. The mismatch was caught only
by checking file timestamps — one iteration away from compounding into wasted
work against the wrong directory.
Convention. When you are an agent (or delegating to an agent) on a task that produces or mutates data artifacts (graph stores, parquet files, indexes, caches, etc.), the final report MUST explicitly include:
- The data-dir path of every artifact produced. Absolute paths only;
~and$VARexpand differently in different shells. - Whether that path is the user's production location or a sandbox.
Production = the path a downstream consumer (
serve, MCP tools, the eval harness) will read by default. Sandbox = anything else, including/tmp/*,/var/tmp/*, scratch worktrees, or any path the user did not ask for by name. - If sandbox: what was different about the run from a production reindex.
A different
--scipflag, a subset of repos, a different--data-dir, skipped tokens, no commit-identity scoping — anything the user would need to know before promoting the sandbox output to production.
This applies even when the task is "successful" — the failure mode is silent ambiguity, not error. Default to over-reporting locations rather than under-reporting.
red → green → how would I fake this? → refactor. The third step is not optional and is not the same as refactoring: on 2026-07-20 an invariant ("the defendant holds no pen") was refactored into place faithfully and then handed the pen back through a new public verb. The invariant was satisfied; the intention was not. Ask what would satisfy the check while defeating its purpose, before shipping.
- State a red as a PROPERTY, not as a defect to block. "Attack N must fail" moves sideways; "a port wrong about the source cannot score better than one that is right" moves toward the goal and can subsume whole families of findings.
- Tier by what you are touching. Instrument (judge, recorder, fixture schema, kernel, a bound decision) → fresh adversaries, full weight. Measured thing (a feature port, a build, a pack) → green + the fake-it question + a discriminating fixture. An instrument that is cheap to change is one nobody can trust.
- Definition of done: a fix ships with the evidence that would catch its regression. A fix in the code and not in the evidence has a half-life.
- Self-verification is never load-bearing. Measured, not assumed: builds have documented "load-bearing" gates that were not, comments have claimed source semantics the code did not implement, and a test called a hook that does not exist and passed. Every real finding came from a fresh adversary or the live source. Judges are always fresh, never the builder — and a judge must prove which tree it tested before its findings count.
This project uses bd (beads) for issue tracking. Run bd prime to see full workflow context and commands.
bd ready # Find available work
bd show <id> # View issue details
bd update <id> --claim # Claim work
bd close <id> # Complete work- Use
bdfor ALL task tracking — do NOT use TodoWrite, TaskCreate, or markdown TODO lists - Run
bd primefor detailed command reference and session close protocol - Use
bd rememberfor persistent knowledge — do NOT use MEMORY.md files
When ending a work session, you MUST complete ALL steps below. Work is NOT complete until git push succeeds.
MANDATORY WORKFLOW:
- File issues for remaining work - Create issues for anything that needs follow-up
- Run quality gates (if code changed) - Tests, linters, builds
- Update issue status - Close finished work, update in-progress items
- PUSH TO REMOTE - This is MANDATORY:
git pull --rebase bd dolt push git push git status # MUST show "up to date with origin" - Clean up - Clear stashes, prune remote branches
- Verify - All changes committed AND pushed
- Hand off - Provide context for next session
CRITICAL RULES:
- Work is NOT complete until
git pushsucceeds - NEVER stop before pushing - that leaves work stranded locally
- NEVER say "ready to push when you are" - YOU must push
- If push fails, resolve and retry until it succeeds