Astra × Fable: Full Power, Less Waste

Astra at Ultra. Fable at max. Shared rules, native hooks and deterministic automation — configured and tested, with workload savings still to measure.

Astra × Fable: Full Power, Less Waste — AI

I was using Opus 5 and GPT-5.6 Sol because I run coding agents frequently and wanted my usage allowance to last. Then Astra and Fable 5.1 arrived. FOMO influenced the decision: I wanted to work with the strongest models available in my setup, at their highest supported effort.

That meant making three changes together: updating every maintained local launch path, giving Claude Code and Codex the same maintained procedures, and reducing avoidable work around the models.

The configuration and mechanical changes are implemented and tested. Native usage observations now feed opportunistic updates during ordinary work. I do not yet have a monthly savings result or evidence that maximum effort delivers better value on every task.

Maximum settings need an execution audit

Changing the model in a settings file leaves several possible overrides: shell aliases, project configuration, custom agents, review commands, saved sessions, detached controllers and scripts that generate other scripts.

I checked those execution and regeneration paths. A launcher that selects the right model today is insufficient if its generator restores an older model tomorrow.

Claude and Codex independently researched their native configuration, then I reconciled the findings into one implementation specification. Disagreements in documentation and model reviews were checked against installed schemas, native behavior and harmless real requests. Implementation review exposed issues in trust handling, diagnostics and usage accounting that were corrected before completion.

The tested configuration is dated 5 September 2026: Codex CLI 0.153.4 and Claude Code 2.1.261. My earlier Astra article recorded an access failure on 3 September; fresh local Codex requests now succeed. That establishes access for the tested account and client, not universal availability.

The relevant settings in ~/.codex/config.toml are:

model = "gpt-6-astra"
model_reasoning_effort = "ultra"
plan_mode_reasoning_effort = "ultra"
review_model = "gpt-6-astra"

[agents]
default_subagent_model = "gpt-6-astra"
default_subagent_reasoning_effort = "ultra"

Ultra is the highest option exposed by the Codex client's live model catalog. It is a Codex setting; the raw Astra API documents a separate effort surface whose ceiling is max. These names should not be copied interchangeably between products. Delegated work also has its own defaults, so I set both the model and effort explicitly. OpenAI documents the subagent controls and delegation behavior.

For Claude Code, the relevant settings in ~/.claude/settings.json are:

{
  "model": "claude-fable-5-1[1m]",
  "fallbackModel": [],
  "env": {
    "CLAUDE_CODE_EFFORT_LEVEL": "max",
    "CLAUDE_CODE_SUBAGENT_MODEL": "claude-fable-5-1[1m]",
    "CLAUDE_CODE_SUBAGENT_MODEL_FORCE": "1"
  }
}

These are excerpts to merge into existing configuration, preserving unrelated settings. Claude's persistent maximum effort belongs in the environment variable. The saved effortLevel and modelSettings keys do not accept max. I removed the obsolete ultracode option from a launcher. Claude defines it as orchestration at xhigh; the existing max environment setting had already overridden it. Claude's model configuration documentation explains this distinction.

Maintained unattended launchers carry explicit provider, model and effort choices. A missing selected executable or unavailable model is surfaced; the configured availability fallback no longer silently selects a weaker coding model. A deliberate later user override remains possible.

Fresh sessions load the new defaults. Saved conversations can retain earlier choices, and provider-controlled internal helpers are separate from the primary model and coding delegates. Local settings also do not reconfigure unrelated remote services.

One maintained contract, two native integrations

An AGENTS.md pointer to CLAUDE.md was a useful start. It did not cover command discovery, native skills, hook payloads, model defaults, memory links or generated automation.

I introduced a versioned common policy with separate short native instructions for each agent. Project procedures retain one canonical owner. Codex skill adapters point to the same procedures used by Claude commands and require the complete applicable instructions to be read before execution.

Native instruction syntax still matters. Codex does not automatically expand Claude's @path/to/file imports; an imported requirement needs an explicit native read instruction.

A manifest declares those relationships: canonical sources, generated adapters, required references, model-bearing files and local retrieval guards. Source hashes make stale adapters detectable.

The agents still use separate sessions, tool protocols and authentication. They share maintained operating rules and source material; they do not acquire a shared model context or cross-provider prompt cache.

This follows the native discovery mechanisms for Codex instructions, Codex skills and Claude skills. The integration work is in the adapters and validation, rather than a claim that the formats are interchangeable.

User-level adapters also let personal controls apply while working in an existing checkout without placing personal harness files in that repository. Those adapters verify repository identity and resolve the canonical procedure from the active checkout, including a linked worktree.

A second opinion comes from the other provider

The request now has the same meaning everywhere: when I ask Claude for the other AI's opinion, it calls Codex Astra at Ultra. When I ask Codex, it calls Claude Fable 5.1 at max. This applies to interactive sessions and unattended parents in every workspace.

A global native skill supplies the caller's identity. Headless controllers pass that identity explicitly; a directory name, inherited environment marker or legacy command named codex-review does not decide the provider.

# Codex parent asks Claude for an independent review.
ai-second-opinion --caller codex --brief /private/task/brief.txt \
  --context /private/task/artifact.txt

# Claude parent asks Codex.
ai-second-opinion --caller claude --brief /private/task/brief.txt

These are local helper commands, not provider-native CLI flags. The parent includes the complete authorized task evidence: constraints, relevant source or artifact text, test results and the reviewed revision. Explicit text files are inlined in full. The reviewer cannot open a path mentioned in the brief, and the helper does not transfer images or attachments.

Each request starts one isolated, text-only native child with the opposite maximum model and effort. Child tools, MCP servers and delegated agents are disabled. This keeps an advisory reviewer from operating the repository or publishing an artifact. The parent's useful capabilities remain available. Both routing directions passed harmless native requests with the installed clients.

The parent reads the returned opinion, checks material claims and reconciles it with the evidence. Agreement between models is not verification; disagreement calls for inspecting the source. There is no automatic retry, weaker-model fallback or recursive consensus loop. An unavailable reviewer is reported as an incomplete opinion. An ordinary same-provider review does not automatically become an additional second-provider request.

Private run records retain input hashes, the requested model and effort, native usage when returned, elapsed time, status and the public answer. They do not retain prompts or native thinking. The normal observer does not automatically ingest these isolated ephemeral children; the helper keeps their usage in its own records, so the ongoing report does not cover them and is not complete account coverage. Failed requests may still consume usage without returning a usable answer or complete counters.

Keep the contract current automatically

Both agents now have native lifecycle hooks for session startup, mutation tools and completion. The hooks perform bounded local checks without an LLM or network request. Successful checks stay silent.

An explicit synchronization step registers reviewed manifests and guard hashes. Automatic refresh can update declared generated adapters from those registered sources. Unknown manifests cannot automatically execute arbitrary guards or write adapters, and paths that escape through symlinks are rejected.

A change requiring interpretation produces a diagnostic. The hook does not invent a replacement policy. Unresolved completion-time drift remains visible at the next SessionStart or supported PostToolUse event without creating a loop of repeated stop continuations.

The ongoing maintenance rule is explicit: when either agent changes AI instructions, commands, skills, hooks or launchers, it must update the canonical source and declared native adapters, synchronize them, and run the relevant checks.

Hook configuration was tested through actual native discovery and execution, including Codex's exact-definition trust. A valid JSON file alone would not prove the hook runs. Codex hooks and Claude hooks remain distinct integration surfaces.

Move executable boilerplate out of prompts

One batch orchestration command contained large embedded scripts that an agent repeatedly had to read and reproduce. I extracted nine code and prompt blocks into maintained source files and added a deterministic materializer.

The command went from 190,762 to 39,541 UTF-8 bytes. Six extracted files remained byte-identical; three received explicit model, runtime or process-ownership corrections. The executable source remains available for inspection and editing.

The materializer writes only its owned files, sets permissions and refuses an observed live controller. A later hardening pass added a per-batch kernel lock shared by the controller and state maintenance. The materializer preserves stop markers, task inputs, receipts and logs. Older paused controllers with custom changes now require a reviewed source refresh before launch; they are not overwritten wholesale.

The same principle applies to health checks. A local process snapshot can identify controllers, actual workers, nested review children and missing log writers. A model does not need to wake up simply to report whether a process exists.

These changes remove repeated boilerplate handling from the model's work while retaining the complete worker contract and validation requirements.

Load complete procedures by stage

A shipping procedure contained 99,458 bytes. It now has a short ordered router, a mandatory common contract and complete planning, implementation, validation and delivery references. The original procedure reconstructs byte-for-byte from the split source.

The router is only 3,256 bytes, but reporting that as the cost of shipping would be misleading. The required common material and current stage must also be loaded:

StageRouter + common contract + full stage
Planning56,484 bytes
Implementation39,410 bytes
Validation36,204 bytes
Delivery46,504 bytes

These are scoped source loads, not total workflow tokens. Work crossing stages still requires the relevant stage instructions, and a resumed run needs evidence of what has already completed.

I also checked native instruction-size limits. Codex's default combined project-document budget is 32 KiB. A large domain rulebook exceeded it, and a nested instruction chain could exceed it cumulatively. Short native forwarders now require explicit complete reads of the canonical files, including continuation after truncated tool output. A validator checks the discovery budget. This protects instruction completeness; the required full read still consumes context.

Keep the lessons, index the evidence

A recurring workflow had accumulated a 392,503-byte learning source. I kept that file byte-identical and generated a 23,831-byte index covering all 76 H2 sections and their H3 children.

The index records a source hash and line ranges. Selecting a section returns its original contents in full. Any content change invalidates the index, even when headings remain unchanged.

General lessons and all relevant source sections remain mandatory before diagnosis. Uncertainty requires widening the read. The index helps locate evidence; a short summary does not replace the evidence.

This is the condition that makes progressive loading defensible: retrieval must preserve the applicable constraints and make missing or stale context visible.

Run recurring work through the same controller

A shared controller now supports an explicit Claude or Codex provider. Both receive the same complete prompt through stdin, the same working directory, full stdout and stderr transcripts, and the same measured completion checks.

The selected provider determines the native invocation. The controller records provider, model, effort, progress and exit status. It does not ask an LLM to translate one provider's launch command into another on every iteration.

The controller handles the mechanical conditions directly:

  • A no-work check exits before launching an agent.
  • An exclusive kernel-backed lock prevents duplicate runs across providers.
  • Persistent stop checks and signals terminate only the iteration's owned process group.
  • Missing executables fail visibly without invoking another provider.
  • Existing stall and iteration limits remain, and completion is measured after each iteration, including the last permitted one.

That last check fixed a real boundary bug: work completed on the final allowed iteration had been reported as limit exhaustion. Tests now distinguish those outcomes.

Controller tests used isolated state and fake agent executables. Paused production automation stayed paused; a model migration is not a reason to launch real work as a test.

Retain logs and count usage correctly

For noninteractive commands, a full-log runner preserves complete output privately and returns a bounded tail with the real exit status. It preserves arguments, stdin, working directory, environment and signals. If the tail is insufficient for diagnosis, the full log is available. Interactive terminal applications run directly because captured children do not have a controlling terminal.

Usage accounting required its own correction. Codex cumulative counters can reset when a session resumes. Taking the largest cumulative value can undercount; summing duplicate exports can overcount.

The parser now counts current native records once per response ID. On a real two-response resume test, the parser matched the native totals: 37,278 input tokens, including 30,976 cached input tokens, and 18 output tokens. Cached input is a subset of that Codex input total, not another amount to add. Reasoning output is likewise a subset of output, not an extra amount to add. Legacy totals without reliable identity are labeled accordingly.

Claude records use their own native identities and cache-field conventions. The statusline exposes model and effort, current context, available plan-window usage and the native main-session cache ratio when present. Missing fields remain missing.

Token counts, context occupancy, subscription allowance and money are different measurements. A cache-hit ratio is not a percentage discount, and API list-price arithmetic is not a subscription bill. Claude's cost documentation and statusline reference describe the native surfaces behind that distinction.

Usage feedback during ordinary work

I added an observer to the shared Python tool so both agents can bring useful findings into normal work. It uses native lifecycle events and the Claude statusline, with no daemon, scheduled model analysis or cache-keepalive requests. It does not run while the clients are idle.

A private SQLite ledger stores usage metadata and hashed identifiers without copying prompts, message content or tool arguments. Each event reads only its supplied native transcript, resumes from a committed byte offset and reads at most 512 KiB. The report exposes missing prefixes, unread data and unsupported records. This is observed activity, not a complete account export; disabled hooks, ephemeral sessions and some child activity can leave gaps.

Finalized Codex responses count once by response ID. Finalized, persisted Claude messages count once by message ID, even when repeated across content blocks. Provisional streams, legacy cumulative counters and context-window snapshots are not added to those totals. Native cost snapshots retain their separate session-estimate scope.

Native testing found a timing issue: Claude's Stop hook can run before the final assistant record is saved. A recording-only SessionEnd hook captures that final record after it is persisted. Codex's tested Stop path already captured its final response. These are version-sensitive client behaviors; an unknown format remains unsupported instead of being guessed.

Reports cover one or seven days and refresh opportunistically, normally no more often than every 15 minutes. The first summary can become eligible after 20 new identified responses following the baseline. Subsequent summaries require 20 additional responses and a daily cooldown. A weekly cadence is also tracked. Those intervals do not promise delivery while a client is idle. The baseline starts with collection, and comparisons require two observed periods with enough responses. Different workloads and partial coverage still limit interpretation.

The assistant receives a pending reminder during a normal session, reads the report and shares useful findings. Headless output leaves user delivery pending for the next ordinary session. A hook attempt is not proof that I saw the result; the assistant acknowledges a summary only after including the findings in a user-visible response. ai-workspace report is available for optional inspection, but summaries no longer wait for me to ask for statistics.

Repeated large inputs, native output-limit failures or tool-definition cache churn can prompt a targeted investigation. The shared rule allows evidence-supported, reversible, low-impact improvements at the owned source within work the user has already authorized, with the necessary checks, review and a record of the change. It preserves maximum effort, required context and verification. A usage observation does not authorize a wider redesign or relaxed acceptance criteria.

This is ongoing observation and maintenance, not proof of savings. Actual charges, accepted outcomes, output quality and cost per accepted task remain unmeasured. A successful session exit does not establish that its work was accepted.

Preserve capability and measure the overhead

The constraint was maximum supported reasoning with required tools, reviews and tests retained. I did not add a cheaper model router, arbitrary output caps, fixed compaction thresholds, cache keepalive requests or forced cache lifetimes.

Stable reusable instructions help preserve cacheable prefixes. Native account-aware caching remains in control. Changing a model or request prefix can invalidate cache reuse; the providers' caches remain separate. Claude documents the exact-prefix behavior and its invalidation rules.

Useful independent work can still run in parallel. Each delegate performs additional model work, so parallelism can trade more tokens for elapsed time. It should have a bounded purpose and produce something the main task needs.

The added maintenance controls also have a cost. Most measured native starting contexts grew. At the user root, the serialized initial input increased from 18,006 to 22,683 bytes, including new policy and discoverable adapter metadata, measured before the later second-opinion skill and observer additions. That snapshot excludes references read later and is not a total workflow measurement.

Before the ongoing observer was added, twelve local mutation-hook calls measured 48.8 ms median, ranging from 47.21 to 51.96 ms. An actual resumed session reported 54 ms for startup and 58 ms for completion. These are small local samples; they do not establish p95/p99 latency or unchanged end-to-end speed.

When the observer extension was added, the global suite passed 41 behavior and accounting tests; tests added by the later second-opinion helper and materializer hardening are not included in that count. In a separate local microbenchmark with 20 fresh Python processes per variant, the hook median increased from 44.12 to 47.38 ms, about 3.3 ms. Those warmed local samples do not establish end-to-end workload latency or account savings.

Earlier, the initial mechanical validation included 27 global behavior and accounting tests, extraction-preservation checks, isolated controller tests, native discovery, and real interactive and headless runs. Claude's parent and delegated assistant metadata confirmed Fable 5.1; its stream did not expose the child's exact provider-side effort. The forced model, inherited maximum configuration and interactive maximum-effort display were verified.

The next test is cost per accepted task

The setup now runs the chosen models, maintains shared procedures and avoids several forms of repeated mechanical work. Whether that offsets the cost of stronger models remains a workload question.

I will evaluate the harness changes separately from the model change. Comparable tasks at the same model and effort can test the harness; any later comparison with Opus 5 or Sol needs a separately declared experiment. Combining both changes in one comparison would obscure the cause.

The measurements are accepted completions, elapsed time, input and output tokens, cache fields, retries, failed runs, human correction time and available plan-usage deltas. Cold and resumed sessions need separate reporting. Quality criteria and required tests must stay the same, and repeated trials are needed because model output varies.

The current result is a configured and regression-tested system with opportunistic usage feedback and an evidence-led maintenance rule. Monthly savings, unchanged quality and faster completion remain hypotheses to test against real work.

💬
Working with a team that wants to adopt AI-native workflows at scale? I help engineering teams build this capability — workflow design, knowledge architecture, team training, and embedded engineering. → AI-Native Engineering Consulting