Ray Fu, ex-Meta senior engineer and AI automation educator

Ray Fu

I'm an Ex Meta Senior Engineer that makes content and teaches OpenClaw and AI Automations.

stan.store/raycfu

How to Build an Agentic OS
Using Fable 5 Part 1

Agentic OS built on Fable 5

Don't want to figure this out alone? I walk members through every step inside the community. Join the Skool → skool.com/raycfu

This is the full step by step guide from the video. By the end you will have Claude Fable 5 running your projects like an employee that costs about 3 dollars a day. It reads your project every morning, decides what needs doing, hands the work to cheap models, verifies everything, tracks its own trust score, and stops itself before it overspends.

Total hands on time is about 2 hours. Then 30 days of letting it earn your trust.

What You Need

You need the Claude CLI (Claude Code) with Fable 5 access and a usage credits cap set. You need the llm CLI with an OpenRouter key so you can call cheap models, install it with: llm install llm-openrouter. You also need jq, gh, git, make, and cron installed, plus a project repo that has a test command.

All the files below live in your repo. The prompts go in a folder called loop, scripts go in loop/scripts, and memory files go in loop/memory.

Step 1: Configure the Engine

Before writing any files, set up Fable 5 correctly. These come straight from the official docs.

The model string is claude-fable-5. It has a 1 million token context window and costs 10 dollars per million input tokens and 50 dollars per million output tokens through usage credits.

Five facts that will save you from painful mistakes:

1. max_tokens caps thinking plus response text combined. Fable thinks a lot, so set max_tokens to at least 64000 on every call or it runs out of room mid thought.

2. There are five effort levels: low, medium, high, xhigh, and max. High is the default. Only use xhigh for long running conductor decisions, never inside loops for workers.

3. Refusals come back as HTTP 200, meaning they look like normal successful responses. Your scripts must check for stop_reason "refusal" in the JSON, not the exit code, or declined requests get silently swallowed.

4. Never ask Fable to show or explain its reasoning in its response. That triggers a refusal category called reasoning_extraction. Audit any old prompts that say "show your thinking" and delete those lines.

5. Turns are long by design. Hard tasks take many minutes. Run everything on scheduled cron jobs instead of sitting there waiting.

Also paste this exact block into every prompt that runs unattended, it stops the model from ending its turn with a plan instead of finished work:

You are operating autonomously. The user is not watching and cannot answer questions mid-task. For reversible actions that follow from the original request, proceed without asking. Before ending your turn, check your last paragraph: if it is a plan, a question, or a promise about work you have not done, do that work now with tool calls. End only when the task is complete or you are blocked on input only the user can provide.

And paste this exact block into every worker prompt to stop over delivery:

Don't add features, refactor, or introduce abstractions beyond what the task requires. A bug fix doesn't need surrounding cleanup. Don't design for hypothetical future requirements: do the simplest thing that works well. Don't add error handling or validation for scenarios that cannot happen. Only validate at system boundaries.

Step 2: Write the Constitution

Create a file called CLAUDE.md in the root of your repo. This is the rulebook. The trick is laws, not tips. Fable follows hard rules and quietly optimizes around soft suggestions, so every line needs a number, a never, or a command that checks it. Keep the whole file under 150 lines.

Here is a starting version, edit the paths and numbers to match your project:

# CLAUDE.md

## NEVER
- Never exceed 200 changed lines in one commit without asking.
- Never touch auth, billing, migrations, or prod config unattended.
- Never report work as done from your own assessment. Done means the check passed.
- Never invent a secret, an endpoint, or a convention. Stop and ask.
- Never add a dependency. Propose it in STATE.md and stop.
- Never exceed effort high inside any loop.
- Never edit or delete a test to make it pass. That is a fail, always.
- Never echo or explain your internal reasoning in response text.

## WORDS
- "done" means the check passes, nothing else
- "small" means under 50 changed lines
- "cleanup" means behavior identical, tests green before and after

## DONE
- Every task has a machine checkable done condition before work starts.
- A fresh agent that saw neither plan nor draft verifies against it.
- The verify script has the final vote.
- Maker and checker disagree twice, stop and queue for a human.

The test for every line: could the model comply 80 percent and still claim success? If yes, rewrite it with a number or a never.

Step 3: Set the Walls and the Gate

Create loop/contract.md. Three lists that decide how much freedom the system gets:

## acts alone
draft PRs on branches; fix lint and test debt; update STATE.md; label issues

## queues for me
auth, payments, migrations; any skill below auto tier; any diff over 400 lines

## wakes me up
verify fails twice on the same item
daily budget breached
anything requests a secret
a standing goal is violated

Then create loop/guardrails/verify.sh, a plain script that runs your tests. This dumb script gets the final vote on all work, the AI never approves its own output. Edit for your stack:

#!/usr/bin/env bash
set -e
npm run typecheck --if-present
npm test --if-present
npm run lint --if-present

Make it executable with chmod +x and confirm it passes on your repo right now. The whole system stands on this script.

Step 4: Build the Daily Loop

The Daily Loop

The loop has four seats. A one cent model reads the project each morning. Fable decides what to do but writes no code. A cheap model does the work. A fresh Fable verifies it. Then the bash script from step 3 votes last.

Create loop/triage.md, the cheap reader prompt:

You receive recent commits, open issues, and CI runs. Output ONLY findings:
- finding: one line
evidence: commit or issue or run id
status: actionable | informational
No fixes, no opinions. Nothing to report = output exactly "status: quiet".
Anything touching auth, payments, migrations, secrets = always actionable, noted "contract-sensitive".

Create loop/conductor.md, the Fable decision prompt:

You are the conductor. You do not write code. You do not edit files.
1. Read STATE, TRUST LEDGER, CONTRACT below. Do not trust memory of them.
2. Pick the ONE highest-value actionable item.
contract-sensitive, ambiguous, or likely over 400 line diff -> action: queue
nothing worth doing -> action: stop
3. Else action: execute, with a spec a mediocre model can follow.
Output ONLY this JSON:
{ "action": "execute|queue|stop", "item": "...", "skill": "kebab-case, stable across runs", "spec": "...", "done_when": ["verifiable", "..."] }
You are expensive. Be brief. Your output is a decision, not an essay.

Create loop/workers/implement.md, the worker prompt:

You receive a work order (JSON). Execute the spec exactly.
Do the ONE next step toward done_when. Small diffs win.
Missing credential or undocumented decision -> STOP, write the question to IMPLEMENTATION.md. Never invent secrets or conventions.
Record what you did and why in IMPLEMENTATION.md (3 lines max).

Create loop/workers/verify.md, the inspector prompt:

You receive a SPEC and a DIFF, nothing else. Judge only what is in front of you.
1. Does the diff satisfy every done_when? Cite lines.
2. Anything outside the spec's scope? Instant fail. Deleted or skipped tests? Instant fail.
Output exactly one line: "PASS: reason" or "FAIL: reason".
The maker was confident. That is not evidence.

Then create loop/loop.sh, the script that runs the whole thing. The flow is: check budget first, pipe git log and issues into the cheap model with the triage prompt, exit if quiet. If actionable, call Fable with claude -p using the conductor prompt at effort xhigh with read only tools and JSON output. Check the response was actually served by Fable and not rerouted. Parse the work order. If action is execute, create a git worktree, run the cheap worker (something like Kimi or DeepSeek through OpenRouter) with the implement prompt and the work order. Then call a fresh Fable at effort high with no tools, showing it only the spec and the diff, using the verify prompt. If it says PASS and your verify.sh script also passes, log a pass to the trust ledger and either ship or queue based on the skill's tier. Otherwise log a fail.

The key claude command for the conductor looks like this:

claude -p "$(cat conductor.md)
STATE: $(cat memory/STATE.md)
TRUST: $(./scripts/trust-log.sh --render)
CONTRACT: $(cat contract.md)" --model claude-fable-5 --effort xhigh --allowedTools "Read" --output-format json

And the verifier call:

claude -p "$(cat workers/verify.md)
SPEC: $(jq -r .spec work-order.json)
DIFF: $(cd ../wt-1 && git diff)" --model claude-fable-5 --effort high --allowedTools "" --output-format json

Run the loop once by hand. A quiet repo should exit clean for about a penny. An actionable repo should produce a work order with all five fields.

Step 5: The Trust Ledger

The Trust Ledger

Create loop/scripts/trust-log.sh. It maintains a file called memory/trust.tsv with three columns: skill name, total runs, and passes. Every finished task logs a pass or fail for its skill, and the script computes a tier from the numbers.

The tier rules: 20 or more runs with a 95 percent or better pass rate means auto, that skill ships unattended. Under 10 runs or under 90 percent means watch, draft only. Everything in between means queue, verified drafts wait for your approval. Demotion is automatic. When an established skill drops below 90 percent the script prints an alert to stderr, which cron emails to you.

Seed it with 4 starter skills, one file each in loop/skills: fix-lint-debt, fix-flaky-test, bump-deps, and triage-issues. Each skill file gets a description, steps, a never list, and a verifiable done condition. Every skill starts at watch.

Step 6: The Goals Directory

Standing Goals

A goal you only verify once is an assumption with a timestamp. So every finished task writes a small file in a goals folder, and every one of those files gets rechecked daily, forever.

Each goal file looks like this:

predicate: cd $REPO && npm test -- tests/auth 2>&1 | tail -1 | grep -q passing
born: 2026-07-06
status: satisfied
last-pass: 2026-07-06
on-violation: wake me. Do not auto-fix.
retire-when: auth module deleted. Retirement is a human decision.

The predicate is just a shell command. Exit 0 means the thing is still true. If a shell script cannot check it, do not write it as a goal. This works for non code things too: find invoices/overdue -mtime +45 | wc -l should print 0, or test -s reports/this-month-review.md.

Create loop/verify-goals.sh, a script that loops over every goal file, runs each predicate with a 60 second timeout, marks it satisfied or VIOLATED, and logs every result with a timestamp to memory/goal-ledger.tsv. If anything is violated it exits 1 and lists the broken goals. When a goal breaks, check what merged since its last pass date with: git log --oneline --since=that-date. Those commits are your suspects.

If you want to rest including steps 7 to 10 and the complete flow:

make sure to follow my instagram at instagram.com/raycfu for part 2 or check out my complete guide in the skool! → skool.com/raycfu