Crux v0.5.0
Highlights
-
Promote
@use-crux/core/observability,@use-crux/otel, and the local Go observability read model to stable beta, replacing process-local reliability assumptions with an explicit multi-invocation contract. See ADR 0002 for the full rationale.Cut the graph record wire/storage contract over to schema v2 only (
runIdfor the logical operation,traceIdfor distributed correlation, andsegmentId/segmentSeqfor one physical process/isolate/invocation). There is no v1 compatibility window: on first startup against the new schema, the local Go backend transactionally discards pre-v2 observability rows, which carry no truthful execution-segment identity, and rebuilds automatically. Every other local table is untouched and no manual.cruxdeletion is required.Add explicit lifecycle ownership for suspend/resume:
run.suspend({ reason })ends the current segment and returns a serializableCruxPropagationCarrier, andobserve.resumeRun(carrier, { reason })opens a fresh segment on the same logical run and emitsrun:resumebefore any child record.observe.withContext()remains context-only and can no longer be mistaken for a resume/suspend/end mechanism. Flow suspension and Convex swarm turns now use this shape instead of persisting a captured context and calling an implicit end.Make delivery lossless and per-record. The transport now inspects an indexed disposition (
accepted/rejected, with aretryableflag) for every sent record instead of trustingresponse.ok; a malformed or partial receipt retries every unaccounted-for record.recordIdidentifies immutable content: an exact duplicate is accepted idempotently, and a conflicting payload under the same id is rejected and diagnosed rather than silently overwriting the original.Add a framework-neutral host lifecycle port (context/defer/deadline) with first-party bindings: Node (
withNodeObservableInvocation), generic serverless (withObservableInvocation), Cloudflare Workers (withWorkersObservableInvocationfrom the new@use-crux/core/observability/workerssubpath, usingExecutionContext.waitUntilwith nonodejs_compatrequirement), and Convex (createCruxConvex()/action/internalAction/agent/swarm wrappers, bound automatically). Every wrapper reports a structuredObservabilityFlushResult(status,delivered,rejected,remaining,deadlineExceeded) instead of a boolean; passonDrainto inspect it. The Convex default flush bound drops from 20 seconds to a fixed 3-second window, since Convex exposes no per-invocation deadline API — existing callers relying on the old window should inspectonDrainor pass an explicit timeout.Remove the stream-finalizer grace timer. Only a stream's own terminal signal (drain, early return, or throw) ends its span, immediately, with stream-derived metrics; a late or never-arriving provider completion attaches as a linked
usage.observedevent/artifact and can never reopen the span or change its recorded duration/status.Build a real active OTel execution bridge in
@use-crux/otel:withTelemetry()now activates the SDK span around the actual instrumented callback (trace.getActiveSpan()resolves correctly inside real work, nested spans parent correctly), mapsrun:suspend/run:resumecorrectly instead of crashing the subscriber on those record types, and starts a fresh root span sharing the originaltraceIdon resume rather than reopening a stale one. Add W3Ctraceparent/tracestateinject/extract helpers and an explicitbaggageAttributeAllowlist(nothing copied by default).observe.flush()/observe.shutdown()now also force-flush the installed telemetry manager's exporter/processor work, bounded by the host lifecycle port above.Add one revisioned Go Runs read model (
/api/observability/runs/page,/api/observability/runs/delta) that joins observability and Quality server-side through an explicit correlation field, bumping a monotonic revision per affected run inside the same ingest transaction it publishes after commit. The page envelope is the sole Runs list HTTP surface; web DevTools, Global Search, and local TUI/CLI clients all consume it instead of a separate bare-array route. Fix a duplicate-recordIdconflict path that previously let a second, different payload overwrite immutable raw content, and fix a Quality/observability correlation bug that could key an unrelated run's feedback/score data onto the wrong run'straceId/runIdcollision.Move DevTools Runs and run detail onto that one read model: delete the client-side merge of a Quality-terminal row list and a separately-fetched observability-running row list, add truthful
suspended/incomplete/conflictedstatus andunknown/healthy/degradeddelivery-health rendering, and gate WebSocket invalidation on the published revision so a reconnect performs a bounded catch-up instead of an unconditional refetch or stale cache.Attach bounded
DefinitionRef[]evidence to observability records so every directly-observed Project Index definition kind joins directly to its canonical definition without stack or name guessing: prompt, context, tool, agent, flow, retriever, blackboard, and composition, plus routing (router/split/retry/cascade/fallback), skill, guardrail, constraint, task, workspace, memory, and the retrievalrag.recipe/rag.rerankerfamilies. A closedDirectlyObservedKind → DefinitionRefRolemap is checked against the coverage manifest at compile time, so a new directly-observed kind cannot ship without a canonical ref. Anonymous or non-authored spans omit the ref rather than guess. Composition APIs already require an authoredid; the random per-executioncompositionIdremains a separate execution identity.Extend that join to canonical runtime contributors and executed children: knowledge bases, tool policies, flow steps, parallel branches, recipe steps, and authored scorers now attach their own role-specific refs when the runtime genuinely holds their authored identity. Other structural children use explicitly parent-derived Catalog activity—“parent ran; this child is not independently observed”—while static-only kinds remain truthful zero-runtime states. No identity is inferred from a display name.
Make recipe reranking authored-id-required so
rag.rerankerruntime evidence is truthful and collision-free:rerank()now takes a required, namedengine(there is no anonymous default-model judge path), andjudgeReranker()requires aname. Build the engine withjudgeReranker({ name, model })or an adapterreranker()and pass it torerank({ engine }). The engine'snameis the canonicalrag.reranker:<name>identity.Project those references transactionally into a rebuildable local runtime-activity overlay and add revision-aware Runs filtering by canonical definition id. The overlay follows run deletion and retention, reuses the existing observability revision stream, and never stores a denormalized Project Index snapshot.
Normalize completed generation outcomes across
@use-crux/openai,@use-crux/anthropic,@use-crux/google, and@use-crux/aiinto closedCruxFinishReasonandCruxProviderError/CruxAdapterErrorshapes (kind/ namespacedcode/retryable/ optional redactedmessage). Stream completion failures surface instead of silent missing metadata; completed tool calls are assembled for bothgenerate()andstream(); progressive tool-call argument deltas are not exposed. Abort and budget timeouts classify asaborted/timeout.retryableis classification only — SDK clients keep their own network retries.Finish Catalog Observability sections and View Runs for every definition kind via the coverage manifest, add Run Detail → Catalog links for all
DefinitionRefs (including unresolved since-deleted ids), and share one delivery-health presentation (unknown/healthy/degraded) with plain-language status copy forsuspended/incomplete/conflicted. Quality signal capture follows nestedtriggeredchild runs so flow step matchers are not falsely uncaptured under aneval.casecell.Keep Catalog activity truthful for runtime-observed primitives that do not yet carry authored definition identity. Deferred work, media operations, and ingest sources remain visible in Runs and Run Detail, while their per-definition Catalog sections explicitly report that runtime evidence is not joined and never fabricate activity counts or View Runs links.
See ADR 0003 for the durable definition↔runtime join and adapter-outcome decision record.
-
Stabilize model routing around
router(),split(),retry(),fallback(), andcascade()wrappers with routing receipts, generate/stream support boundaries, and updated adapter docs.Breaking routing API changes: router
.with()and.select()are removed in favor of call-siteroutingandrouteoptions; variadicfallback(a, b, opts)is replaced byfallback([models], opts);_meta.router/_meta.cascade/_meta.fallbackare replaced byresult.routing; native OpenAI, Anthropic, Google, and Convex model options now type-reject routing wrappers instead of accepting unsupported values.Extend Project Index routing facts, static extraction, native semantic parity, relation policies, and index lints to cover split routes, retry targets, array-form fallback, call-profile model targets, and RouteArgs callback source refs.
Surface canonical routing receipts in local devtools run detail and Project Index views, including router defaults, split buckets, retry/fallback attempts, cascade budgets, and receipt-backed Turn Decision Report chips.
Project Index now shows required
RouteArgscontext types and literal route call-profile parameters. Run Detail renders receipt TTFT, bounded attempt errors, and cascade tier note/budget from the same canonicalrouting.reportpreview.Run Detail now accepts the canonical JSON-safe receipt when unavailable routing costs are serialized as
null, including nested retry, fallback, and cascade cost fields.OpenTelemetry span naming now covers all five canonical
routing.*primitives and no longer treats thefallback.attemptedge/name as a primitive. -
Promote
@use-crux/core/runtimeand its store-adapter contract to stable beta while Crux remains pre-1.0.Remove unused Runtime Engine dead port exports, validate
crux.flows.signal()against the durable flow snapshot before emitting, warn once when a durable target name is re-registered with a different definition, and make productioncreateRuntimeHandler()fail closed unless wake request verification is configured explicitly or supplied by the wake adapter.Embed delivered event payloads in runtime flow snapshots so flow replay no longer scans the event log after delivery. Store adapters must persist the
payloadfield passed tostate.markSnapshotDelivered().Add Runtime Engine retention config and bounded maintenance pruning for events, terminal work, terminal snapshots, confirmed outbox rows, idempotency keys, settled timers, and settled waiters. The memory, Postgres, and Convex runtime stores implement the new prune contract and conformance coverage.
Fence Runtime Engine wake commits with lease tokens and heartbeat leases while target code runs. Stale workers now exit cleanly with
LEASE_LOSTinstead of retrying, dead-lettering, or overwriting a reclaimed worker's result.Add named Runtime Engine composite commits and the optional store-adapter
runComposite(kind, input)override. Core keeps the defaulttransact()runner, and the Convex runtime component now routes composites through one mutation for atomic host-bound commits.Run the shared composite conformance cases against Convex's component-backed
runCompositepath, and encode composite Date payloads with Convex-valid object keys.Use Convex-compatible filenames for internal Runtime Engine component modules so Convex codegen and deployment can discover them.
Make
config()lifecycle-safe by installing one hook layer per active config, replacing the previous active config on repeat calls, and keeping independent layers such as imperative devtools intact when a config is disposed.Hard-rename the global hook-store API from the runtime family to the hooks family:
CruxHooks,getHooks(),setHooks(),updateHooks(),resetHooks(), andmergeHooks(). The old hook-store names are removed with no deprecated aliases so Runtime Engine terminology can stay unambiguous.Rename the Runtime Engine task target factory from
task()todurableTask()and remove the dead task output generic. Project Index runtime target discovery and lint guidance now recognizedurableTask()declarations. RemovecreateConvexRuntimeBridgefrom the public@use-crux/convexroot and package subpath; usecreateCruxConvex().run(),.storage(), and.bridge()as the single Convex entry point.Add
createTestRuntime()on@use-crux/core/runtime/testingfor app-level Runtime Engine tests. The harness installs an in-memory runtime hook layer, provides a controllable clock forflow.after()and suspend timeouts, and exposes boundedtick()/settle()helpers without real timers.Runtime Engine store adapters now honor the runtime clock when pending work is requeued, keeping
createTestRuntime()timers deterministic even when the injected clock differs from wall time.Bound Runtime Engine outbox dispatch to eight concurrent deliveries by default, add
RuntimeOutboxPort.listByWork()for targeted orphan recovery, remove shared-counter event append hot spots and the unusedruntimeCounterstable from the Convex runtime component, require namespaces for maintenance scans, and removeeval.runplus stale bridge manifest fields from the runtime bridge command contract. Orphan requeue now treats any same-namespace pending wake for the work id as live, even when its idempotency key was refreshed.Disable Runtime Engine lease heartbeat timers for Convex host bindings while preserving lease fencing, and make the default heartbeat scheduler a no-op when timer APIs are unavailable.
Remove unused exported idempotency helpers
flowSignalResumeKey()andwatchDeliverKey()from@use-crux/core/runtime. -
Adopt parsed Zod input throughout prompt resolution, run
sanitizebefore top-level auto-escape, warn when nested string input cannot be auto-escaped, and collect nestedwhen()/match()/contributor schemas consistently for prompt input validation.Split context resolver memoization from provider cache hints: dynamic contexts now use
memo: { ttl }for app-side memoization andcache: trueonly for provider cache breakpoints. Prompt definitions now reject duplicate statically reachable entry ids, context memoization requires a dynamicid-bearing context, and dynamic context callbacks receive only their declared input fields.Provider system adaptations now participate in the typed system block model. In prompt mode,
prependSystem/appendSystemproducesystemBlockswithsource: "adaptation:<key>"; prepend adaptations land after the stable cached prefix so provider cache boundaries stay byte-stable. Messages-mode prompts fold the final adapted system text intomessageswithout returning parallelsystemorsystemBlocksfields.Tool-name collisions now throw consistently across prompt-time tool sources, with both owners attributed. Skill, context, contributor, blackboard, and prompt-level tools must use unique names; call-site
generate()/stream()tools remain the intentional override path.compilePrompt().inspect()now runs with quiet observability, instrumentation, and diagnostics ports: it still executes the shared resolver pass and memo-cache path, but emits no spans, artifacts, diagnostics, or cache instrumentation events.Harden prompt authoring types for the stable beta surface:
messagesmode is now compile-time exclusive withsystem/prompt,when()wrapper predicates receive partial context input,match()branch input keys are included as optional merged input fields, concrete prompts preserve literalhasOutput, and heterogeneousAnyPromptConfigremains assignable from precise prompt configs.Consolidate custom prompt composition on
contributor(): remove the publicinjectable()factory and relatedInjectableConfig/InjectableEntry/PromptInjectionexports, renameContributorContributiontoContribution, renameAdapterMaptoProviderAdaptations, and keep primitive-family attribution internal to first-party factories.Add portable tool-loop controls to
GenerationSettings:toolChoice,stopWhen,maxSteps, and themaxSteps()/hasToolCall()helpers. Core-step adapters honor neutral stop conditions, OpenAI/Anthropic/AI SDK adapters map neutral tool choice to provider-native request fields, and provider-specific tool-control variants move to each adapter's typedextraoption.Make provider prompt caching a stable-prefix contract: cached context blocks now compose before the uncached tail, token budgets can drop only uncached blocks, prompt-level static system text can join the provider cache prefix,
SystemBlock.cacheBoundarymarks the single native breakpoint, andprompt.budgetartifacts reportprefixOverflowwhen the stable prefix exceeds the requested budget.Add freshness record metadata for resolved contexts: inspect parts and
context.contributionartifacts now report live-vs-memo provenance, original resolution time, memo-hit age, and optional segment source facts (observedAt/sourceVersion) when primitives such as retrievers already provide them.Tighten adapter parity and trust behavior: usage metadata no longer fabricates zero-token counts when providers omit usage, forged or mismatched tool approval responses become model-visible denial results instead of thrown errors, agent tool composition avoids prompt type widening, AI SDK call settings pass through to gateway calls, AI SDK structured streams report dropped tool observability through each request's diagnostics, and provider cache boundary conformance now runs across OpenAI, Anthropic, Google, and AI SDK adapters.
Add adapter contract harness scaffolding for the pre-launch API cleanup: target type assertions, a canonical result validator for shared conformance tests, canonical options fixtures, and headless equivalence TODO rows for the handle/transport phases.
Nest normalized generation usage details under
inputTokenDetailsandoutputTokenDetails, preserving no-fabricated-zero usage semantics while exposing cache-read/cache-write/reasoning token classes consistently. Add the portableGenerationSettings.reasoninghint and map it across AI SDK, Anthropic, OpenAI, and Google adapters.Replace generation
timeoutMswith structuredtimeoutbudgets:totalMsfor the whole managed call,stepMsfor provider attempts,chunkMsfor stalled streams, andtoolMs/tools[name]for tool execution. Crux timeouts now reject with typedTimeoutError, and loop-runtime step observers useonStepEnd.Return canonical native adapter result envelopes from
generate()andstream(): accumulatedtext, optional completeusage, optionalcost,steps,finalStep, provider-neutralmessages, typed.raw, and retained_metafor observability. Native streaming now returns{ textStream, raw, completion }, withcompletionresolving to the same canonical envelope fields.Bring
@use-crux/aiinto the same adapter contract: portable AI SDK call settings now use canonical Crux fields, SDK-native request controls live underextra,generate()andstream()return canonical envelopes with typed.raw, and stateless UI-message helpers bridge canonical stream results to AI SDKuseChatresponses.Move tool approval policy to composition layers: declare
toolApprovalon contexts, prompts, or call options; tool definitions no longer carry approval policy. Approval resolution now preserves exact-over-wildcard precedence, call-site/prompt/context provenance in inspect output, and the existing suspend/resume token hardening across native and AI SDK adapters.Document the
@use-crux/corestable beta contract inpackages/core/STABILITY.md, add the0.4.0-beta.0changelog entry, and align prompt/context/adapter docs with the stabilized composition, caching, freshness, and tool-control surfaces.Add typed per-tool execution context: tools can declare
contextSchema, callers supply validatedtoolsContextkeyed by tool name, and sharedruntimeContextnow threads through tool execution, middleware, and approval-policy callbacks across core and AI adapter calls.Expose public adapter codecs and the first headless call handle: all adapters now export
toParams()/fromResponse()translation helpers, and@use-crux/anthropicsupportsprepare()/step()/finish()over the same executor path as managedgenerate().Complete the headless ladder across first-party generation adapters: Anthropic, OpenAI, Google, and AI SDK expose
prepare()handles,generate()accepts typedtransportcallbacks, managed/handle/transport equivalence is covered by adapter tests, andstream()withtransportnow fails explicitly withCruxTransportStreamUnsupportedError.Raise all Crux workspace packages to a Node.js 22 ESM-only contract: package exports no longer include CommonJS
requireconditions, and local skill file loading moved from@use-crux/core/skillto the explicit Node-only@use-crux/core/skill/nodesubpath. Replaceimport { skill } from '@use-crux/core/skill'withimport { skill } from '@use-crux/core/skill/node'anywhere you callskill.fromFile().Keep quality signal capture independent from a previously configured observability transport: evaluation cells still capture trace records synchronously, but a stale or hanging forwarding transport no longer stalls quality runs or inflates cell durations.
Introduce the canonical multimodal message vocabulary:
Message.contentnow accepts strings or readonlyContentPart[],ToolModelOutputrich content usesContentPart[], and the oldToolContentPart/mediapart surface is removed. Core now exposes the finaltext | image | filecontent union,MediaSource,textPart,contentText,messageText, andhasMediaParts; media parts put bytes, URLs,Asset, orBlobvalues directly onsource.Make native transcript codecs content-aware beneath the existing public
toParams()/fromResponse()ladder. Anthropic now encodes canonical image and PDF content through one exhaustive part table for messages and tool results, decodes assistant media back into canonical message content, and rejects unsupported media before provider I/O.Retire the AI SDK adapter's blind content-array passthrough for recognizable parts: canonical multimodal messages now encode to AI SDK text/image/file parts, native AI SDK
ModelMessage[]fromconvertToModelMessages()reaches the AI SDK loop without lossy Crux media conversion, and SDK control parts move intoMessage.metadatafor canonical result history. The pre-launch v6mediacompatibility decoder is removed.Add native multimodal part tables for OpenAI and Google. OpenAI chat now encodes image, audio, and file data through provider content parts, decodes assistant/user media back into canonical content, and no longer flattens rich tool results with raw base64 text. Google now encodes inline and URI-backed media through
inlineData/fileData, decodes assistant inline media into canonical content, and requiresmediaTypebefore sending URI-backed parts natively.Route guardrails, compaction, memory capture, semantic cache keys, resolver system folding, Convex memory persistence, and OTel message-content export through the canonical
messageText()projection. Multimodal message previews now use bounded placeholders instead of[object Object]or raw base64, and@use-crux/convexre-exports the core multimodal content helpers.Document the multimodal message layer across the core README, core architecture notes, docs guide, core reference, adapter references, and Convex reference, including the adapter capability matrix and strict/degrade behavior.
Close multimodal adapter edge cases found during review: AI SDK tool and assistant transcripts now preserve rich content instead of dropping it, Anthropic strict mode applies to tool-result content, OpenAI degrades unsupported audio formats, and tool-result text fallbacks use bounded placeholders rather than raw base64.
Harden multimodal follow-up edges: AI SDK malformed media parts now warn before being dropped, encode-path unknown parts use request diagnostics, response tool results preserve structured content arrays, large media text descriptors avoid full payload hashing, capture policy redacts reference-only artifacts in off mode, inline data URLs sanitize consistently, and OTel message-content fallbacks continue past empty structured content.
Introduce the public Asset/AssetStore foundation for multimodal persistence: data, URL, and provider-file assets now share one discriminated union,
inMemoryAssetStore()provides explicit local persistence, and storage bundles can carry an optionalassetscapability.Migrate workspace persistence from the removed byte-store surface to
AssetStore:Storagenow usesassets, workspaces persist oversized/binary content as data assets, Convex exposesconvexAssetStore()/ConvexAssetStoreConfig, Project Index/devtools storage facts usestorage.assetStoreplususes_asset_storerelations, local storage warnings use asset vocabulary, and Project Index cache identities are bumped for the new read model.Add safe media-boundary error contracts for the multimodal input pipeline:
InvalidMediaSourceErrorreports malformed media source values before provider I/O, andUnsupportedCapabilityErroraggregates adapter/model capability failures with safe message paths and remediation.Activate the final direct message input grammar across core and first-party consumers.
ContentPartis now the readonlytext | image | fileunion with media onsource; removed helper factories, source-specific variants, degradation settings, and the competing content error no longer ship. Sliding-window persistence now accepts oneStoragebundle and saves media as asset refs before its message record.Lower OpenAI chat media at the shared per-turn request boundary. Managed generation, streaming, call handles, custom transports, and later tool turns now receive equivalent native image/file payloads; OpenAI image detail, MIME, filenames, and provider file IDs are preserved. Known unsupported model/media combinations fail with safe exact paths before any provider request.
Harden the multimodal boundary before release: data URLs are decoded with bounded allocation, Blob-backed assets remain directly usable, corrective generation repeats the same per-call normalization, OpenAI provider-file audio stays in native file-ID form, ordinary observability emits only private media descriptors, persisted media records are semantically validated, and sliding-window summary plus messages commit atomically.
Complete direct media input parity for Anthropic and Google language calls. Anthropic now lowers URL/data image and PDF input, preserves supported media cache-control options, and rejects unsupported stable-SDK file IDs before I/O; Google now lowers URL/data/provider-file media to
fileData/inlineData, preserves MIME, filenames, and media-resolution options, and reports selected-model media failures before any provider request.Preserve Convex Agent as the media lifecycle owner for
@use-crux/convex: profile-backed Agent calls now pass native AI SDK image/file parts to Convex Agent, reuse stored Convex file URLs without duplicate Crux asset writes, defer inline media autosave to the installed Agent threshold, and redact media URLs/storage identifiers from memory and observability previews.Validate canonical media through provider-owned, side-effect-free hooks after prompt resolution and before native I/O. First-party direct adapters, AI SDK, and Convex Agent now share semantic conformance cases while keeping model support knowledge private to each integration; omitted extensions and capability discovery remain absent from public runtime types and records.
Sanitize multimodal observability recursively before serialization. Message, tool, result, and error capture now retain only bounded media descriptors with safe scalar facts; raw bytes, Blob contents, base64/data URLs, filenames, bearer references, provider file IDs, and signed URL details cannot reach local transports or OTel message-content attributes.
Apply the same descriptor allowlist defensively when Crux Local ingests old or untrusted artifact previews, before general retention caps. Devtools recognize retained image/file descriptors and render compact accessible fact labels instead of expandable JSON or raw media viewers.
Complete OpenAI's native stateless media surface: speech generation now returns immediately usable audio bytes through the shared bounded-operation lifecycle, and supported English audio translation routes to the native translations endpoint. Unsupported speech and transcription controls still fail before provider I/O.
Add native Google speech generation with typed single- and multi-speaker voices. The operation returns the provider's audio bytes through the shared bounded lifecycle and rejects portable controls Google cannot honor without prompt emulation.
Expose AI SDK speech as a direct unbound operation alongside image generation and transcription. Custom speech models continue through AI SDK-owned dispatch while Crux preserves native warnings, response metadata, provider metadata, and immediately usable audio bytes.
Re-export AI SDK image, transcription, and speech operations by exact identity from Convex while leaving the Convex Agent loop, file autosave, thread persistence, and useChat lifecycle unchanged.
Finalize Anthropic's stable-SDK multimodal boundary: image and PDF input plus rich tool results remain native, audio/video and provider file IDs fail before I/O, and image generation, transcription, and speech remain structurally absent.
Harden completed-operation option ownership: OpenAI completed speech excludes SSE and translation forwards only translation-native fields; Google keeps Imagen and Gemini extras isolated, defers unknown model IDs to native validation, and requires provider audio MIME; AI SDK speech prevents native extras from shadowing portable fields.
Make OpenAI transcription and translation extra namespaces mutually exclusive and task-aware at runtime, so an inactive endpoint namespace always fails before provider I/O.
Account for media in internal message budgeting and compaction metrics without changing the public synchronous text tokenizer. Direct adapters estimate from provider/model identity and already-known asset facts only; unknown media uses a deterministic conservative fallback, invalid provider estimates fail before I/O, and reported provider usage remains authoritative.
Apply the same private media budgeting path to AI SDK generation and streaming from stable provider/model identity, with a safe fallback reason for custom identities and no direct-adapter dependency. Convex Agent continues to own its native loop and usage accounting without a second rule set or extra model/storage action.
Define the flat provider-neutral image generation contract: typed Crux prompts lower through the shared resolver, language-only features fail together before provider I/O, native successes normalize to immediately usable ordered data assets, and malformed or empty successes receive a tagged no-image error.
Add
openai.generateImage()as one native OpenAI Images API operation, with typed OpenAI controls, native edit support for byte assets, ordered data-asset results, raw response preservation, and unchanged transport errors.Expose stateless AI SDK image generation through the injectable gateway, bound
CruxAi, provider runtime extension, and package-levelgenerateImage(), preserving native files, warnings, usage, response metadata, provider metadata, and the raw result without entering the language loop.Add
google.generateImage()through the native Google GenAIgenerateImages()surface, with package-local model/control support declarations, safe preflight rejection, ordered byte results, safety warnings, request metadata, and unchanged provider errors.Map Imagen data-asset references and portable masks to Google’s native
editImageendpoint, expose collision-freeextra.editcontrols, and reject unsupported Gemini masks before provider I/O.Complete five-adapter image-operation parity: Convex Agent exactly re-exports the AI SDK function without Agent or storage behavior, Anthropic omits the operation structurally, and tested internal support fixture data projects the OpenAI, AI SDK, Google, Convex, and Anthropic bindings into adapter docs.
Make conversation compaction media-aware by describing each media part through the configured native generation path before summarizing an ephemeral text-only copy.
GenerateTextFnnow accepts either a prompt or canonical messages plus an output bound, and core sliding windows and Convex compaction share the same optional media controls.Define flat provider-neutral transcription contracts with always-present seconds-based segments, tagged semantic-empty failures, storage-free source normalization, and strict result validation. Node adapters share an explicit
@use-crux/core/transcription/nodeHTTPS downloader with bounded streaming, redirect and DNS safety, and pinned validated resolutions, while root and neutral transcription entrypoints remain isolate-safe.Replace ingest OCR hooks with a readonly application-owned media operations port. File and URL sources now detect common image formats, image files derive ordinary text through one bound generation call, visual-only PDF pages use the same operation with one-page instructions while native text pages remain model-free, and explicitly identified Assets can enter the existing file source pipeline without provider dependencies.
Add
openai.transcribe()as one bound native Audio API operation. Crux reuses its storage-free source normalization and secure downloader, maps portable language/prompt controls plus typed OpenAI extras, validates common text and seconds-based segments, warns when timing is absent, and preserves native results and transport failures.Expose stateless AI SDK transcription through the injectable gateway, provider runtime extensions, bound
CruxAi, and package-leveltranscribe(). URL audio always receives Crux's bounded secure downloader, unsupported common mappings fail before I/O, and native segments, warnings, response metadata, provider metadata, raw results, and errors remain intact.Complete five-adapter transcription parity: Google uses one composed
generateContent()call with audio and a fixed transcript-only instruction, returns empty timing arrays, and rejects timestamp requests rather than presenting model-invented timing; Convex Agent exactly re-exports the AI SDK operation; Anthropic remains structurally absent; and an internal all-five fixture locks native, composed, delegated, and absent support expectations.Derive ingest documents from MP3, WAV, M4A, OGG, FLAC, and WebM audio through the existing media operations port. Each source invokes transcription once, emits ordinary text parts with explicit seconds locations when timing is valid, preserves only safe language/duration/warnings and StoredAsset refs, strips signed URL data, and carries time provenance into core indexing without retaining audio or provider payloads.
Complete explicit media derivation in ingest: rename the application-bound semantic operation from
generatetodescribewith no alias, migrate transcription to the final interval contract, and derive video from caller-supplied visual description, soundtrack transcription, or both. Video evidence records its exact derivation mode, never samples frames implicitly, and preserves soundtrack seconds through indexing and retrieval.Extend the existing Quality judge to
JudgeContent(string | Asset | readonly ContentPart[]) while keeping structured outputs selector-driven. Media reaches the bound judge as normal canonical message content, binary outputs are never written implicitly to cassettes or output caches, and media-aware cassette/output identity epochs invalidate stale entries.Preserve streamed text in canonical completion content when a provider reports an empty content array, so text, steps, messages, and final-step projections remain lossless.
Harden Quality media persistence: cell, experiment, and failure snapshots project media to safe descriptors; unknowable Blob identity disables synchronous cache reuse; and both generation and whole-value cassette paths refuse implicit binary or locator-bearing media.
Preserve allowlisted media source facts through ingest and indexing. Documents and chunks can retain safe HTTPS/path/AssetRef/media-type facts plus validated page or seconds locations, while vector metadata remains source-detail-free and source lifecycle identity remains
sourceId.Replace the pre-v1 flat retrieval-hit
sourceId/sourceUrl/sourcePathfields with readonlysource: { id, url?, path?, assetRef?, mediaType?, location? }. Indexed retrieval, custom retrievers, recipes, rerankers, tools, citations, Quality signals, workspaces, Convex, observability, and devtools now use one structured attribution value without media hydration on retrieval paths.Publish the final progressive multimodal guide and fixture-generated five-adapter matrix for chat media, image generation, transcription, explicit AssetStore persistence, media ingest, and attributed retrieval. The public story keeps provider/model support checks adapter-owned and exposes no runtime capability registry or automatic persistence path.
Make mixed generation output lossless:
GenerateResult.contentis now the authoritative ordered assistant output,textis its text-only projection,stepsexposes ordered step facts, and stream completion buffers exact media, reasoning, tool-call, warning, metadata, and message content without adding live media delta events.Unify bounded media operation contracts before provider migration: image generation, transcription, and speech now share required warnings, provider metadata, native/composed execution facts, raw results, cancellation, and total/step timeout vocabulary. Image edits enforce reference-mask and size/aspect-ratio laws, transcription exposes honest segment/word/speaker intervals, generated URLs remain usable without hidden downloads, and the new speech seam returns one explicit data asset.
Run bounded media operations through one immutable functional lifecycle for normalization, private support preflight, native invocation, result validation, and descriptor-only reporting. Known unsupported routed candidates fail before provider I/O, unknown models reach native validation, total and per-attempt deadlines compose with cancellation, retry/fallback/router/split preserve call counts and original failures, and completed operations remain outside the language/tool loop.
Index authored media operations and ingest sources through backend-neutral Project Index contracts. Static TypeScript extraction records only proven modalities and allowlisted authored options, preserves named and nested operation structure plus compiler-owned relations, and excludes prompts, locators, references, filenames, provider identifiers, and arbitrary provider options.
Match those authored media facts exactly in the Rust/Oxc static frontend, including nested ownership and ingest relations. Static cache epoch
static-parse-v61, Oxc projection identitycrux_native_group3.8, and native primitive manifest v11 prevent pre-media output from being reused.Resolve authored media operations through backend-neutral semantic evidence, including imported and local aliases, routing relations, deterministic misuse findings, and discarded outputs. The TypeScript backend now consumes the shared source profile without rereading local closure files, and semantic cache epoch
semantic-facts-v27prevents pre-media facts from being reused.Match authored media evidence exactly in the TypeScript-Go backend through the complete shared analyzer. Native backend identity
tsgo-native-preview-v2and runtime identitynative-preview-v2prevent cached pre-media native evidence from being reused.Expose all seven deterministic media lint contracts and preserve safe authored media facts through shared events and the Go Project Index read model. Semantic cache epoch
semantic-facts-v27, authored media manifest v2, native backend/runtime v3, and Go snapshot epoch 33 prevent stale pre-lint projections after restart.Correct Project Index media extraction to follow public
generate(prompt, options)andstream(prompt, options)calls, derive adapter identity only from resolved package/binding provenance, preserve prompt/model-routing relations, and index transcriptiontask. Static TS/Rust and semantic TS/native backends now prove exact parity from real package-resolved fixtures; native primitive manifest v11 and defer compiler projection v2 invalidate their structured compiler identities.Normalize authored transcription tasks in Project Index and Catalog to
task: 'transcribe' | 'translate', including object-form translation requests, without retaining target language or media locators. Bump the Go Project Index snapshot cache to epoch 33.Migrate OpenAI, Google, and AI SDK image generation and transcription onto that shared lifecycle without changing their native endpoint ownership. Specialized results now expose the common warnings, provider metadata, execution, and raw tail; provider errors remain unchanged, Convex keeps exact AI SDK re-exports, Anthropic keeps structural omission, and adapter authors can bind future speech definitions without adding persistence or a second loop.
Preserve provider-native multimodal continuation without leaking opaque state into text or observability: Anthropic redacted thinking, Google thought signatures and assistant media, and OpenAI generated-audio IDs now survive tool loops. OpenAI audio output supports WAV, AAC, MP3, FLAC, Opus, and PCM16 with honest MIME types; AI SDK output accepts every documented media data shape; safety-transformed stream text retains the provider's mixed-content slot ordering.
Instrument completed media operations with the closed media observability vocabulary (
media.generate_image,media.transcribe,media.generate_speech,media.describe), allowlistedmedia.reportartifacts, and canonicalderived.fromlineage. The shared completed-operation runner emits exact provider/model/execution/call facts, nests composed children such asgeneration.call, and never retains raw media under any capture mode. Safe descriptorsourceCategoryis the closed uniondata | url | provider-file | asset-ref | bytes | blob | unknownacross core, OTel, local retention, and Devtools Runs (data URLs project asdata; arbitrary tokens becomeunknown). Ingest describe/transcribe derivation links under those primitives; OTel maps media spans to documentedgen_ai.operation.namevalues (generate_image,transcribe,generate_speech,generate_content) with production text off by default. Devtools Catalog and Runs gain purpose-built media projections, filters, descriptor cards, attempt/composition timelines with provider/model, transcript timelines, complete input→operation→output→ingest/index/retrieval lineage with relationship edges and page/time attribution, and Catalog joins without thumbnails, players, locators, or raw media.Publish the complete progressive v1 documentation and fixture-checked five-adapter matrix for every message modality, mixed assistant media, image generation, transcription, and speech. Adapter references and package READMEs now state exact native/composed/structurally absent boundaries, explicit persistence, safe observability, Project Index linting, and framework-owned AI SDK/Convex behavior without adding a runtime capability API.
Restore Crux Local startup Runtime preflight by validating the complete operation vocabulary in the embedded worker, and move cross-plane injection-state and judge-report presentation into shared Devtools modules so feature dependency boundaries remain enforceable.
Harden final media privacy edges: Devtools Catalog join labels never derive from
definitionId(including suffix stripping) and fall back to a fixed generic label when no safe display name is recorded; local retention reconstructs audio/video descriptors with the same allowlist andsourceCategorynormalization as image/file; Convex Agent message previews emit canonical descriptor-shaped media facts (kind+sourceCategory) so data URLs staydataand never re-enter Core as fakeurlmarkers. -
Add request-scoped
defer(callback)with bounded host-lifetime execution, the
explicit@use-crux/core/defer/nodeHTTP integration, and Runtime-backed named
target staging throughawait defer(target, input). Postgres and Convex Runtime
stores now persist named deferred intents and recover their release through the
existing transactional outbox. Publicdefer()is rejected during replayable
flow execution, and the Runtime snapshot field for replay-visible child work is
hard-renamed fromscheduledEffectstoscheduledWorkwith no compatibility
field or read path.
Inline callbacks now isolate nested named commits per callback and report late
commit failures without stopping sibling cleanup.
Project Index now discovers public inline and named scheduling sites as stable
deferred-workdefinitions, resolves their task and enclosing-definition
relations, and reports replay-unsafe, floating-promise, missing-scope, and
explicitly missing-Runtime diagnostics.
Public deferred work emitsdefer.scheduledanddefer.runobservability
spans with causaltriggerededges, one lightweight grouped run when no
originating Crux run exists, and quiet diagnostics-only internal composition.
Inline retained tasks flush only after their full graph closes. Named wakes use
fresh same-trace runs and segments with a causal edge, then flush only after the
durable outcome commits.
Devtools Catalog and Runs surface deferred-work kinds, lifecycle states, and
honest handler-returned streaming notes.
Provider-neutral serverless hosts live at@use-crux/core/defer/serverless
(injectedwaitUntil/after/ named-only).@use-crux/nextbinds Next.js
after()as response-finished. Convex bridge runs install a named-only lifetime
so inline callbacks fail withDEFER_CAPABILITY_MISSINGwhile named Runtime work
remains supported.
Docs cover host reliability boundaries, completion classes, strict named commit,
at-least-once edges, cancellation limits, and the distinction from
flow.defer()/ future Effects.The defer setup contributor now participates in
crux setup, reporting host
integration and named Runtime durability readiness without redefining the
shared@use-crux/core/setupcontract.
Deferred intent stores now preserve the first terminal state across memory,
Postgres, and Convex, and setup distinguishes inline host wrapping from literal
named-workdurableFinalization: truecapability. -
Stabilize the Quality beta API and experiment record contract:
ctx.score()becomesctx.recordScore(), post-score callbacks are nowafterScores, retrieval recipe targets usetarget.retrievalRecipe(), decision-report assertions use the singulardecisionReportnamespace, and experiment records now write schema-version 2cellswith ordered assertionoutcomesonly.Harden Quality determinism by adding explicit cache identity epochs, including structured-output schemas and tool parameter schemas in cassette keys, including case input, prompt, params, dataset content, and scorer identity in output-cache/baseline fingerprints, failing loudly on corrupt committed baselines, and rejecting invalid scorer values instead of aggregating them.
Make Quality artifact writes atomic and safer under concurrent runs: cassette recording now single-flights duplicate misses, flushes merge with on-disk entries under a lock, timed-out cells quarantine late trace/cassette writes, and local read-model records write via temp-file rename.
Harden judge-backed scoring by pinning judge generation settings, framing untrusted output/reference/context in judge prompts, stamping judge provenance on score metadata and baseline identity, and adding human-label plus judge-report tooling for judge-vs-human agreement.
Structured judge inputs are serialized before prompt framing, so Safety's judge-backed constraints can evaluate object-shaped outputs without losing fields or throwing during escaping.
Harden the Quality local pipeline by making
crux quality run --jsonemit a single run summary object, adding worker/core protocol version checks and run-scoped event ids, surfacing worker crashes as structured exit-2 failures, fixing live read-model collisions and source-frame path containment, reporting skipped legacy records, and requiring an explicit devtools promotion variant.Quality observability capture now uses an observability-owned hook registry, preserving run-scoped trace capture without importing Node-only Quality internals into platform-neutral runtime bundles.
Add the Quality machine contract surface:
@use-crux/core/quality/schemasexports validation schemas and JSON Schema generation for records and CLI JSON, experiment records embed agent-readablefailuresartifacts, andcrux quality diff <expA> <expB> --jsoncompares saved experiment records through core-owned diff policy.Add the Quality adoption path:
crux quality initscaffolds first evals for uncovered prompt definitions,crux quality import-tracesconverts retained local traces into JSONL dataset rows, and dataset-backed failures now surface dataset path plus content fingerprint in persisted records, run summaries, failure artifacts, and experiment diffs.Add the Quality agent loop:
crux quality runnow supports--failed, deterministic--sample/--seed,--max-cost, and--changed-sincesubsets,crux quality mcpexposes list/run/show/diff/evidence/judge-report/label tools over MCP, andcrux quality initscaffolds a local Quality skill for coding agents.Declare Quality beta: the authoring surface, experiment/manifest schemas, CLI JSON outputs, and exit codes are stable within 0.x minors; future breaking changes require a minor bump and migration note while the first-party runner facade remains internal.
Finish Quality Project Index parity for beta: native static extraction now treats
afterScoresassertions as evaluation-levelctx.expect()sites, evaluation definitions expose catalog facts for devtools chips, spec experiment records enrichevaluation:*and covered definitions in the local read model, andcrux quality initdiscovers uncovered targets from Project Model evidence before falling back to config exports.Complete Quality observability coverage by removing the unused
quality.snapshotartifact kind, emittingbaseline.promotionartifacts on successful promotion, emitting diff-modecomparison.reportartifacts from core experiment diffs, and forwardingquality diffevents into local activity.Make the Quality machine contract operable from the UI: devtools serves
GET /api/quality/judge-report/{evaluationId}andGET /api/quality/experiments/diff?a=&b=(the latter runs the core diff op in a worker), the experiment read model surfaces core-ownedfailuresartifacts, and the workbench renders fix-surface chips that deep-link to the covered definition, a Failure Artifact panel with dataset provenance and cassette id, Pass/Fail cell labeling, a judge-trust panel (agreement, confusion matrix, kappa, disagreements), and an experiment Compare picker with a per-score/per-case diff and a drift banner. The TUI shows fix-surface letters on failing cells, the dataset fingerprint, and a CLI hint for the judge-report and diff views. -
Promote Safety to its stable beta boundary model. Guardrails and constraints now author through
{ id, on, run }with typedboundary.*targets, duplicate policy ids fail fast,safety.tunecontrols per-call posture, structured-output rewrites keep returned text/object synchronized, and Safety audit/error/observability records are safe-by-default.Streaming guardrails now protect ordinary output streams by default through sentence-gated checks, explicit final/disabled modes, bounded hold behavior, and the AI SDK stream bridge preserves policy-terminal completion errors without unhandled rejections.
Add the provider-agnostic Safety strategy pack (
guardrail.pii,guardrail.secrets,guardrail.injection,guardrail.classifier,constraint.judge,constraint.citations, andtoolPolicy.*), Project Index Safety facts/lints, Devtools Safety intervention surfacing, and updated docs with migration notes plus beta roadmap RFC links. -
Add the provider-neutral
@use-crux/core/setupcontributor and planner contract
and the aggregatecrux setupcheck/apply/JSON workflow. Runtime setup now
participates as a contributor through that single setup command.
Unhealthy reports exit nonzero, contributor failures remain isolated and
privacy-safe, and apply reports retain planning and adapter-reported failures. -
Add runtime-backed
workspace.watch()subscriptions for durable create, update, delete, and rename events, including cursor polling, unsubscribe, and transaction-aware event emission.Classify
workspace.watch()as read-style Project Index data access and bump local/indexer cache identities so existing snapshots do not mask the new facts.Update local devtools workspace read models so deleted files are removed from the tree and observed rename/move operations update the visible destination path.
Render
watchas a recognized read-style Project Index data-access operation in the devtools intelligence panel. -
Harden indexer untrusted-input handling: source-only static syntax planning no longer imports project config, extension loading verifies resolved package identity and containment before import, and source-map disk reads are contained to the project root.
Preserve semantic source-profile completeness across worker streams so incomplete profiles are not reused through the semantic facts cache, and split Project Index worker transport limits so multi-line fact/artifact streams can exceed the per-line cap without failing.
Harden Project Index correctness and determinism across patch merging, native/static syntax parity, record-lane extraction, stale provided-record handling, extension diagnostics, and rejected cache reads so cached, incremental, and native-backed runs preserve the same read-model facts.
Improve Project Index watch latency and live updates with lower save-path debounce windows, exported-interface source hashes that stop body-only edits from cascading to dependents, per-file WebSocket index deltas, cancellable background semantic waves, and watch-run fallback/status telemetry in local devtools. Source rows now include source and interface hash evidence so incremental planning can safely preserve single-file leaf edits across restarts.
Freeze the indexer stable-beta public surface:
ExtractContextand extension manifest authoring types now have type-level guards, root syntax-record projection options no longer expose host-only worker controls, host static-index helpers carry those controls through explicitForHostAPIs, runtimeusetarget matching is data-driven, and docs now mark root/testing/source-resolver/contracts as stable-beta surfaces while keeping extensions experimental and host subpaths Crux-owned.Finish stable-beta indexer housekeeping: bump the static, semantic, and Go Project Index cache epochs, store Go snapshot fact caches under epoch-specific directories, align Crux Indexer and Project Index terminology/config docs, and promote the documented stable-beta Index Lint rule set while keeping the remaining rules preview.
Improve the Rust/Oxc static syntax frontend with
oxc_semantic-backed scope visibility so match-local initializer resolution respects declaration order and nested shadowing instead of leaking later same-scope bindings. Import-qualified call interests now also resolve through Oxc symbol references so local shadowing cannot be misclassified as a first-party import call. Function return records now resolve direct identifier returns through Oxc binding evidence at record-production time, and the static parse cache epoch is bumped tostatic-parse-v53for the output change.Add first-party static golden checks so Rust/Oxc output is compared against the captured Rust-owned golden, and scale the local Rust/Oxc static-index worker pool default to
GOMAXPROCSwhile preservingCRUX_STATIC_INDEX_WORKER_POOL_SIZEas the explicit tuning override.Retire the legacy TypeScript first-party static parser, bundled extractors, bundled lint evaluator, and root in-process project indexing APIs.
@use-crux/indexeris now the extension-authoring SDK plus Project Index record contracts; bundled first-party extraction and linting are Rust-only binary features owned by Crux Local and the CLI. The TypeScript host remains only for third-party extension contributions and host/config/semantic support lanes.Remove the monolithic
compileProjectIndexhost pipeline and syntax-record patch RPC from the TypeScript worker path. Runtime artifact generation now projects supplied native Project Index definitions instead of parsing source in-process, and no-config Quality prompt-test collection no longer falls back to TypeScript source projection.Make root/host
createStaticExtraction()require an explicit syntax frontend, while keeping the TypeScript syntax-record producer as the documented/testingfixture default. Fixture traces now report the syntax producer identity used for the run.Ship Crux Local platform packages with an enforced Rust/Oxc static-index worker sibling: staged release validation now rejects platform tarballs that omit
bin/crux-static-index-workeror carry mismatchedos/cpumetadata. The staged@use-crux/localmanifest injects platform packages as optional dependencies, while committed workspace manifests are guarded against platform optional dependencies so workspace and Karyla installs stay local-path based.Run semantic enrichment as shard-local work when the Project Index source graph proves complete shard and dependency evidence. Crux Local now fans semantic requests across a lazy worker pool and merges shard semantic patches without invalidating AST/source facts.
Move Crux Local incremental watch reindexing onto the production Go to Rust/Oxc Static Index compiler path and remove the TypeScript bundled fallback path. The production watch benchmark now enforces the Tier-A leaf p95 budget on the shipping path.
Keep source/interface hash evidence consistent across TypeScript and Rust/Oxc static records, including constructor signatures, and bump the static parse cache epoch to refresh stale source-row cache entries.
Tighten Project Index transport contracts by keeping WebSocket index messages and Rust source snippets on typed payload shapes instead of untyped JSON values.
Prefer the packaged Rust/Oxc static-index worker for
/testingfixture extraction when the matching platform package is resolvable, and fully flip the native AST gate to Rust-vs-golden validation with no TypeScript bundled baseline comparison.Update Crux Local native build baselines to Go 1.26.4 and Rust 1.96.1, and refresh the Rust/Oxc static syntax frontend dependency identity to Oxc 0.139.
Align native and semantic definition identity with runtime observability joins: composition definitions now require and use their authored
id, canonical ids remain byte-identical to the runtimeDefinitionRefbuilders, and missing composition ids no longer fall back to a misleading local variable name.
Fixes
-
Promote Safety to its stable beta boundary model. Guardrails and constraints now author through
{ id, on, run }with typedboundary.*targets, duplicate policy ids fail fast,safety.tunecontrols per-call posture, structured-output rewrites keep returned text/object synchronized, and Safety audit/error/observability records are safe-by-default.Streaming guardrails now protect ordinary output streams by default through sentence-gated checks, explicit final/disabled modes, bounded hold behavior, and the AI SDK stream bridge preserves policy-terminal completion errors without unhandled rejections.
Add the provider-agnostic Safety strategy pack (
guardrail.pii,guardrail.secrets,guardrail.injection,guardrail.classifier,constraint.judge,constraint.citations, andtoolPolicy.*), Project Index Safety facts/lints, Devtools Safety intervention surfacing, and updated docs with migration notes plus beta roadmap RFC links. -
Harden indexer untrusted-input handling: source-only static syntax planning no longer imports project config, extension loading verifies resolved package identity and containment before import, and source-map disk reads are contained to the project root.
Preserve semantic source-profile completeness across worker streams so incomplete profiles are not reused through the semantic facts cache, and split Project Index worker transport limits so multi-line fact/artifact streams can exceed the per-line cap without failing.
Harden Project Index correctness and determinism across patch merging, native/static syntax parity, record-lane extraction, stale provided-record handling, extension diagnostics, and rejected cache reads so cached, incremental, and native-backed runs preserve the same read-model facts.
Improve Project Index watch latency and live updates with lower save-path debounce windows, exported-interface source hashes that stop body-only edits from cascading to dependents, per-file WebSocket index deltas, cancellable background semantic waves, and watch-run fallback/status telemetry in local devtools. Source rows now include source and interface hash evidence so incremental planning can safely preserve single-file leaf edits across restarts.
Freeze the indexer stable-beta public surface:
ExtractContextand extension manifest authoring types now have type-level guards, root syntax-record projection options no longer expose host-only worker controls, host static-index helpers carry those controls through explicitForHostAPIs, runtimeusetarget matching is data-driven, and docs now mark root/testing/source-resolver/contracts as stable-beta surfaces while keeping extensions experimental and host subpaths Crux-owned.Finish stable-beta indexer housekeeping: bump the static, semantic, and Go Project Index cache epochs, store Go snapshot fact caches under epoch-specific directories, align Crux Indexer and Project Index terminology/config docs, and promote the documented stable-beta Index Lint rule set while keeping the remaining rules preview.
Improve the Rust/Oxc static syntax frontend with
oxc_semantic-backed scope visibility so match-local initializer resolution respects declaration order and nested shadowing instead of leaking later same-scope bindings. Import-qualified call interests now also resolve through Oxc symbol references so local shadowing cannot be misclassified as a first-party import call. Function return records now resolve direct identifier returns through Oxc binding evidence at record-production time, and the static parse cache epoch is bumped tostatic-parse-v53for the output change.Add first-party static golden checks so Rust/Oxc output is compared against the captured Rust-owned golden, and scale the local Rust/Oxc static-index worker pool default to
GOMAXPROCSwhile preservingCRUX_STATIC_INDEX_WORKER_POOL_SIZEas the explicit tuning override.Retire the legacy TypeScript first-party static parser, bundled extractors, bundled lint evaluator, and root in-process project indexing APIs.
@use-crux/indexeris now the extension-authoring SDK plus Project Index record contracts; bundled first-party extraction and linting are Rust-only binary features owned by Crux Local and the CLI. The TypeScript host remains only for third-party extension contributions and host/config/semantic support lanes.Remove the monolithic
compileProjectIndexhost pipeline and syntax-record patch RPC from the TypeScript worker path. Runtime artifact generation now projects supplied native Project Index definitions instead of parsing source in-process, and no-config Quality prompt-test collection no longer falls back to TypeScript source projection.Make root/host
createStaticExtraction()require an explicit syntax frontend, while keeping the TypeScript syntax-record producer as the documented/testingfixture default. Fixture traces now report the syntax producer identity used for the run.Ship Crux Local platform packages with an enforced Rust/Oxc static-index worker sibling: staged release validation now rejects platform tarballs that omit
bin/crux-static-index-workeror carry mismatchedos/cpumetadata. The staged@use-crux/localmanifest injects platform packages as optional dependencies, while committed workspace manifests are guarded against platform optional dependencies so workspace and Karyla installs stay local-path based.Run semantic enrichment as shard-local work when the Project Index source graph proves complete shard and dependency evidence. Crux Local now fans semantic requests across a lazy worker pool and merges shard semantic patches without invalidating AST/source facts.
Move Crux Local incremental watch reindexing onto the production Go to Rust/Oxc Static Index compiler path and remove the TypeScript bundled fallback path. The production watch benchmark now enforces the Tier-A leaf p95 budget on the shipping path.
Keep source/interface hash evidence consistent across TypeScript and Rust/Oxc static records, including constructor signatures, and bump the static parse cache epoch to refresh stale source-row cache entries.
Tighten Project Index transport contracts by keeping WebSocket index messages and Rust source snippets on typed payload shapes instead of untyped JSON values.
Prefer the packaged Rust/Oxc static-index worker for
/testingfixture extraction when the matching platform package is resolvable, and fully flip the native AST gate to Rust-vs-golden validation with no TypeScript bundled baseline comparison.Update Crux Local native build baselines to Go 1.26.4 and Rust 1.96.1, and refresh the Rust/Oxc static syntax frontend dependency identity to Oxc 0.139.
Align native and semantic definition identity with runtime observability joins: composition definitions now require and use their authored
id, canonical ids remain byte-identical to the runtimeDefinitionRefbuilders, and missing composition ids no longer fall back to a misleading local variable name. -
Stabilize model routing around
router(),split(),retry(),fallback(), andcascade()wrappers with routing receipts, generate/stream support boundaries, and updated adapter docs.Breaking routing API changes: router
.with()and.select()are removed in favor of call-siteroutingandrouteoptions; variadicfallback(a, b, opts)is replaced byfallback([models], opts);_meta.router/_meta.cascade/_meta.fallbackare replaced byresult.routing; native OpenAI, Anthropic, Google, and Convex model options now type-reject routing wrappers instead of accepting unsupported values.Extend Project Index routing facts, static extraction, native semantic parity, relation policies, and index lints to cover split routes, retry targets, array-form fallback, call-profile model targets, and RouteArgs callback source refs.
Surface canonical routing receipts in local devtools run detail and Project Index views, including router defaults, split buckets, retry/fallback attempts, cascade budgets, and receipt-backed Turn Decision Report chips.
Project Index now shows required
RouteArgscontext types and literal route call-profile parameters. Run Detail renders receipt TTFT, bounded attempt errors, and cascade tier note/budget from the same canonicalrouting.reportpreview.Run Detail now accepts the canonical JSON-safe receipt when unavailable routing costs are serialized as
null, including nested retry, fallback, and cascade cost fields.OpenTelemetry span naming now covers all five canonical
routing.*primitives and no longer treats thefallback.attemptedge/name as a primitive. -
Add runtime-backed
workspace.watch()subscriptions for durable create, update, delete, and rename events, including cursor polling, unsubscribe, and transaction-aware event emission.Classify
workspace.watch()as read-style Project Index data access and bump local/indexer cache identities so existing snapshots do not mask the new facts.Update local devtools workspace read models so deleted files are removed from the tree and observed rename/move operations update the visible destination path.
Render
watchas a recognized read-style Project Index data-access operation in the devtools intelligence panel. -
Stabilize the Quality beta API and experiment record contract:
ctx.score()becomesctx.recordScore(), post-score callbacks are nowafterScores, retrieval recipe targets usetarget.retrievalRecipe(), decision-report assertions use the singulardecisionReportnamespace, and experiment records now write schema-version 2cellswith ordered assertionoutcomesonly.Harden Quality determinism by adding explicit cache identity epochs, including structured-output schemas and tool parameter schemas in cassette keys, including case input, prompt, params, dataset content, and scorer identity in output-cache/baseline fingerprints, failing loudly on corrupt committed baselines, and rejecting invalid scorer values instead of aggregating them.
Make Quality artifact writes atomic and safer under concurrent runs: cassette recording now single-flights duplicate misses, flushes merge with on-disk entries under a lock, timed-out cells quarantine late trace/cassette writes, and local read-model records write via temp-file rename.
Harden judge-backed scoring by pinning judge generation settings, framing untrusted output/reference/context in judge prompts, stamping judge provenance on score metadata and baseline identity, and adding human-label plus judge-report tooling for judge-vs-human agreement.
Structured judge inputs are serialized before prompt framing, so Safety's judge-backed constraints can evaluate object-shaped outputs without losing fields or throwing during escaping.
Harden the Quality local pipeline by making
crux quality run --jsonemit a single run summary object, adding worker/core protocol version checks and run-scoped event ids, surfacing worker crashes as structured exit-2 failures, fixing live read-model collisions and source-frame path containment, reporting skipped legacy records, and requiring an explicit devtools promotion variant.Quality observability capture now uses an observability-owned hook registry, preserving run-scoped trace capture without importing Node-only Quality internals into platform-neutral runtime bundles.
Add the Quality machine contract surface:
@use-crux/core/quality/schemasexports validation schemas and JSON Schema generation for records and CLI JSON, experiment records embed agent-readablefailuresartifacts, andcrux quality diff <expA> <expB> --jsoncompares saved experiment records through core-owned diff policy.Add the Quality adoption path:
crux quality initscaffolds first evals for uncovered prompt definitions,crux quality import-tracesconverts retained local traces into JSONL dataset rows, and dataset-backed failures now surface dataset path plus content fingerprint in persisted records, run summaries, failure artifacts, and experiment diffs.Add the Quality agent loop:
crux quality runnow supports--failed, deterministic--sample/--seed,--max-cost, and--changed-sincesubsets,crux quality mcpexposes list/run/show/diff/evidence/judge-report/label tools over MCP, andcrux quality initscaffolds a local Quality skill for coding agents.Declare Quality beta: the authoring surface, experiment/manifest schemas, CLI JSON outputs, and exit codes are stable within 0.x minors; future breaking changes require a minor bump and migration note while the first-party runner facade remains internal.
Finish Quality Project Index parity for beta: native static extraction now treats
afterScoresassertions as evaluation-levelctx.expect()sites, evaluation definitions expose catalog facts for devtools chips, spec experiment records enrichevaluation:*and covered definitions in the local read model, andcrux quality initdiscovers uncovered targets from Project Model evidence before falling back to config exports.Complete Quality observability coverage by removing the unused
quality.snapshotartifact kind, emittingbaseline.promotionartifacts on successful promotion, emitting diff-modecomparison.reportartifacts from core experiment diffs, and forwardingquality diffevents into local activity.Make the Quality machine contract operable from the UI: devtools serves
GET /api/quality/judge-report/{evaluationId}andGET /api/quality/experiments/diff?a=&b=(the latter runs the core diff op in a worker), the experiment read model surfaces core-ownedfailuresartifacts, and the workbench renders fix-surface chips that deep-link to the covered definition, a Failure Artifact panel with dataset provenance and cassette id, Pass/Fail cell labeling, a judge-trust panel (agreement, confusion matrix, kappa, disagreements), and an experiment Compare picker with a per-score/per-case diff and a drift banner. The TUI shows fix-surface letters on failing cells, the dataset fingerprint, and a CLI hint for the judge-report and diff views.