Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .behavior/v2026_02_15.khlone-v0/1.vision.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ crew:
foreman: ehmpathy/foreman

brains: # friendly aliases → brain slugs (resolved by rhachet)
claude: claude@anthropic/claude/code/opus/v4.5
claude: claude@anthropic/claude/opus/v4.5
codex: codex@openai/codex/v5.3-with-thought
grok: opencode@xai/grok/code-fast-1
gemini: gemini@google/gemini/code/v2
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ $ khlone act "implement auth"
1. khlone detects no zone state for @feat/auth
2. khlone auto-inits zone
3. khlone looks up hero config → role: foreman, brain: claude
4. khlone looks up "claude" alias → claude@anthropic/claude/code/opus/v4.5
4. khlone looks up "claude" alias → claude@anthropic/claude/opus/v4.5
5. khlone checks: is there an extant brainCli for foreman.1? → no
6. khlone calls rhachet to spawn a new brainCli from slug
7. rhachet returns brainCli instance (pid: 12345)
Expand Down Expand Up @@ -87,7 +87,7 @@ $ khlone ask "research auth patterns" --who researcher++
1. khlone parses --who researcher++ → enroll new researcher clone
2. khlone looks up "researcher" role alias → ehmpathy/researcher
3. khlone determines brain for new clone → hero default: claude
4. khlone looks up "claude" alias → claude@anthropic/claude/code/opus/v4.5
4. khlone looks up "claude" alias → claude@anthropic/claude/opus/v4.5
5. khlone checks: is there an extant brainCli for researcher.1? → no (just enrolled)
6. khlone calls rhachet to spawn a new brainCli from slug
7. rhachet returns brainCli instance (pid: 12346)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ interface Clone {
role: string; // role alias (e.g., mechanic)
roleSlug: string; // fully qualified (e.g., ehmpathy/mechanic)
brain: string; // brain alias (e.g., claude)
brainSlug: string; // full brain slug (e.g., claude@anthropic/claude/code/opus/v4.5)
brainSlug: string; // full brain slug (e.g., claude@anthropic/claude/opus/v4.5)
index: number; // instance index (the .n in mechanic.n)
status: CloneStatus;
pid: number | null; // brainCli process id (null if not spawned)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ khlone never spawns CLI binaries directly. it calls rhachet, and rhachet calls t

### 1.1 spawn a BrainCli from a brain slug

**the fundamental:** khlone holds a brain slug (e.g., `claude@anthropic/claude/code/opus/v4.5`). it needs to hand that slug to rhachet and get back a live BrainCli handle — a process that khlone's daemon can manage.
**the fundamental:** khlone holds a brain slug (e.g., `claude@anthropic/claude/opus/v4.5`). it needs to hand that slug to rhachet and get back a live BrainCli handle — a process that khlone's daemon can manage.

**why:** every clone needs a CLI process. the daemon spawns one per clone. the slug comes from `khlone.yml` alias expansion (e.g., user writes `claude`, khlone expands to full slug, rhachet expands slug into a runnable CLI).

Expand Down Expand Up @@ -240,7 +240,7 @@ these are preferences, not demands. rhachet should design its own contract — b
* the returned handle is long-lived — persists across many ask/act calls.
*/
const brainCli = await genBrainCli({
brainSlug: 'claude@anthropic/claude/code/opus/v4.5',
brainSlug: 'claude@anthropic/claude/opus/v4.5',
cwd: '/home/vlad/git/ehmpathy/myrepo',
role: 'ehmpathy/foreman',
series: null, // null on first spawn; ref text on respawn
Expand Down Expand Up @@ -349,7 +349,7 @@ these show exactly how khlone's daemon uses BrainCli at each touchpoint.

// 1. spawn brainCli for the hero clone
const brainCli = await genBrainCli({
brainSlug: 'claude@anthropic/claude/code/opus/v4.5',
brainSlug: 'claude@anthropic/claude/opus/v4.5',
cwd: '/home/vlad/git/ehmpathy/myrepo',
role: 'ehmpathy/foreman',
series: null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,6 @@ itll specify aliases for the brains too
e.g.,

grok = xai/grok/code-fast-1
claude = anthropic/claude/code/opus/v4.5
claude = anthropic/claude/opus/v4.5
codex = openai/codex/v5.3
etc
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ crew:
foreman: ehmpathy/foreman

brains: # brain aliases for --brain
claude: anthropic/claude/code/opus/v4.5
claude: anthropic/claude/opus/v4.5
grok: xai/grok/code-fast-1
codex: openai/codex/v5.3
gemini: google/gemini/code/v2
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -795,7 +795,7 @@ interface Clone {
role: string; // alias
roleRef: string; // "ehmpathy/mechanic"
brain: string; // alias
brainRef: string; // "anthropic/claude/code/opus/v4.5"
brainRef: string; // "anthropic/claude/opus/v4.5"
status: 'idle' | 'active';
task: {
focus: { slug: string; progress: number } | null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -241,8 +241,8 @@ class RateLimiter {
**the assumption:**
```ts
const BRAIN_ADAPTERS: Record<string, () => Promise<BrainCli>> = {
'anthropic/claude/code/opus/v4.5': () => import('rhachet-brains-anthropic'),
'anthropic/claude/code/sonnet/v4': () => import('rhachet-brains-anthropic'),
'anthropic/claude/opus/v4.5': () => import('rhachet-brains-anthropic'),
'anthropic/claude/sonnet/v4': () => import('rhachet-brains-anthropic'),
'opencode/opencode/v1': () => import('rhachet-brains-opencode'),
};
```
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -463,8 +463,8 @@ khlone resolves brain alias to BrainCli adapter:
// khlone/src/domain.operations/clone/resolveRoleBrain.ts

const BRAIN_ADAPTERS: Record<string, () => Promise<BrainCli>> = {
'anthropic/claude/code/opus/v4.5': () => import('rhachet-brains-anthropic'),
'anthropic/claude/code/sonnet/v4': () => import('rhachet-brains-anthropic'),
'anthropic/claude/opus/v4.5': () => import('rhachet-brains-anthropic'),
'anthropic/claude/sonnet/v4': () => import('rhachet-brains-anthropic'),
'opencode/opencode/v1': () => import('rhachet-brains-opencode'),
// future: grok, codex, gemini
};
Expand Down Expand Up @@ -585,7 +585,7 @@ khlone tracks session IDs per clone:
# {worktree}/.khlone/.bind/clones/mechanic.1/state.yml
slug: mechanic.1
role: ehmpathy/mechanic
brain: anthropic/claude/code/opus/v4.5
brain: anthropic/claude/opus/v4.5
sessionId: abc123-uuid-here # <-- tracked
status: idle
```
Expand Down Expand Up @@ -1138,8 +1138,8 @@ crew:
foreman: ehmpathy/foreman

brains:
claude: anthropic/claude/code/opus/v4.5
sonnet: anthropic/claude/code/sonnet/v4
claude: anthropic/claude/opus/v4.5
sonnet: anthropic/claude/sonnet/v4
opencode: opencode/opencode/v1

hooks:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ interface BrainAdapter {
interface BrainAdapterMeta {
/** adapter package name */
package: string;
/** brain identifier (e.g., 'anthropic/claude/code/opus/v4.5') */
/** brain identifier (e.g., 'anthropic/claude/opus/v4.5') */
brain: string;
/** implementation mode */
mode: 'cli' | 'sdk' | 'hybrid';
Expand Down Expand Up @@ -758,7 +758,7 @@ const resolveAdapter = async (input: { brain: string }): Promise<BrainAdapter> =
// rhachet/src/brains/registry.ts

interface BrainMeta {
slug: string; // 'anthropic/claude/code/opus/v4.5'
slug: string; // 'anthropic/claude/opus/v4.5'
package: string; // 'rhachet-brains-anthropic'
name: string; // 'Claude Code Opus'
mode: 'cli' | 'sdk';
Expand All @@ -767,14 +767,14 @@ interface BrainMeta {

const BRAIN_REGISTRY: BrainMeta[] = [
{
slug: 'anthropic/claude/code/opus/v4.5',
slug: 'anthropic/claude/opus/v4.5',
package: 'rhachet-brains-anthropic',
name: 'Claude Code Opus',
mode: 'cli',
auth: 'cli-login',
},
{
slug: 'anthropic/claude/code/sonnet/v4',
slug: 'anthropic/claude/sonnet/v4',
package: 'rhachet-brains-anthropic',
name: 'Claude Code Sonnet',
mode: 'cli',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ roles: # role aliases available for --who role++
**quote:**
```yaml
brains: # brain aliases for --brain
claude: anthropic/claude/code/opus/v4.5
claude: anthropic/claude/opus/v4.5
grok: xai/grok/code-fast-1
codex: openai/codex/v5.3
gemini: google/gemini/code/v2
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ interface Clone {
role: string; // role alias from crew.roles
roleRef: string; // resolved "ehmpathy/mechanic"
brain: string; // brain alias from crew.brains
brainRef: string; // resolved "anthropic/claude/code/opus/v4.5"
brainRef: string; // resolved "anthropic/claude/opus/v4.5"
status: CloneStatus;
task: {
focus: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ const mechanicRole = genRole({

// brains provide inference
const claudeBrain = genBrainRepl({
slug: 'anthropic/claude/code/opus/v4.5',
slug: 'anthropic/claude/opus/v4.5',
});

// actors combine role + brain
Expand Down Expand Up @@ -109,7 +109,7 @@ interface RhachetContext {

/**
* create brain instance by ref
* @param brainRef - e.g., "anthropic/claude/code/opus/v4.5"
* @param brainRef - e.g., "anthropic/claude/opus/v4.5"
* @returns brain ready to attach to actor
*/
createBrain(brainRef: string): Promise<BrainRepl>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -697,7 +697,7 @@ config:
mechanic: ehmpathy/mechanic
researcher: ehmpathy/researcher
brains:
claude: anthropic/claude/code/opus/v4.5
claude: anthropic/claude/opus/v4.5
grok: xai/grok/code-fast-1
zones:
- slug: "@main"
Expand Down Expand Up @@ -732,7 +732,7 @@ zone:
role: mechanic
roleRef: ehmpathy/mechanic
brain: claude
brainRef: anthropic/claude/code/opus/v4.5
brainRef: anthropic/claude/opus/v4.5
status: active
task:
focus:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
branch: vlad/v0-0-bhrain-cli
bound_by: init.behavior skill
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
emit your response to the feedback into
- .behavior/v2026_02_20.v0p0-bhrain-cli/$BEHAVIOR_REF_NAME.[feedback].v1.[taken].by_robot.md

1. emit your response checklist
2. exec your response plan
3. emit your response checkoffs into the checklist

---

first, bootup your mechanics briefs again

npx rhachet roles boot --repo ehmpathy --role mechanic

---
---
---


# blocker.1

---

# nitpick.2

---

# blocker.3
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
# block 0: BrainCli — audit gap report

> audit of implementation vs blueprint (`3.3.blueprint.v1.i1.md`) and blackbox criteria (`2.1.criteria.blackbox.md`)

---

## gap.1 = `--model` flag absent from dispatch args

**severity**: blocker (wrong model used at runtime)

**what**: `getOneDispatchArgs` does not pass `--model` to the claude CLI. the `BrainAtomConfig` has a `.model` field (e.g., `'claude-haiku-4-5-20251001'`, `'claude-opus-4-5-20251101'`) but it is never forwarded to the spawned process. all slugs — haiku, sonnet, opus — spawn with whatever default model the `claude` binary selects.

**where**: `src/_topublish/rhachet-brains-anthropic/getOneDispatchArgs.ts`

**evidence**: the args array contains `-p`, `--output-format`, `--input-format`, `--verbose`, `--allowedTools`, and optionally `--resume` — but no `--model`.

**fix**: add `'--model', input.config.model` to the args array. requires `AnthropicBrainCliConfig` to expose the `model` field from `BrainAtomConfig`, or pass the full atom config.

---

## gap.2 = `--model` flag absent from interact args

**severity**: blocker (same root cause as gap.1)

**what**: `getOneInteractArgs` also does not pass `--model`. interact mode should boot the correct model too.

**where**: `src/_topublish/rhachet-brains-anthropic/getOneInteractArgs.ts`

**fix**: same approach as gap.1 — add `--model` from config.

---

## gap.3 = `act()` has zero integration test coverage

**severity**: blocker (usecase 4 from blackbox criteria completely untested)

**what**: the integration test only exercises `ask()`. `act()` is never called against a live process. the criteria require:
- act returns BrainOutput with non-zero tokens
- act has access to mutation tools (Edit, Write, Bash)
- act on not-booted handle throws
- act on interact-mode handle throws

**where**: `src/_topublish/rhachet-brains-anthropic/__test__/genBrainCli.integration.test.ts`

**fix**: add `[t5]` or similar with `act({ prompt })` call, assert BrainOutput shape and metrics.

---

## gap.4 = interact mode boot has zero integration test coverage

**severity**: blocker (usecases 5-interact, 9, 10 from blackbox criteria untested)

**what**: no test boots a handle in interact mode. the criteria require:
- usecase 9: dispatch → interact preserves series; interact → dispatch preserves series
- usecase 5: terminal.onData fires with raw PTY bytes in interact mode
- usecase 10: terminal.write in interact mode relays to stdin

**where**: `src/_topublish/rhachet-brains-anthropic/__test__/genBrainCli.integration.test.ts`

**fix**: add test cases that boot interact, verify mode switch, and test terminal i/o.

---

## gap.5 = ask/act on interact-mode handle guard not tested

**severity**: caution (usecase 3/4 guard clause untested)

**what**: the blackbox criteria state: "given a BrainCli handle booted in interact mode, when ask/act is called, then it throws." this guard exists in prod code but is never exercised by a test.

**where**: `src/_topublish/rhachet-brains-anthropic/__test__/genBrainCli.integration.test.ts`

**fix**: add test: boot interact → call ask → expect throw. same for act.

---

## gap.6 = sequential metrics independence not tested

**severity**: caution (usecase 11 from blackbox criteria untested)

**what**: the criteria state: "given two sequential ask calls on the same handle, each BrainOutput has independent metrics (per-call, not cumulative), both > 0." no test exercises two consecutive dispatch calls on the same handle.

**where**: `src/_topublish/rhachet-brains-anthropic/__test__/genBrainCli.integration.test.ts`

**fix**: add test: boot → ask → ask → assert each BrainOutput has independent non-zero token counts.

---

## gap.7 = `AnthropicBrainCliConfig` lacks `model` field

**severity**: blocker (prerequisite for gap.1 and gap.2 fixes)

**what**: `AnthropicBrainCliConfig` has `{ slug, binary, spec, tools }` but no `model` field. the `BrainAtomConfig` from `rhachet-brains-anthropic` has `{ model, description, spec }`. the `model` field is lost in the `getOneConfig` helper which only extracts `.spec`.

**where**: `src/_topublish/rhachet-brains-anthropic/BrainCli.config.ts`

**fix**: add `model: string` to `AnthropicBrainCliConfig` and populate it from `CONFIG_BY_REPL_SLUG[replSlug].model` in `getOneConfig`.

---

## gap.8 = unit tests for dispatch/interact args don't assert `--model`

**severity**: caution (follows from gap.1/gap.2 — tests must cover model arg once added)

**what**: `getOneDispatchArgs.test.ts` and `getOneInteractArgs.test.ts` don't check for `--model` in output args. once the flag is added to prod code, tests must verify it.

**where**: `src/_topublish/rhachet-brains-anthropic/__test__/getOneDispatchArgs.test.ts`, `getOneInteractArgs.test.ts`

**fix**: add assertions for `--model` presence and correct value per config.

---

## summary

| # | gap | severity | scope |
|---|-----|----------|-------|
| 1 | `--model` absent from dispatch args | blocker | prod |
| 2 | `--model` absent from interact args | blocker | prod |
| 3 | `act()` not tested | blocker | test |
| 4 | interact mode not tested | blocker | test |
| 5 | ask/act on interact guard not tested | caution | test |
| 6 | sequential metrics not tested | caution | test |
| 7 | config lacks `model` field | blocker | prod |
| 8 | unit tests don't assert `--model` | caution | test |
15 changes: 15 additions & 0 deletions .behavior/v2026_02_20.v0p0-bhrain-cli/0.wish.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
wish =

to execute the decomposed scope of the block 0 of v0

as defined here .behavior/v2026_02_15.khlone-v0/3.5.decomposition.handoff.block_0.v1.i1.md


that whole khlone-v0 is avialable for reference

but htis handoff do .behavior/v2026_02_15.khlone-v0/3.5.decomposition.handoff.block_0.v1.i1.md

should give you all the info you need

and is the authority on the scope for this particular wish

Loading