Skip to content

v3.20.0

v3.20.0 makes what the orchestrator did checkable and exportable. A run's operational record now has a scorecard, a supervision-period conduct fold, and an OSCAL assessment-results export an auditor can read. Credentials and exports are hardened where a scope or a check could previously be widened or skipped.

Compliance exports an auditor can check

bernstein compliance export-oscal writes a NIST OSCAL v1.1.0 assessment-results document from the tamper-evident audit chain, lineage log, and cost ledger, and the vendored schema validates the output (#5890, #6011).

SIEM audit exports now carry the chain, not an opaque HMAC. Each exported AuditEntry carries prev_hmac and a monotonic sequence, every batch can be closed with a signed SegmentReceipt, and bernstein audit verify-export <file> checks an exported JSONL file for deletion, reordering, and gaps using only the file and the signer's public key (#5034, #5605).

Trace export for orchestrator-written runs

bernstein trace export now accepts runs the orchestrator wrote. The emitter and recorder were specified independently and never met: the emitter read model_id / model_provider / gate_config, while the orchestrator journaled model / provider and no gate configuration. The orchestrator now journals the resolved facts additively under the emitter's names — model_id and model_provider on agent_spawned (the existing model and provider keys stay), and the resolved gate configuration as gate_config once at run start. A missing fact still refuses, and an endpoint-routed worker with no provider still refuses rather than inventing a vendor name (#6051).

A run that spawned more than one worker previously exported only a single record. The emitter now splits such a run into one Trust Record per hop (exec_id set to that spawn event's agent_id, in spawn order) and folds them into the existing run-level aggregate. A hop's model comes from its own agent_spawned event and a hop with no tool_call evidence omits tool_transcript rather than reporting a false zero. The aggregate carries tool_transcript only when every member does. Journals with no agent_spawned event keep the prior single-record behaviour (#6050).

An endpoint-routed worker that resolved no provider now journals the model namespace from the operator-configured model identifier. For omnilab/fleet-hard it journals model_provider as omnilab and keeps model_id as omnilab/fleet-hard. A bare identifier or an empty namespace is not a provider, and a resolved provider always wins, so a run that cannot say who served the model still refuses rather than gaining a made-up vendor name (#6063).

An exported record previously wrote the CLI adapter identifier -- claude, codex -- into model.provider, which names the tool that was driven, not the vendor of the model that answered. Adapters now declare the vendor of the models they front, and the orchestrator journals that declaration. An adapter that fronts several vendors, or a gateway, or anything it cannot name, declares no vendor; that hop journals no model_provider and export refuses it by agent id, the same way an endpoint-routed worker is refused (#6064).

The documentation now states that a record's model_id is the identifier the role policy asked for, written as configured. A policy that names a tier records the tier word, not the dated identifier the adapter launched. Pin a model when the record has to name the exact one that ran (#6077).

A run that accounts for itself

Scorecard is a six-section run summary - trajectory, verification, recovery, state consistency, safety, and replayability - where every field cites the journal seq and step_hash it was read from, so a verifier that disagrees on the source of a field can refuse the scorecard (#5413).

The supervision-period conduct fold records decisions the system already makes - decisions whose record could not previously be checked by someone who was not there - as a DSSE/Ed25519 artefact (#5476, #5853).

Credentials and records that stay in scope

A credential's file scope can now be narrowed to a sub-glob: src/** can hand out src/core/**, where before the mint refused any child pattern the parent had not declared verbatim (#5418, #5518).

Runtime key material written at the repository root is gitignored, closing a path where auth/agent_identity_jwt_secret and the private half of the a2a lineage signing key could be staged by git add -A (#5801, #5802). Untrusted values are escaped before they reach log lines (#5989), path-traversal identity ids are rejected at the store boundary (#6028), and the volunteer hub no longer accepts every browser origin (#6029).

The benchmark stops rewarding guessing

A declined task no longer scores like a wrong one. An instance can end abstained, and summaries report three rates - resolve, abstain, and confident-error - so a run that answers rarely and well is distinguishable from one that answers everything and is often wrong (#5567, #5905).

Operator-facing fixes

The bare entry point ran agent discovery in a background thread and then waited ten seconds for it. On a machine where discovery is slower than that, the wait raised TimeoutError and bernstein -g "..." --plan-only died with a traceback instead of printing the plan. The results were cosmetic and read by nothing downstream, so the wait is gone (#6052).

A quarantined task that is skipped now transitions to failed instead of wedging quiescence (#5967). The gRPC TaskService fills goal from a task's title when the goal field is absent (#5889). Journal retention stays capped when the active run sorts first (#5881), and the effort bandit is fed the effort level a session actually ran at (#5879).

Release and CI

A version bump that lands through the merge queue now tags. The dispatcher listens on gh-readonly-queue/main/** and confirms the triggering SHA is an ancestor of main before tagging, so a queue entry that is later ejected is skipped rather than published (#4930, #6033, #6038).

The lint gate covers tests/ (#5606), and the CodeQL sanitizer model pack is now actually loaded, so the project's log sanitizers are modelled as barriers for the log-injection query (#6025). The quorum check's red annotation is never empty (#5829), and the nightly mutation job measures lineage_tips again after naming only the tests that can kill it (#5595, #5610).

Also in this release

docs/integrations.md maps directories, IdPs, secret stores, and telemetry to what is shipped, open, and deliberately not planned, with a "wired" column that distinguishes reachable modules from mere names (#5023, #5528). Conformance vectors now cover the attribution and authority questions of the auditor scoreboard (#5058, #5059, #5680, #5798). The conformance score now runs every vector module present in the directory instead of a hand-written list, so a vector added without editing that list is still scored (#6053). A Windows spawn preserves operator-set PYTHONUTF8 and PYTHONIOENCODING (#5718). Scanner paths are normalized to POSIX for cross-platform consistency (#5791). Evidence-envelope golden vectors are pinned to their exact bytes on line-ending-translating checkouts (#5516). The getting-started pages no longer quote output the CLI never produced (#3619, #5534). Duplicate CLI command registration is caught anywhere under src/ (#5847). The reliability-docs assertion reads its file as UTF-8 (#5816). The Simplified Chinese, Vietnamese, and Turkish README translations were adopted (#5258, #5287, #5283).

Contributors

  • vaibhav8a - the lint gate over tests/ (#5606), credential glob scope narrowing (#5418), the homebrew tap credential naming (#5826), declined-task scoring and three bench rates (#5567), the hourly quorum sweep (#5827), the CodeQL trunk analysis (#5795), the integration index (#5023), the lineage_tips mutation baseline (#5595), the getting-started sample fixes (#3619), eight shards on the critical path (#5794), and tagging the commit CI validated (#5796).
  • thegoodengineer - the authority conformance vectors (#5059), the Vietnamese and Turkish README translations (#5287, #5283), the PluginTrust docstring accuracy (#5676), the GateRunnerCommandsMixin composition pin (#5682), and the BERNSTEIN_RUN_ID xfail cleanup (#5703).
  • Bhumika-1432006 - the SIEM audit export chain (#5034), the attribution conformance vectors (#5058), the test-impact re-export resolution (#5111), and the journal retention cap (#5881).
  • Chirag6722 - the queue hygiene label fix (#5848), the tree-wide duplicate command registration guard (#5847), the root key-material gitignore (#5801), the evidence-envelope byte pinning (#5516), and the reliability-docs UTF-8 fix (#5816).
  • victorwon2001 - the Windows PYTHONUTF8/PYTHONIOENCODING pass-through (#5722) and the macOS test file timeout (#5714).
  • mikemikimike - the project-pulse rate-limit retries (#5812).
  • 7487 - the Simplified Chinese README translation (#5258).
  • Phoenix1504e - the POSIX normalization of scanner paths (#5791).