You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Repository split (phases 2–3): the platform is weftbase. The module path is now github.com/weftbase/weft (was github.com/weftgo/weft) and the loop's github.com/weftbase/weft/core (was github.com/weftgo/weft/core); every import, the facades' generate lines and the instrumentation scope names (github.com/weftbase/weft/core, …/otel/slog, …/thread) follow. The platform packages moved, with their history, to their own repository and module, github.com/weftbase/weftbase (not released yet; its own version line; it imports weft, weft never imports it): studio (+ studio/ingest, studio/runtime, the web source and the embedded dist), obsdb (+ obsdb/sqlite, obsdb/clickhouse, obsdb/obsdbtest), runtime, scope, exporter, the CLI (cmd/weft → github.com/weftbase/weftbase/cmd/weftbase, the binary weftbase), the examples studio-local, devtools-vite and thread/examples/approvals (now weftbase's examples/approvals), ADRs 0018 and 0024 (one-line pointers stay here), the weft mcp recipe and the platform's testing sections. weft studio and weft dev leave weft with the platform: traces go to any OpenTelemetry backend (OTEL_EXPORTER_OTLP_ENDPOINT, otel.OTLP), and Weftbase's console is coming (weftbase is not released yet); WEFT_STUDIO_URL / WEFT_STUDIO_TOKEN / WEFT_STUDIO_ADDR / WEFT_DB / WEFT_DISCOVERY are WEFTBASE_URL / WEFTBASE_TOKEN / WEFTBASE_ADDR / WEFTBASE_DB / WEFTBASE_DISCOVERY (no fallback), the local data directory ./.weft/ is ./.weftbase/ (default database ./.weftbase/weftbase.db), panel tokens are wb_pt.…. otel.Local and friends, which phase 1 moved to weft/exporter, are github.com/weftbase/weftbase/exporter. The framework drops clickhouse-go and fsnotify from its requirements. make layering now fails when anything here imports github.com/weftbase/weftbase; the Makefile's studio, vacuum and mcp-check targets and CI's clickhouse, studio and devtools-vite jobs moved with the platform. Step by step: MIGRATION-0.21.md.
otel (repository split, phase 1: weft emits; the platform stores, shows and controls): weft/otel no longer knows Weft Studio or its store. It configures from the standard OpenTelemetry variables only (OTEL_EXPORTER_OTLP_ENDPOINT and _HEADERS, OTEL_SERVICE_NAME, OTEL_RESOURCE_ATTRIBUTES, OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT) plus WEFT_ENV for the development gate, and no longer imports obsdb or the discovery file. Removed from otel: Local, Studio, LocalDB (package function and Pipeline method), StudioEndpoint (both), NoDiscovery, FromSDKSpans, FromSDKRecords, the WEFT_STUDIO_URL / WEFT_STUDIO_TOKEN / WEFT_DB environment destinations, the discovery-file join and Install's zero-config fallback to the local sink — otel.Install() with nothing configured now records nothing, says so in one INFO line and returns a no-op. Several destinations at once, each with its own content policy, raw bodies, the dev detail and the slog bridge are unchanged.
exporter (new, platform; moves to the weftbase repository with studio, obsdb and runtime): the local developer platform's destinations on top of otel. Migration: otel.Local(p) → exporter.Local(p), otel.Studio(url, tok) → exporter.Studio(url, tok), otel.LocalDB() → exporter.LocalDB(), p.LocalDB() → exporter.LocalDBOf(p), otel.StudioEndpoint() → exporter.StudioEndpoint(), p.StudioEndpoint() → exporter.StudioEndpointOf(p), otel.NoDiscovery() → exporter.NoDiscovery(), otel.FromSDKSpans / FromSDKRecords → exporter.FromSDKSpans / FromSDKRecords; for today's zero-config developer behaviour (WEFT_STUDIO_URL / WEFT_STUDIO_TOKEN, the discovery file of a running weft studio, WEFT_DB, the local sink when nothing is configured) use exporter.Install(...) / exporter.Start(ctx, ...) in place of otel.Install / otel.Start; they take the same otel options. exporter.StudioEndpoint (what runtime.Install dials) is known for any installed pipeline with an exporter.Studio destination, a plain otel.Install included. Studio, the runtime link, the weft CLI and the examples use it.
Every framework package (everything but studio, obsdb, runtime, scope, exporter, cmd/weft, their internal packages and the platform examples), its tests included, no longer imports a platform package; the adapters', otel's and thread's tests that read records back from obsdb run against in-memory exporters, and their store round trips moved to exporter's tests.
Fixed
thread: a decider's context no longer hides what the parked turn ran under. A resume takes the parked turn's context values only where the deciding call's context has none, and metadata is one value: an inbox handler that tagged its request context with core.WithMetadata and called Decide ran the resume without the parked turn's tenant pairs (likewise an inherited ParkAllExcept list under one of the decider's own). The settings recorded on the requests now ride every resume, in-process too; the parked metadata wins per key, the decider's merges under it (ADR 0021, amendment 2026-11-03). A deferred steer's follow-up had the same hole against the turn it aimed at, and keeps that turn's metadata and park rule the same way (ADR 0019).
thread: a call parked under Quorum(n) needs n approvals however the session is reopened. Quorum is a session option and was not recorded: a process that opened the session without it (another worker, another build) resolved the call on one approval. The request now records its quorum (ApprovalRequestEntry.Quorum, JSON quorum; Request.Quorum on Pending), and the stricter of it and the session's own decides; a pool mirror carries the child's and records the parent's (ADR 0021, amendment 2026-11-03).
thread: an approved call no longer runs on a build whose tool changed under it. A parked call resumed after a deploy or a restart onto new code ran its approval against whatever tool now had its name: a tool whose input schema changed in a way the old arguments still decoded into ran the call under a contract nobody approved, and a removed or renamed one read NO_SUCH_TOOL with nothing in the audit trail. The request now records its tool's input schema hash (ApprovalRequestEntry.SchemaSHA256, JSON schema_sha256); on a resume whose agent lacks the tool or has another schema, the approved call is denied with a model-visible reason and audited (resume step, outcome drift). Denied and resolved calls, and a change to the approval mark alone, are unaffected (ADR 0021, amendment 2026-11-03).
core: an agent's manifest — and weft.manifest.hash, its sha256 — now changes when any agent below it in the delegation tree does. A Subagent tool named its child only, so a child's (or grandchild's) prompt, tools or model could change while the parent kept its hash, and a version pinned by that hash did not pin what the parent delegates to. Each Subagent tool entry now carries subagent_hash, the child's own manifest hash (named or unnamed child), which pins its children in turn; the manifest still does not recurse, and the hash is still the sha256 of Manifest(a) (ADR 0012, amendment 2026-11-03).
thread: a deny-grant's decision is recorded with Viagrant (it had none); an approval grant's always was.
thread: a resume after a restart no longer drops the park rule the parked turn ran under. A turn sent with thread.RunOptions(core.ParkOn(...)) or core.ParkAllExcept(...) (or one inheriting ParkAllExcept from a delegating run, as a thread/pool child does) parked a call; the process restarted; the reopened session was decided — and the resume ran without the rule, so the next call to a tool the rule parks ran unasked. The rule is now recorded on the boundary's request entries and put back on a resume that lost the parked turn's options. New: core.RunResult.Park / weft.ParkRule, the rule a run ran under as data, and ParkRule.Options() to re-apply it.
thread: a resume after a restart, and a queued send or steer restored by Open, no longer drop the rest of what they were sent under. The parked or queued send's OnlyTools narrowing, a lowered MaxSteps or Parallelism, its Instructions, Thinking, ToolChoice and Params overrides, and its caller metadata (thread.RunOptions(core.Metadata(...)) and the context's core.WithMetadata pairs, a tenant among them) were process state: after a restart the resumed run offered tools the send had narrowed away, spent past the lowered budget and lost its tenant tag, and a send queued behind an open boundary under ParkAllExcept ran every tool unasked. They are now recorded as data — ApprovalRequestEntry.Run on the boundary's requests (replacing the unreleased ApprovalRequestEntry.Park, JSON park → run.park) and ReceiptEntry.Run on queued and accepted receipts — and put back on the restored run. A restored narrowing is never dropped to let the run start: an OnlyTools name the reopening agent no longer registers, or a budget above its own, fails the run with ErrInvalidRunOption before any model call. UseModel and OnMessages are not data and stay lost: a run restored after a restart runs on the agent's own model (ADR 0021).
Added
thread: Grant.Who and GrantEntry.Via — a grant records its grantor and the door it came through (user for Session.Grant; an ApproveAlways grant takes the minting decision's Who — its signing key id when the signature named no one — and Via). Every decision a grant makes is recorded with the grantor as its Who, so an automatic approval is attributable to the person who made the grant; the grant.mint record carries who in its content (ADR 0021, amendment 2026-11-03).
thread: Decision.RequestID and Decision.ArgsSHA256 — a decision can name the occurrence and the arguments its decider was shown (copy them from the Request). Both optional; when set, Decide refuses a decision whose occurrence is not the pending one (ErrNotPending) or whose arguments are not the request's (ErrArgsChanged, now returned by Decide as well as DecideSigned), before recording anything — call ids repeat across turns, so a page left open on an earlier occurrence could approve a later call that reuses the id. The hash is recorded on the decision entry as ApprovalDecisionEntry.ArgsSHA256 (JSON args_sha256); DecideSigned records its verified challenge's. An Approver answer naming other arguments is a decline (ADR 0021, amendment 2026-11-03).
core: RunSettings, SettingsOf(ctx, opts...), RunSettings.Options() and RunSettings.Clone() (facade: weft.RunSettings, weft.SettingsOf) — what a run is started under, as data: its park rule, OnlyTools, run-level MaxSteps and Parallelism, the Instructions / Thinking / ToolChoice / Params overrides, and the metadata its options and context carry. A caller persisting a run to start or resume in another process records it and puts it back; thread does for its parked boundaries and queued sends.
otel: Tag(tag, opts...), TagOf(opt) and Pipeline.Tags() — a package wrapping otel recognises its own options among the ones it is handed and, for any pipeline (one a plain otel.Install built included), which of its destinations the pipeline built; and Environment(opts...), which marks a wrapper's destinations as environment destinations (dropped under NoEnv, de-duplicated by URL against the destinations named in code — an explicit otel.OTLP on WEFTBASE_URL's URL wins over weftbase's environment Studio again — and leaving Install its Fallback when all of them fail). weftbase's exporter is built on them.
otel: Sink(name, open, opts...) — a destination written synchronously (simple processors, no queue; Blocking and QueueSize do not apply), first-party by default, whose exporters open builds at Start and the pipeline shuts down after its providers (or at once when the build fails); an export error is counted and named by the destination's WARN, never handed to OTel's error handler. FirstParty() (a destination you operate yourself: content on by default; raw bodies, the dev detail and app logs eligible — usable on OTLP and Exporters), Fallback(opts...) (destinations used only when nothing else is configured), BatchDelays(logs, spans), Name(label), Options(opts...) (a comparable bundle), Installed(), Pipeline.Destinations(), and the exported ErrNoDestinations. NoEnv() and NoGlobal() are comparable values.
make layering (scripts/check-layering.sh, CI job layering): fails when a framework package, tests included, depends on a platform package.
thread: WithApprovedBy(ctx, class) — the public way for a layer above thread (a platform's inbox, an integration, the devtools playground) to decide through Session.Decide on someone's behalf and have each approved call reported with weft.tool.approved_by set to that class instead of user (record contract §3). Reporting only; the decision, its validation and its record are unchanged. weft/runtime now uses it instead of the internal provenance package, which only thread imports — a step toward moving runtime out of the framework module.