Skip to content

feat: per-role transport (access: harness|api) — planner via direct API - #2

Merged
dyuhaus merged 2 commits into
mainfrom
feat/api-transport
Jul 17, 2026
Merged

feat: per-role transport (access: harness|api) — planner via direct API#2
dyuhaus merged 2 commits into
mainfrom
feat/api-transport

Conversation

@dyuhaus

@dyuhaus dyuhaus commented Jul 17, 2026

Copy link
Copy Markdown
Owner

What & why

Fable-over-subscription is going away. This adds a per-role transport so a
role can run over the direct Anthropic API (billed to David's own key, off the
interactive subscription) while the other role stays a normal harness subagent.
The concept lives in roles.config.json (access: "harness" | "api") so every
adapter — Claude Code, generic, programmatic, the live pi adapter — can honor it.

This PR is additive and safe to merge before any key exists. The planner stays
access: "harness"; behavior is unchanged until someone runs the flip. No key is
placed anywhere in the repo.

What changed

  • Config/schemaroles.<role>.access (default harness), providers.<p>.apiKeyFile
    (a path, never the secret), top-level classIds ({"fable":"claude-fable-5"}) so
    the API path can map an alias to a concrete --model id. Stays version: 1.
  • apply.pyvalidate() now returns (errors, warnings) and warns when an
    api role can't resolve a concrete model id; new resolve_api_model(); role_view
    exposes access/api_model/api_key_file; new resolve (machine-readable
    role facts, env|json) and prompt (rendered role charter) subcommands;
    set --access harness|api; print_table/generic show transport. install_claude()
    renders a subagent only for harness roles, removes a stale generated one
    for api roles (a hand-written file without the marker is left with a warning), and
    two-pass renders /pb and /pbg from per-transport dispatch fragments.
  • bin/role-call (committed) — runs an api role headless via the claude CLI:
    composes charter + task, applies the read-only allowlist for read-only roles, runs
    with the key exported only into the child process (never argv/logs), prints the
    result to stdout and role=… model=… cost=$… session=… to stderr. Supports
    --task-file / - / --ping / --dry-run / --force. Exit codes: 2 no key,
    4 role is harness-transport.
  • bin/set-api-key (committed) — interactive, terminal-only key intake to a
    0600 keyfile (~/appdata/anthropic/api-key); refuses non-tty, echoes nothing,
    prints only length + short fingerprint; --verify makes one tiny real call.
  • Templates/docsadapters/claude-code/dispatch/{planner,builder}-{harness,api}.md;
    /pb and /pbg use them; /pbg-planner & /pbg-builder document transport switching.
    PRIMITIVE.md gains a "Role transports (access)" section + flip runbook; README
    config snippet / quickstart / layout / harness table updated.

Verification (all no-cost; done on scratch copies)

  • apply.py validate — valid, no spurious warnings (planner still harness).
  • Regenerating the real ~/.claude from the (harness) config: planner.md and
    builder.md subagents byte-identical to pre-change; /pb differs only by
    line-wrap; /pbg normalizes "the builder" → "the builder subagent"; /pbg-*
    gain an additive transport paragraph. Nothing breaks before the flip.
  • Scratch config with planner access=api: agents/planner.md removed (marker-bearing),
    agents/builder.md kept, /pb rewritten to call bin/role-call planner with
    absolute paths; a marker-less planner.md is left in place with a warning; --dry-run
    prints "would remove …".
  • set planner --access api/--dry-run, real set, and set-back to harness — all correct;
    api set with no key present prints an advisory WARNING (never fails).
  • role-call planner --dry-run: command shown, key_source=MISSING, model
    claude-fable-5, read-only allowlist; with ANTHROPIC_API_KEY=<dummy>from env;
    with a 0600 keyfile → from <keyfile> and a real (fake-claude) invoke returns
    the result on stdout + the cost=$… line on stderr; a non-600 keyfile warns; a
    response lacking total_cost_usd emits the subscription-billing WARNING.
  • Failure paths: no key + real run → exit 2 with the remedial message;
    role-call builder … (harness) → exit 4; --force overrides.
  • apply.py generic (api config) prints the per-role "Reached via …" lines.
  • git diff scanned for key material — none (gitleaks pre-commit also clean).

Flip runbook (Step 12 — do this once the key exists; NOT done here)

  1. bin/set-api-key — David provisions the Anthropic key (interactive, in a real terminal).
  2. bin/role-call planner --ping — confirm a cost=$… line appears (proves it bills the API key).
  3. python3 apply.py set planner --access api — flips transport + regenerates (removes ~/.claude/agents/planner.md).
  4. /pb smoke on a tiny task — confirm the orchestrator invokes role-call planner and relays the cost.
  5. Update the machine AGENTS.md + CLAUDE.md sentences and the memory note (see below).

⚠️ Footgun

After the flip there is no planner subagent. Any orchestrator guidance that
says "delegate to the planner subagent" must be updated in the same session to
"run dev-primitive/bin/role-call planner", or /pb//pbg will have nothing to
delegate to. The regenerated /pb//pbg already say this; harness-level docs must be
brought in line at flip time.

Deferred to flip time (NOT edited in this PR — checklist)

  • /home/dyadmin/AGENTS.md — Model Routing: note planning may be routed via
    the direct API, in which case there is no planner subagent and /pb///pbg
    invoke dev-primitive/bin/role-call planner.
  • /home/dyadmin/CLAUDE.md — same one-sentence note so the Claude entrypoint
    matches the neutral contract.
  • Update the "Two-model dev primitive" memory note after the flip.

🤖 Generated with Claude Code


Update — now provider-generic (2nd commit)

The api transport is no longer Anthropic-only. bin/role-call now picks a
direct-API engine from the provider type, so an access: "api" role can run
over any OpenAI-compatible or local endpoint:

provider type engine mechanics
anthropic claude-cli headless, agentic claude -p (moved verbatim; unchanged behavior)
openai, local chat single-shot POST {baseUrl}/chat/completions, Authorization: Bearer, charter = system msg, task = user msg; no tools
google (none) role-call + validate steer you to Gemini's OpenAI-compatible endpoint as a type:"openai" provider
  • apply.py — engine derived in role_view and emitted as append-only resolve
    facts (ENGINE/PROVIDER/BASE_URL/BASE_URL_ENV; existing keys/order untouched);
    keyfile defaults to ~/appdata/<provider>/api-key; provider-scoped
    classIds["<provider>:<class>"] lookup (id → scoped → global → literal); new
    non-error warnings for google-type and for openai/local with no base URL; a chat
    "no tools — inline all context" note injected into the /pb /pbg dispatch text.
  • bin/role-callcase "$ENGINE"; chat engine does base-URL precedence
    ($baseUrlEnv → literal baseUrl → hard error), writes charter/task to two temp
    files, and calls a stdlib-urllib python child with the key exported only as
    RC_KEY (never argv — verified with ps). Per-engine stderr contract:
    claude-cli keeps cost=$<total_cost_usd> + the subscription-billing warning;
    chat prints tokens=<prompt>+<completion> (+ cost=$… when returned, tokens=?
    when usage absent). Exit codes 0/1/2/4 preserved; the exit-2 no-key message now
    names the provider.
  • bin/set-api-key — defaults the keyfile, keys its shape check by provider then
    type (anthropicsk-ant-, openroutersk-or-, openaisk-), and --verify
    targets the first api role's engine (chat success = a tokens= line; noted that for
    chat this proves key validity, not billing attribution).
  • Config/docsproviders.<p>.baseUrl (schema + demo openrouter entry) and a
    demo "openrouter:sonnet" scoped classId; PRIMITIVE.md gains a "Provider engines"
    section (engine table, base-URL precedence, non-agentic rule, per-engine cost
    contract, Gemini recipe, worked OpenRouter example); README + pi adapter note updated.

Active wiring untouched: both roles stay anthropic / harness; regenerating the
live ~/.claude from the config is a byte-for-byte no-op (verified). All Step-8 tests
pass with no keys and no spend (fake chat/completions server end-to-end: --ping
and full path return OK with tokens=12+2; keyfile fallback; exit-2 no-key; missing
base URL; HTTP 500 body excerpt; google guidance; --ping body carries max_tokens;
ps shows no key in any argv).

Follow-up — live-key smoke (deferred; needs a real provider key, NOT done here)

  • OpenRouter auth acceptancebin/set-api-key openrouter then
    bin/role-call planner --ping against the real endpoint returns a tokens= line.
  • Real model ids — confirm the pinned --id (e.g. anthropic/claude-sonnet-5)
    is one the endpoint actually serves.
  • usage.cost — check whether the provider returns usage.cost so the
    cost=$… suffix appears (OpenRouter does; some endpoints omit it → tokens=?).
  • End-to-end /pb — flip a role to a chat-engine provider and confirm the
    orchestrator runs bin/role-call and relays the token/cost figures.

Summary by CodeRabbit

  • New Features
    • Added per-role transport selection (harness vs direct provider API) with transport-aware role switching.
    • Introduced bin/role-call for running api roles via direct provider endpoints with cost diagnostics and safe key handling.
    • Added bin/set-api-key for interactive API-key provisioning (with optional verification).
    • Expanded apply.py with --access, plus resolve and prompt actions for role facts and rendered charters.
  • Documentation
    • Updated README and adapter guidance for the new two-model api workflow, templates, portability, and verification steps.

Add a per-role `access` transport ("harness" default, or "api") so a role can be
routed over a direct provider API call — billed to that provider's own key, off
the interactive subscription — while the other role stays a normal harness
subagent. Expressed in roles.config.json so every adapter can honor it.

Config/schema:
- roles.<role>.access: harness|api; providers.<p>.apiKeyFile (path, not secret);
  top-level classIds map for alias -> concrete model id resolution.
- config stays version 1, additive only; planner remains access=harness here.

apply.py:
- validate() returns (errors, warnings); warns when an api role can't resolve a
  concrete model id. New resolve_api_model(); role_view gains access/api_model/
  api_key_file. New `resolve` (env|json role facts) and `prompt` (rendered role
  charter) subcommands. `set --access harness|api`. print_table/generic show
  transport. install_claude() renders a subagent only for harness roles, removes a
  stale generated one for api roles (leaves hand-written files), and two-pass
  renders /pb and /pbg from per-transport dispatch fragments.

Wrapper + provisioning (committed, not generated):
- bin/role-call: run an api-transport role headless via the claude CLI; key
  exported only into the child process, never argv/logs; prints result to stdout
  and role/model/cost/session to stderr; --task-file/-/--ping/--dry-run/--force.
- bin/set-api-key: interactive, terminal-only key intake to a 0600 keyfile; never
  echoes the secret; optional --verify makes one tiny real call.

Templates/docs:
- dispatch/{planner,builder}-{harness,api}.md fragments; pb/pbg use them; pbg-*
  switch commands document transport. PRIMITIVE.md "Role transports" section +
  flip runbook; README config/quickstart/layout/harness-table updated.

Regenerating the real ~/.claude from the (still harness) config leaves the
planner/builder subagents byte-identical; /pb and /pbg differ only cosmetically
(line-wrap) or additively (transport docs), so nothing breaks before the flip.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This change adds per-role harness or api transport configuration, API model and credential resolution, transport-aware Claude adapter generation, direct API execution scripts, interactive key provisioning, and related CLI commands and documentation.

Changes

Per-role transport support

Layer / File(s) Summary
Transport contracts and configuration
roles.schema.json, roles.config.json, README.md, PRIMITIVE.md
Defines access, API model mappings, API key file configuration, default role transport settings, and transport usage and flip instructions.
Transport-aware configuration and adapter generation
apply.py, adapters/claude-code/*.tmpl, adapters/claude-code/dispatch/*
Adds transport validation, model and fact resolution, resolve/prompt commands, transport-specific agent generation and cleanup, and planner/builder dispatch variants.
API role execution and key provisioning
bin/role-call, bin/set-api-key
Runs API roles through configured direct-API engines, resolves credentials from environment or key files, reports usage, and securely provisions and verifies API keys.
Claude workflow integration
adapters/claude-code/pb.md.tmpl, adapters/claude-code/pbg.md.tmpl, adapters/claude-code/dispatch/*
Replaces hardcoded planner and builder references with transport-aware dispatch and role references for planning, building, and verification.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant apply_py
  participant Claude_adapter
  participant role_call
  participant Provider_API
  User->>apply_py: set role --access api
  apply_py->>Claude_adapter: regenerate transport-specific commands
  Claude_adapter->>role_call: dispatch planner or builder task
  role_call->>Provider_API: submit role charter and task with API key
  Provider_API-->>role_call: return result and usage
  role_call-->>User: print result and billing information
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 42.31% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and accurately summarizes the main change: per-role transport with planner direct API support.
Description check ✅ Passed The description covers the what, why, verification, and deploy/flip notes, with only minor template deviations.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/api-transport

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@adapters/claude-code/dispatch/planner-api.md`:
- Line 1: Remove the positional {{BUILDER_MODEL}} argument from the planner
command in adapters/claude-code/dispatch/planner-api.md:1-1 and from the
corresponding builder command in
adapters/claude-code/dispatch/builder-api.md:1-1 so transport fallback does not
rewrite model configuration. Update both remedy commands to use only set <role>
--access harness, preserving the existing planner and builder roles
respectively.

In `@adapters/claude-code/pbg-builder.md.tmpl`:
- Around line 29-34: The transport commands in
adapters/claude-code/pbg-builder.md.tmpl lines 29-34 and
adapters/claude-code/pbg-planner.md.tmpl lines 29-34 are hardcoded to one
installation path. Replace /home/dyadmin/dev-primitive in both templates with
the quoted {{DEV_PRIMITIVE_DIR}} placeholder while preserving the existing set
builder/planner commands and access behavior.

In `@adapters/claude-code/pbg.md.tmpl`:
- Line 35: Update the Step 0 planner instruction in the pbg template so it
delegates through {{PLANNER_REF}} rather than directly to planner, ensuring the
no-until flow remains transport-aware when access=api removes the planner
subagent file.

In `@bin/role-call`:
- Around line 155-157: Update the child invocation in the role-call command to
clear ANTHROPIC_AUTH_TOKEN and CLAUDE_CODE_USE_BEDROCK, CLAUDE_CODE_USE_VERTEX,
and CLAUDE_CODE_USE_FOUNDRY before exporting API_KEY_ENV and launching CMD.
Preserve the existing stderr capture and argument handling while ensuring the
child selects the exported API key.
- Around line 29-30: Update the read-only role configuration in bin/role-call so
it uses the read-only/plan permission mode and removes the destructive Bash
path, including Bash(find:*), from READONLY_ALLOWED_TOOLS. Keep only the
narrowly scoped inspection commands needed by read-only roles.
- Around line 187-193: The auth validation flow must not infer billing source
from role-call’s informational total_cost_usd field: update bin/role-call lines
187-193 to report cost without treating its presence or absence as
authentication evidence. Update bin/set-api-key lines 89-107 so validation does
not hardcode role-call planner --ping; exercise the configured provider or
otherwise validate each relevant $PROVIDER value while preserving the existing
API-key setup behavior. Use the existing role-call and provider-selection
symbols to keep both paths consistent.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: a494a8f0-0780-451a-a538-32cab4e038ef

📥 Commits

Reviewing files that changed from the base of the PR and between 50a7888 and 8ba51b4.

📒 Files selected for processing (15)
  • PRIMITIVE.md
  • README.md
  • adapters/claude-code/dispatch/builder-api.md
  • adapters/claude-code/dispatch/builder-harness.md
  • adapters/claude-code/dispatch/planner-api.md
  • adapters/claude-code/dispatch/planner-harness.md
  • adapters/claude-code/pb.md.tmpl
  • adapters/claude-code/pbg-builder.md.tmpl
  • adapters/claude-code/pbg-planner.md.tmpl
  • adapters/claude-code/pbg.md.tmpl
  • apply.py
  • bin/role-call
  • bin/set-api-key
  • roles.config.json
  • roles.schema.json

@@ -0,0 +1 @@
Run the planner over the direct API (billed to the configured Anthropic key, not this session's subscription). Write the task plus any relevant context to a temp file, then run via Bash: `{{DEV_PRIMITIVE_DIR}}/bin/role-call planner --task-file <that file>`. Planner turns can run many minutes — run it in the background and wait for completion, or use the maximum Bash timeout (600000 ms). Its stdout is the plan; the stderr `[role-call]` line reports the run's API cost — include that figure in your final report. If it exits non-zero, surface its error message to the user verbatim and offer the remedies it names (provision the key with `{{DEV_PRIMITIVE_DIR}}/bin/set-api-key`, or temporarily re-route planning with `python3 {{DEV_PRIMITIVE_DIR}}/apply.py set planner {{BUILDER_MODEL}} --access harness`). Do not silently do the planning yourself.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Transport fallback should not rewrite model configuration.

  • adapters/claude-code/dispatch/planner-api.md#L1-L1: remove the positional {{BUILDER_MODEL}}; it replaces the planner model.
  • adapters/claude-code/dispatch/builder-api.md#L1-L1: remove the positional {{BUILDER_MODEL}}; it can convert a pin into an unpinned class.

Both remedies should run only set <role> --access harness.

📍 Affects 2 files
  • adapters/claude-code/dispatch/planner-api.md#L1-L1 (this comment)
  • adapters/claude-code/dispatch/builder-api.md#L1-L1
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@adapters/claude-code/dispatch/planner-api.md` at line 1, Remove the
positional {{BUILDER_MODEL}} argument from the planner command in
adapters/claude-code/dispatch/planner-api.md:1-1 and from the corresponding
builder command in adapters/claude-code/dispatch/builder-api.md:1-1 so transport
fallback does not rewrite model configuration. Update both remedy commands to
use only set <role> --access harness, preserving the existing planner and
builder roles respectively.

Comment on lines +29 to +34
**Transport.** The builder's current transport is `{{BUILDER_ACCESS}}`. You can also
switch *how* the role is reached, not just which model:
`python3 /home/dyadmin/dev-primitive/apply.py set builder --access api|harness`.
Switching to `api` runs the builder as a direct API call (its own key, off the
subscription) and **removes the builder subagent file**; switching back to `harness`
recreates it. See PRIMITIVE.md "Role transports (`access`)".

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

The generated transport commands are tied to one machine.

  • adapters/claude-code/pbg-builder.md.tmpl#L29-L34: replace /home/dyadmin/dev-primitive with quoted {{DEV_PRIMITIVE_DIR}}.
  • adapters/claude-code/pbg-planner.md.tmpl#L29-L34: make the same installation-aware substitution.
📍 Affects 2 files
  • adapters/claude-code/pbg-builder.md.tmpl#L29-L34 (this comment)
  • adapters/claude-code/pbg-planner.md.tmpl#L29-L34
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@adapters/claude-code/pbg-builder.md.tmpl` around lines 29 - 34, The transport
commands in adapters/claude-code/pbg-builder.md.tmpl lines 29-34 and
adapters/claude-code/pbg-planner.md.tmpl lines 29-34 are hardcoded to one
installation path. Replace /home/dyadmin/dev-primitive in both templates with
the quoted {{DEV_PRIMITIVE_DIR}} placeholder while preserving the existing set
builder/planner commands and access behavior.

not vague.

1. **Plan ({{PLANNER_MODEL}}).** Delegate to the `planner` subagent with the task,
1. **Plan ({{PLANNER_MODEL}}).** Delegate to {{PLANNER_REF}} with the task,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Make the Step 0 planner call transport-aware too.

Line 28 still says to delegate directly to planner. With access=api, that subagent file is removed. Use {{PLANNER_REF}} there as well so /pbg without until: can derive acceptance criteria.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@adapters/claude-code/pbg.md.tmpl` at line 35, Update the Step 0 planner
instruction in the pbg template so it delegates through {{PLANNER_REF}} rather
than directly to planner, ensuring the no-until flow remains transport-aware
when access=api removes the planner subagent file.

Comment thread bin/role-call
Comment on lines +29 to +30
# Read-only tool allowlist for read-only roles (e.g. planner): inspection only, no mutation.
READONLY_ALLOWED_TOOLS='Read,Grep,Glob,WebFetch,WebSearch,TodoWrite,Bash(git log:*),Bash(git diff:*),Bash(git show:*),Bash(git status:*),Bash(ls:*),Bash(cat:*),Bash(head:*),Bash(tail:*),Bash(rg:*),Bash(grep:*),Bash(find:*),Bash(wc:*)'

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
claude --help | grep -E -- '--tools|--allowedTools|--permission-mode' || true

Repository: dyuhaus/dev-primitive

Length of output: 199


🏁 Script executed:

#!/bin/bash
set -euo pipefail

git ls-files bin/role-call
wc -l bin/role-call
cat -n bin/role-call | sed -n '1,220p'

printf '\n--- references ---\n'
rg -n --hidden --no-messages -- '--allowedTools|--tools|--permission-mode|READONLY_ALLOWED_TOOLS|role-call' .

Repository: dyuhaus/dev-primitive

Length of output: 16296


🌐 Web query:

Anthropic Claude Code CLI --allowedTools --tools --permission-mode documentation semantics auto-approve restrict tool set read-only plan mode

💡 Result:

Claude Code CLI offers robust mechanisms to manage agent permissions, tool access, and operational modes, enabling users to balance automation with security [1][2][3]. Permission Modes Permission modes control how Claude Code approves tool calls [1][3]. You can set the mode via the --permission-mode flag or in your settings files [1][3]. - default: Enables read-only exploration; interactive approval for edits and sensitive actions [1][3]. - acceptEdits: Allows reads, file edits, and common filesystem commands (e.g., mkdir, touch, mv) without prompting [2][3]. - plan: Allows only read-only operations to explore and propose changes; prevents actual file edits [1][3]. - auto: Uses background safety checks to auto-approve tool calls that align with your request [1][3]. - dontAsk: Denies all tools unless explicitly pre-approved via permissions rules (useful for CI/CD) [1][3]. - bypassPermissions: Skips all permission prompts (except for explicit security circuit breakers like rm -rf /), effectively auto-approving all tool calls [1][4][3]. Tool Control Flags - --allowedTools: Accepts a comma-separated list of tools to auto-approve for the session [2]. It corresponds to permissions.allow settings [5][6]. Note that in bypassPermissions mode, allowedTools does not restrict the agent from using other tools; it only pre-approves the ones listed [6]. - --disallowedTools: Removes tools from the agent's context entirely, preventing them from being used [5][6]. This is the most effective way to block specific tools even in permissive modes [6]. Semantics and Best Practices - Permissions are enforced by the CLI, not the model [1][7]. - To lock down an agent, pair --allowedTools with --permission-mode dontAsk [6][2]. - If you require autonomous execution without prompts but want to restrict tools, avoid bypassPermissions. Instead, use a combination of default or dontAsk modes, or explicitly define allowed/disallowed lists [4][6]. - You can manage permission rules interactively during a session using the /permissions command [1][8]. Documentation for these features is centralized at code.claude.com [1][9][8].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail
rg -n --hidden --no-messages -- '--allowedTools|--tools|--permission-mode|read-only|plan mode|readonly' README.md PRIMITIVE.md adapters bin apply.py

Repository: dyuhaus/dev-primitive

Length of output: 967


🏁 Script executed:

#!/bin/bash
set -euo pipefail
rg -n --hidden --no-messages -- 'READ_ONLY|read_only|readonly|access:' roles.config.json apply.py README.md PRIMITIVE.md bin/role-call adapters

Repository: dyuhaus/dev-primitive

Length of output: 1461


Tighten the read-only role permissions
--allowedTools only pre-approves calls; it does not make this role read-only. Bash(find:*) still leaves a destructive shell path open, so read-only roles should use a read-only/plan permission mode and a narrower allowlist.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@bin/role-call` around lines 29 - 30, Update the read-only role configuration
in bin/role-call so it uses the read-only/plan permission mode and removes the
destructive Bash path, including Bash(find:*), from READONLY_ALLOWED_TOOLS. Keep
only the narrowly scoped inspection commands needed by read-only roles.

Comment thread bin/role-call Outdated
Comment on lines +155 to +157
# --- invoke: key exported only into the child; never in argv --------------------
ERRFILE="$(mktemp)"
OUT="$( export "$API_KEY_ENV=$KEY"; "${CMD[@]}" 2>"$ERRFILE" )"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
env | sed 's/=.*//' |
  grep -E '^(CLAUDE_CODE_USE_(BEDROCK|VERTEX|FOUNDRY)|ANTHROPIC_AUTH_TOKEN|ANTHROPIC_API_KEY)$' || true
claude auth status --text || true

Repository: dyuhaus/dev-primitive

Length of output: 199


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Locate the script and inspect the relevant section.
git ls-files | rg '^bin/role-call$'
wc -l bin/role-call
sed -n '130,190p' bin/role-call

# Search for credential handling around the wrapper.
rg -n 'ANTHROPIC_AUTH_TOKEN|ANTHROPIC_API_KEY|CLAUDE_CODE_USE_(BEDROCK|VERTEX|FOUNDRY)|export "$API_KEY_ENV|env -i|unset ' bin/role-call

Repository: dyuhaus/dev-primitive

Length of output: 2967


🏁 Script executed:

#!/bin/bash
sed -n '1,130p' bin/role-call
rg -n 'CLAUDE_CODE_USE_(BEDROCK|VERTEX|FOUNDRY)|ANTHROPIC_AUTH_TOKEN|ANTHROPIC_API_KEY|env -i|unset ' bin/role-call

Repository: dyuhaus/dev-primitive

Length of output: 5779


🌐 Web query:

Claude Code authentication precedence ANTHROPIC_AUTH_TOKEN ANTHROPIC_API_KEY CLAUDE_CODE_USE_BEDROCK CLAUDE_CODE_USE_VERTEX CLAUDE_CODE_USE_FOUNDRY

💡 Result:

Claude Code determines authentication using a strict order of precedence. When multiple authentication methods are present, the CLI selects the first valid method found in the following order, from highest to lowest priority: [1][2] 1. Cloud Provider Credentials: If any of the environment variables CLAUDE_CODE_USE_BEDROCK, CLAUDE_CODE_USE_VERTEX, or CLAUDE_CODE_USE_FOUNDRY are set to 1, the CLI uses the corresponding cloud provider's authentication mechanism [1][3][4]. Only one of these can be active at a time [4]. 2. ANTHROPIC_AUTH_TOKEN: This environment variable is checked next [1][2]. It is used for routing requests through an LLM gateway or proxy and is sent as an Authorization: Bearer header [1][5]. 3. ANTHROPIC_API_KEY: This environment variable is checked third [1][2]. It is used for direct access to the Anthropic API and is sent as an X-Api-Key header [1][6]. In interactive mode, this key requires a one-time approval, which is then remembered; in non-interactive mode (-p), it is used automatically if present [1][5]. 4. apiKeyHelper: If defined, this mechanism runs a script to dynamically retrieve credentials (often used for rotating keys or vault-based management) [5][2]. 5. OAuth/Subscription Credentials: If no higher-precedence methods are configured, Claude Code defaults to using stored OAuth credentials from your Claude subscription or Console login (initialized via the claude auth login command) [1][2][7]. Important notes on precedence: - Precedence shadowing: If you have an active Claude subscription but also have ANTHROPIC_API_KEY set in your environment, the API key takes precedence once approved [1][2]. This can cause authentication failures if the key belongs to an expired or disabled organization; in such cases, running unset ANTHROPIC_API_KEY allows the CLI to fall back to your subscription [1][2]. - Status checking: You can verify which authentication method is currently active by running the command claude auth status [1][2][8]. - Configuration: You can manage settings, including custom API keys, via the /config command within an interactive session [1]. Settings can also be defined in configuration files (e.g., ~/.claude/settings.json) [5][9].

Citations:


🏁 Script executed:

rg -n 'ANTHROPIC_AUTH_TOKEN|CLAUDE_CODE_USE_BEDROCK|CLAUDE_CODE_USE_VERTEX|CLAUDE_CODE_USE_FOUNDRY|ANTHROPIC_API_KEY' .

Repository: dyuhaus/dev-primitive

Length of output: 222


Clear inherited auth vars before launching claude.
ANTHROPIC_AUTH_TOKEN and CLAUDE_CODE_USE_{BEDROCK,VERTEX,FOUNDRY} outrank the exported ANTHROPIC_API_KEY, so the child can still pick the wrong credential source. Unset the conflicting vars (or run the child under env -u ...) here.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@bin/role-call` around lines 155 - 157, Update the child invocation in the
role-call command to clear ANTHROPIC_AUTH_TOKEN and CLAUDE_CODE_USE_BEDROCK,
CLAUDE_CODE_USE_VERTEX, and CLAUDE_CODE_USE_FOUNDRY before exporting API_KEY_ENV
and launching CMD. Preserve the existing stderr capture and argument handling
while ensuring the child selects the exported API key.

Comment thread bin/role-call Outdated
Derive a direct-API engine from the provider `type` so an api-transport role
can run over any OpenAI-compatible or local endpoint, not just Anthropic:

- anthropic -> `claude-cli` (headless, agentic `claude -p`, unchanged/verbatim)
- openai|local -> `chat` (single-shot POST {baseUrl}/chat/completions, Bearer
  key exported only into the python child, never in argv)
- google -> no direct engine; role-call and validate point to Gemini's
  OpenAI-compatible endpoint as a type:openai provider

apply.py emits ENGINE/PROVIDER/BASE_URL(_ENV) facts (append-only), defaults the
keyfile to ~/appdata/<provider>/api-key, adds provider-scoped classIds
("<provider>:<class>") lookup, api base-url/engine warnings, and a chat
no-tools context note in the /pb /pbg dispatch fragments. set-api-key defaults
the keyfile, keys its shape check by provider/type, and verifies against the
first api role's engine. Active roles stay anthropic/harness; regenerating
~/.claude is a no-op.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@dyuhaus

dyuhaus commented Jul 17, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
bin/role-call (1)

85-90: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Place fallible assignments inside conditionals under set -e.

Both manual error paths are currently unreachable when their commands fail.

  • bin/role-call#L85-L90: assign FACTS within the if condition so resolver diagnostics are printed and cleaned up.
  • bin/role-call#L178-L187: assign OUT within an if/else so the original exit status is captured and ERRFILE is processed.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@bin/role-call` around lines 85 - 90, The fallible command substitutions in
bin/role-call at lines 85-90 and 178-187 must be placed inside conditional
contexts so set -e does not bypass their manual error handling. Update the FACTS
assignment to run within an if condition while preserving resolver diagnostics
cleanup, and restructure the OUT assignment within an if/else so the original
exit status is captured and ERRFILE processing remains reachable.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@bin/role-call`:
- Around line 285-293: Update the request flow around urllib.request.urlopen so
redirects are rejected before the request carrying the Authorization header can
be resent. Add a redirect-handling mechanism for the existing Request that
raises or otherwise aborts on 30x responses, while preserving the current
timeout and POST behavior.
- Around line 177-179: Update the child-environment setup in the role-call
invocation around ERRFILE, OUT, and CMD so it exports
CLAUDE_CODE_SUBPROCESS_ENV_SCRUB=1 alongside the API key before launching
claude, ensuring subprocesses, hooks, and MCP servers cannot inherit the key.
- Line 150: Update the command construction in bin/role-call so the combined
prompt is provided to claude through stdin rather than passed via the -p
argument. Preserve the existing timeout, model, and JSON output options while
removing the prompt from argv to avoid process-list exposure and argument-length
limits.

In `@PRIMITIVE.md`:
- Line 138: Remove the leading space from the inline code span in PRIMITIVE.md,
changing the documented cost suffix from ` cost=$<usage.cost>` to
`cost=$<usage.cost>` while preserving the surrounding wording.

---

Outside diff comments:
In `@bin/role-call`:
- Around line 85-90: The fallible command substitutions in bin/role-call at
lines 85-90 and 178-187 must be placed inside conditional contexts so set -e
does not bypass their manual error handling. Update the FACTS assignment to run
within an if condition while preserving resolver diagnostics cleanup, and
restructure the OUT assignment within an if/else so the original exit status is
captured and ERRFILE processing remains reachable.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: e61fb0bb-16c5-4824-8165-93eca8b13e57

📥 Commits

Reviewing files that changed from the base of the PR and between 8ba51b4 and 2c15806.

📒 Files selected for processing (10)
  • PRIMITIVE.md
  • README.md
  • adapters/claude-code/dispatch/builder-api.md
  • adapters/claude-code/dispatch/planner-api.md
  • adapters/pi/README.md
  • apply.py
  • bin/role-call
  • bin/set-api-key
  • roles.config.json
  • roles.schema.json
🚧 Files skipped from review as they are similar to previous changes (6)
  • adapters/claude-code/dispatch/planner-api.md
  • adapters/claude-code/dispatch/builder-api.md
  • bin/set-api-key
  • roles.schema.json
  • README.md
  • apply.py

Comment thread bin/role-call
$TASK"

# --- build the claude command ----------------------------------------------
CMD=( timeout "$TIMEOUT" claude -p "$PROMPT" --model "$MODEL" --output-format json )

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the relevant area of bin/role-call with line numbers.
sed -n '120,190p' bin/role-call | cat -n

# Find where claude is invoked and how PROMPT is constructed.
rg -n --no-heading 'claude -p|PROMPT=|TIMEOUT|CMD=\(' bin/role-call

# Look for any mention of stdin usage or headless/headless docs in the repo.
rg -n --no-heading 'stdin|headless|output-format json|claude -p' .

Repository: dyuhaus/dev-primitive

Length of output: 6268


🏁 Script executed:

#!/bin/bash
set -euo pipefail
sed -n '130,175p' bin/role-call | cat -n

Repository: dyuhaus/dev-primitive

Length of output: 2557


🌐 Web query:

Anthropic Claude headless mode stdin prompt -p documentation

💡 Result:

Anthropic provides "headless mode" for Claude Code via the -p (or --print) flag, which allows the tool to run non-interactively as a standard Unix process [1][2]. This mode is designed for automation, CI/CD pipelines, and scripted tasks [3][4]. Key mechanisms for headless mode include: - Execution: Add the -p flag followed by your prompt to run Claude Code as a single batch operation [1][3]. The process executes the agent loop, outputs the result, and terminates immediately [2][3]. - Stdin Input: Claude Code in headless mode reads from stdin, allowing you to pipe data directly into the prompt [1][2]. You can pipe content using standard shell patterns like cat file.txt | claude -p 'analyze this' or claude -p 'analyze this' < file.txt [2][5][3]. - Output Formatting: Use the --output-format flag to control the response, with options such as json, text, or stream-json for machine-readable parsing [1][2][3]. - Tool Permissions: Since there is no human in the loop to approve actions, you must use --allowedTools to pre-approve specific tools (e.g., Bash, Read, Edit) to prevent the agent from pausing for permission [1][2][3]. - Best Practices: - Since headless mode cannot handle follow-up conversational turns, include all necessary context and instructions within the initial prompt [5][3]. - Piped stdin input is subject to a size cap (10MB as of recent versions) [1][2]. For larger datasets, reference file paths directly in the prompt instead of piping the raw content [1][2]. - Use --bare to reduce startup time by skipping unnecessary hook, plugin, and memory auto-discovery, which is recommended for scripts and SDK calls [1]. For programmatic SDK-level control, advanced protocols exist that enable JSON-based stdin/stdout streams (--input-format stream-json and --output-format stream-json), allowing external applications to maintain session state and handle permission requests via a custom control protocol [6][4]. Top results: [1][2][6][3]

Citations:


Pipe the prompt through stdin instead of argv. bin/role-call:150 sends the combined prompt as a single command-line argument, exposing it in process listings and risking argv-length failures. Claude headless mode accepts stdin, so this can stay out of argv.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@bin/role-call` at line 150, Update the command construction in bin/role-call
so the combined prompt is provided to claude through stdin rather than passed
via the -p argument. Preserve the existing timeout, model, and JSON output
options while removing the prompt from argv to avoid process-list exposure and
argument-length limits.

Comment thread bin/role-call
Comment on lines +177 to +179
# --- invoke: key exported only into the child; never in argv ---------------
ERRFILE="$(mktemp)"
OUT="$( export "$API_KEY_ENV=$KEY"; "${CMD[@]}" 2>"$ERRFILE" )"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🔴 Critical | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the relevant script and search for the env var in the repo.
git ls-files | rg '^bin/role-call$|CLAUDE_CODE_SUBPROCESS_ENV_SCRUB|Anthropic|Claude|API_KEY_ENV'

printf '\n--- bin/role-call excerpt ---\n'
sed -n '150,210p' bin/role-call

printf '\n--- repo references to CLAUDE_CODE_SUBPROCESS_ENV_SCRUB ---\n'
rg -n "CLAUDE_CODE_SUBPROCESS_ENV_SCRUB" .

Repository: dyuhaus/dev-primitive

Length of output: 2641


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Pull a broader context around subprocess/env handling in bin/role-call.
ast-grep outline bin/role-call --view expanded || true

printf '\n--- surrounding lines ---\n'
sed -n '1,260p' bin/role-call

Repository: dyuhaus/dev-primitive

Length of output: 11239


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Verify whether the env var is documented upstream and what it is intended to do.
python3 - <<'PY'
import urllib.request, re
urls = [
    "https://code.claude.com/docs/en/env-vars",
]
for url in urls:
    try:
        with urllib.request.urlopen(url, timeout=20) as r:
            text = r.read().decode('utf-8', 'replace')
        print("URL:", url)
        for m in re.finditer(r'CLAUDE_CODE_SUBPROCESS_ENV_SCRUB.{0,180}', text):
            print(m.group(0))
            break
        else:
            print("No direct mention found in fetched HTML.")
    except Exception as e:
        print("URL:", url, "ERROR:", e)
PY

Repository: dyuhaus/dev-primitive

Length of output: 346


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Check the current line contents precisely in case surrounding context matters.
nl -ba bin/role-call | sed -n '168,186p'

Repository: dyuhaus/dev-primitive

Length of output: 199


🌐 Web query:

CLAUDE_CODE_SUBPROCESS_ENV_SCRUB Claude Code subprocess env scrub documentation

💡 Result:

CLAUDE_CODE_SUBPROCESS_ENV_SCRUB is an environment variable for Claude Code used to enhance security by restricting the information exposed to subprocesses [1][2]. When set to 1, this variable triggers several security controls for subprocesses such as the Bash tool, hooks, and MCP stdio servers [1][2]: 1. Environment Variable Scrubbing: It strips Anthropic API keys (e.g., ANTHROPIC_API_KEY) and various cloud provider credentials (e.g., AWS, GCP, Azure environment variables) from the environment inherited by child subprocesses [1][3][2]. While the parent Claude process retains these credentials for necessary API calls, child processes are prevented from accessing them, which mitigates risks related to prompt injection attacks attempting to exfiltrate secrets via shell expansion [1][2]. 2. PID Namespace Isolation (Linux only): On Linux systems, it executes Bash subprocesses within an isolated PID namespace [1][2]. This prevents the subprocess from viewing or signaling host processes via /proc or standard tools like ps, pgrep, and kill [1][2]. Consequently, any background daemons launched by the subprocess are confined and cannot persist beyond the session [4]. It is recommended to use this variable as part of a defense-in-depth strategy, particularly when running Claude Code in environments where untrusted users might trigger actions [3]. Note that in some versions, enabling this flag may impose stricter default permission modes [5]. For more granular control over specific app-level secrets (like GITHUB_TOKEN or NPM_TOKEN), it is often used in combination with sandbox.credentials [6].

Citations:


Scrub the Anthropic key from Claude subprocesses at bin/role-call:177-179. Set CLAUDE_CODE_SUBPROCESS_ENV_SCRUB=1 in the child environment before invoking claude; otherwise Bash tools, hooks, and stdio MCP servers can inherit and exfiltrate the key via prompt injection.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@bin/role-call` around lines 177 - 179, Update the child-environment setup in
the role-call invocation around ERRFILE, OUT, and CMD so it exports
CLAUDE_CODE_SUBPROCESS_ENV_SCRUB=1 alongside the API key before launching
claude, ensuring subprocesses, hooks, and MCP servers cannot inherit the key.

Comment thread bin/role-call
Comment on lines +285 to +293
req = urllib.request.Request(url, data=json.dumps(body).encode("utf-8"), method="POST")
req.add_header("Authorization", "Bearer " + key)
req.add_header("Content-Type", "application/json")
try:
timeout = float(os.environ.get("RC_TIMEOUT", "") or 3600)
except ValueError:
timeout = 3600
try:
resp = urllib.request.urlopen(req, timeout=timeout)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Map the file and inspect the relevant area.
wc -l bin/role-call
sed -n '240,330p' bin/role-call

# Find any redirect handling or urllib opener customization in this file.
rg -n "HTTPRedirectHandler|NoRedirect|build_opener|urlopen|add_header|redirect" bin/role-call

Repository: dyuhaus/dev-primitive

Length of output: 3783


🏁 Script executed:

python3 - <<'PY'
import inspect, urllib.request, urllib.error

print("HTTPRedirectHandler.redirect_request source:")
print(inspect.getsource(urllib.request.HTTPRedirectHandler.redirect_request))

print("\nRequest.add_header source:")
print(inspect.getsource(urllib.request.Request.add_header))
PY

Repository: dyuhaus/dev-primitive

Length of output: 2020


🏁 Script executed:

python3 - <<'PY'
import inspect, urllib.request

print(inspect.getsource(urllib.request.HTTPRedirectHandler.redirect_request))
PY

Repository: dyuhaus/dev-primitive

Length of output: 1814


Reject redirects before sending the bearer token. urllib.request copies Authorization onto redirected requests, so a 30x from the provider can leak the API key to the redirect target. The POST body is dropped, but the credential still needs redirect handling here.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@bin/role-call` around lines 285 - 293, Update the request flow around
urllib.request.urlopen so redirects are rejected before the request carrying the
Authorization header can be resent. Add a redirect-handling mechanism for the
existing Request that raises or otherwise aborts on 30x responses, while
preserving the current timeout and POST behavior.

Comment thread PRIMITIVE.md
A missing `total_cost_usd` is the tell that the call may have billed the
**subscription** rather than the API key — that warning is specific to this engine.
- `chat`: `role=… engine=chat model=<response model or requested> tokens=<prompt>+<completion>`,
with ` cost=$<usage.cost>` appended only when the provider returns a cost, and

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Remove the leading space inside the code span.

Change ` cost=$<usage.cost>` to `cost=$<usage.cost>` to satisfy MD038 and avoid rendering an unintended leading space.

🧰 Tools
🪛 markdownlint-cli2 (0.23.0)

[warning] 138-138: Spaces inside code span elements

(MD038, no-space-in-code)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@PRIMITIVE.md` at line 138, Remove the leading space from the inline code span
in PRIMITIVE.md, changing the documented cost suffix from ` cost=$<usage.cost>`
to `cost=$<usage.cost>` while preserving the surrounding wording.

Source: Linters/SAST tools

@dyuhaus
dyuhaus merged commit d2a641d into main Jul 17, 2026
1 check passed
@dyuhaus
dyuhaus deleted the feat/api-transport branch July 17, 2026 17:48
dyuhaus added a commit that referenced this pull request Jul 17, 2026
Reverts #2 (`d2a641d`). David decided to drop the dual-source
(API-credit + subscription) transport for `/pb` and keep planning on the
Claude subscription only.

## Why
- The transport code was **dormant** — no API key was ever provisioned
and both roles ran `harness`/subscription, so `/pb` never actually used
API credits.
- The credit-drain David observed came from the *separate* fable-cutoff
probe (in the homelab repo, now removed and its PR closed), whose 6h
`claude --model fable` calls consumed his metered subscription Fable
allowance → intermittent HTTP 429 "Usage credits are required for this
model".
- Per David's decision, removing the transport capability entirely
rather than leaving inert code in the public repo.

## Effect
- `roles.config.json` returns to the original two-role setup
(planner=fable, builder=opus, no `access`/`classIds`/`apiKeyFile`).
- `bin/role-call`, `bin/set-api-key`, dispatch fragments, and the
`access`/provider-engine additions are removed.
- `/pb` and `/pbg` return to the original subscription subagent flow.
- `apply.py validate` passes on the reverted tree.

## After merge
Pull main on both checkouts and run `python3 apply.py claude` to
regenerate the local adapter from the reverted config (behavior is
unchanged — subscription-only).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- **Changes**
- Simplified role configuration to focus on selecting planner and
builder models.
  - Removed direct provider API execution and API-key setup commands.
  - Reduced configuration commands to supported model-setting workflows.
- Claude Code integrations now consistently use planner and builder
subagents.
- Updated planning, building, and verification guidance for a clearer
workflow.

- **Documentation**
- Revised setup, usage, portability, and adapter documentation to
reflect the streamlined configuration.
- Added a dry-run example and clarified options such as `--id` and
`--no-apply`.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

Co-authored-by: dyuhaus-agent <yuhaus.david@gmail.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant