Changelog: harnessweaver¶
All notable changes to the core package. The format follows Keep a Changelog, and this package uses Semantic Versioning on its own (RULES V1). Versions before 0.18.0 were never published; their Git tags (harnessweaver@<version>) mark the commit where each one landed.
[Unreleased]¶
0.18.0 - 2026-11-02¶
The first version published to PyPI.
Added¶
harnessweaver.errors.ModelRefusal, aRunBlockedwith codemodel_refusalthat a model provider raises when the model declines. Itsresponsecarries what the call used.
Fixed¶
- A governed model call counts the usage and cost of a request the model declined, and notes the refusal on the trace (RULES R13), before it ends the run. It doesn't run
model.after.
0.17.1 - 2026-11-01¶
Added¶
- The package ships the Apache-2.0
LICENSEin its wheel and sdist.
Fixed¶
- The package's README no longer says it isn't published.
0.17.0 - 2026-11-01¶
Security¶
- Approvals bind what they should (ADR-0044, ADR-0045):
- One tool-call approval no longer authorizes every later identical call in the run. A grant covers one execution, and a repeat asks again.
approvals.grant_scope: runkeeps the old run-wide behavior. This changes behavior for existing specs. - An approval given for another step's ask no longer satisfies a role the approver doesn't hold. Governor step 7 checks the manifest's
approver_role, else the task's, else the spec's. When they differ the run asks again, keeping any edit, so that takes two approvals unless one person holds both roles. - Concurrent answers to one approval can no longer each resume the run.
approve()claims the request through the newStateStore.claim_approval, including across processes on one SQLite file. The loser getsNotFound(HTTP 404 from harnessd) and records nothing.cancel()claims too. A third-partyStateStoremust add this method.
Fixed¶
- A
workflowrun that suspended again before another step finished lost its finished steps from the checkpoint, so they ran again.
0.16.0 - 2026-11-01¶
Security¶
Model and tool content could get past a runtime control. Each of these now holds (S2, R1, R4, R11):
- A value a
sessionorworkflowhandler, or a custom runner, returned skippedoutput.before, so output guards and validation rules didn't apply to it, not even a locked baseline guard. The harness now firesoutput.beforeon every task's final output, unless the run just accepted exactly that value (s.generate,s.validate, thedirectrunner). Changed: a handler that returns unchecked content a guard objects to now ends blocked, or followsvalidation.on_failurefor a repair, since nothing is left to repair it. Runner conformance no longer countsoutput.beforeas a hook the runner fires itself. - The
directrunner ran any registered tool the model named, skipping the approvals of the task that does offer it. A registered tool the task'stoolsdon't list now blocks the run with codetool_not_offered, beforetool.before. - Guards never saw JSON object keys, and
pii_redactandstrip_instructionsskipped them, so an object keyed by e-mail address reached the model and a secret in a key passedsecrets.scan. Keys are now checked and redacted like values. Keys that redact to one name stay distinct ([EMAIL],[EMAIL]#2), so no entry is lost. Changed: a guard also sees field names, so one that flags a field name of a typed output sends it to repair.
0.15.0 - 2026-11-01¶
Security¶
An app's team could shed org baseline controls with files in its own repository. These all fail loading now (R1, R2, R10, ADR-0043):
- Wildcard locks (
guardrails.input[*],tasks.*.budget) were never enforced. They now cover every key or item, including ones added later, andspec explainshows them. - A profile in the app's
.harness/profileswith a higher version replaced the platform's baseline of the same name. The first directory on the search path holding a name now owns it, and versions are never compared across directories. Changed: a platform that splits versions of one name across directories must keep them together. - A profile the app listed before its baseline could put keys into items the baseline then locked. Profiles the app's team can write now merge after the platform's.
- An app file with the same reference text as a platform profile's parent displaced that parent. Profiles are now told apart by file, for duplicates and cycles, so the same relative text in two directories is no longer called a cycle.
nullat a path with amaxbound removed the limit and passed the bound. It's now aBoundViolation.- A baseline guard that fails open passed the R2 check when it sat in a parallel group with a closed one. Each guard is now checked. Changed: such a baseline now fails loading.
- An item with the merger's temporary id (
~autoN~) could edit a locked item that had no id. Those ids are now reserved. - A locked
where: sidecarguard followed an unlockedsidecar.url, so a later layer could repoint it. Changed: a baseline that locks such a guard must now locksidecar.urltoo. - A
__proto__key in a spec layer now fails loading.
0.14.0 - 2026-10-26¶
Removed¶
@hw.providerno longer accepts the planned portsDatasetStore,ErrorClassifier,ToolRegistryandValidator: nothing calls providers for them yet, so declaring one is now anunknown porterror instead of a provider that never runs. DESIGNS.md No. 14 marks them planned.@hw.providerno longer acceptsport="ToolGovernor". The harness always builds its own governor, which enforces the baseline, so a spec can't select another one (R1) and such a provider never ran. TheToolGovernorProtocol stays inharnessweaver.ports.NoSettings(fromharnessweaver.modules.evals.graders): theexactandschema_validgraders declare no config, as other settings-free providers do, and reject settings withtakes no settings.
0.13.1 - 2026-10-25¶
Fixed¶
Harness.authorize(and soharnessweaver policy testand harnessd'sPOST /v1/tools/{name}/authorize) gave an approval verdict only the manifest'sapproval.approver_role, while a real run falls back to the task's and then the spec'sapprovals.approver_role. A dry run now names the role a real run waits for (DESIGNS No. 18).
0.13.0 - 2026-10-20¶
Added¶
- Tool manifests take
server, the MCP server of atransport: mcptool. - The
tool-allow-listcontract (ToolAllowList, inharnessweaver.core.gateway) thatharnessweaver gateway mcpwrites (ADR-0039).
0.12.0 - 2026-10-20¶
Added¶
- Guards declared
where: sidecarrun their provider in harnessd atsidecar.url, with the token fromsidecar.token_env; the guard's fail mode applies when the sidecar can't answer (R2), andplugins.allowstill applies to the provider's name (S8). Harness.decide_policy(the policy engine's answer alone),Harness.output_type, andSession.validate(..., output=type), which parses a value into a type beforeoutput.before.- The sidecar's request models, as contracts in
harnessweaver.core.sidecar(ADR-0038).
0.11.2 - 2026-10-20¶
Fixed¶
Harness.runner_of(), and soharnessweaver doctor, failed for a task run by a plugin's runner (such aspydantic_ai.agent): it looked the runner up by its short name.
0.11.1 - 2026-10-20¶
Fixed¶
- Brings the fix from 0.10.1 into the line that has 0.11.0, which was built alongside it.
0.11.0 - 2026-10-20¶
Added¶
Harness.wrap(agent, task=...): wrap style for agent frameworks, with the adapter found from the agent's type asinstrument()finds one (DESIGNS No. 15, ADR-0037).- Adapters govern what their framework runs itself:
RunContext.call_tool(call, backend=...)runs a framework's own tool throughtool.before, the governor andtool.after, andRunContext.approval_needed(call, reason)brings an approval the framework asks for to the harness's approvers. During a replay the recording answers an adapter's own model and tool calls too (RunContext.replaying, ADR-0035).
0.10.1 - 2026-10-20¶
Fixed¶
- A guard with
action: require_approvalinguardrails.outputasked for approval again after a human approved the output, so the run never completed. The guardrails pipeline now honoursOutputEvent.approval: the approval answersrequire_approvaland is traced as aguard_approvednote, whileblock,escalate,repair, redactions and shadow guards still apply (DESIGNS No. 18, No. 20). Theguardsconformance fixtures gain approved-output cases (approved: true).
0.10.0 - 2026-10-20¶
Added¶
- Evals (DESIGNS No. 23, ADR-0033, ADR-0034): one dataset format (
suite,task,graders,thresholds,caseswithinput,expectand their owngraders), graders asEvaluatorproviders (the new port), the deterministic gradersexact,field_equals,numeric_close,contains,regex,citation_present,schema_valid,guard_firedandstatus, and thellm_judgegrader, which grades in a governed session.Harness.evaluate(dataset)runs a suite and returns aSuiteResult;Harness.output_schema(task)gives a task's output schema. - Online evals:
evals.online.sample_ratesamples runs by their id atrun.end, grades them withevals.online.graders(deterministic only) and keeps anOnlineSampleper run;Harness.samples()reads them. Sampling fails open. - The shadow-guard report: how often each
log_onlyguard would fire, with false positives and misses against what eval cases expect (R8). Harness.authorize(tool, args, ...): the governor's verdict on a call as a dry run, using up no rate limit (ADR-0036).- Recording and replay (ADR-0035): with
state.record: truea run keeps its model answers, tool results and approvals;Harness.replay(run_id)runs it again against the current code and spec without calling out or running tools, and reports changed statuses, outputs and decisions. - Fixtures suite
evals: graders, dataset labels, summaries and baselines, sampling and the shadow-guard report. Schemaseval-dataset,eval-sample,grade,suite-result,baseline,online-sample,policy-tests,recorded-callandreplay-report. The conformance suite checks evaluators (valid grades with no raw content, repeatable when deterministic, within their latency class) and state-store recordings and samples. - Guards that fire, or would fire in shadow mode, are traced as
harness.guardnotes (codesguard_firedandguard_shadow) naming the guard in full, likeguardrails.input[pii].
Changed¶
- The
StateStoreport gainsappend_recording,recording,put_sampleandsamples, and theToolGovernorport'sauthorizetakesdry_run.Harness.from_spectakes astatestore; giventelemetry, it no longer configures an OpenTelemetry SDK itself.
0.9.0 - 2026-10-20¶
Added¶
- Routing (DESIGNS No. 19, ADR-0029):
routing.<router>cascades onroute.decide, with the built-inrules,lexical,semantic(with the dependency-freesemantic.hashingEmbedder) andllmstages and any@hw.decider. The first confident stage wins, answers outside the options and failing stages abstain, the router'sdefaultanswers otherwise, and every stage's answer is aharness.routingtrace note. NewEmbedderport. - Context (DESIGNS No. 21, ADR-0030):
context.<source>with the built-inmemory(documents with ACLs, searched lexically) andpassthroughproviders, ands.context(name).fetch(query). Every item, fetched or passed tocheck(), has its ACL re-checked (require_acldrops items without one), is labelled with its source and trust, and is capped bymax_itemsandmax_tokensbefore thecontext.aftermiddleware; every drop is traced. NewContextProviderport. - Memory (ADR-0031): the
Memoryport, the in-processmemoryprovider, the spec'smemorysection (default_ttl) ands.memory(put,get,delete,entries), namespaced per tenant, principal and task. - The
grounded.citationsguard (ADR-0032): output sentences must cite, in square brackets, context ids the run kept; a run's kept ids survive suspension. - Fixtures suite
routing: tokens, similarity, the hashing embedder, therulesandlexicalstages and the cascade. The conformance suite checks context providers (valid items, only what the tenant may see), embedders and memory stores.
0.8.0 - 2026-10-19¶
Added¶
- Policy (DESIGNS No. 17, ADR-0025): the
PolicyEngineport and the built-incel_liteengine. Every engine combinespolicy.rulesthe same way (hw.decide_by_rules): deny beats require_approval beats allow, the first of equals decides, an unevaluable rule never loosens, anddefault_effectapplies when none match. Rules takehook: run.startto restrict runs and sessions as they start. New contractsPolicyRule,PolicyConfig,PolicyRequestandPolicyDecision. - Tool governance (DESIGNS No. 17):
kind: Toolmanifests (tools.manifests,hw.ToolManifest) withnative,httpandcodetransports, pinned definition digests (hw.definition_digest), rate limits, approval rules, output sanitizers and tenant allow-lists; and the nine-step governor every tool call passes, after thetool.beforemiddleware and on its final arguments: registry and digest, a JSON Schema subset check (ADR-0026), policy, the lethal trifecta (tools.trifecta), rate limits held under concurrency, an optionaltools.judgethat can only tighten, approval (tools.approval_by_risk, tasks'approvals.tools), and sanitizing beforetool.after. Each decision is aharness.governortrace note. Calls carry an idempotency key that survives resumption. Tools given in code carry manifests too (ToolDescription.from_manifest, ADR-0028). - Approval channels (DESIGNS No. 18): the
ApprovalChannelport and the built-inmemory,callback,consoleandwebhookchannels, notified once a request is saved; a failing channel is traced and the run keeps waiting. - The
Sandboxport andtransport: codetools (ADR-0027). - Guard findings can be marked
private(pii.regex's are); finding private data marks the run private for the trifecta check (S3). - The conformance suite checks policy engines (valid, repeatable decisions within their latency class), sandboxes (code runs; it can't read a host file) and approval channels.
- Fixtures suite
governor: canonical JSON and digests, rate syntax, the JSON Schema subset, policy combination, governor steps 1 to 7 and step 9. Schemastool-manifest,tool-verdict,policy-request,policy-decisionandsandbox-result.
0.7.0 - 2026-10-19¶
Changed¶
- Logging goes through loguru (ADR-0024), which becomes the core's fifth dependency (RULES R6). The core is silent until the app opts in with
logger.enable("harnessweaver"), and never adds sinks. The conformance suite's raw-content check (R9) captures loguru andloggingoutput alike. - Large modules are now packages of focused modules, each package's
__init__exporting what other code imports:harnessweaver.harness(theHarnessclass is built from mixins for runs, endings, approvals and sessions),core.context,core.plugins,core.registry,core.telemetry,spec.loader,spec.merge,spec.models,modules.guardrails.pipeline,modules.guardrails.providers(one module per built-in guard) andtesting.conformance;core.exprgainsgrammar,declaredandoperationsmodules. The public API (import harnessweaver as hw) is unchanged.
0.6.0 - 2026-10-18¶
Added¶
Harness.runner_of(task): the runner a task uses and the hook points it reaches.- Each repair attempt is traced as a note with code
repair, giving field locations and messages but never the values (R13, R9).
0.5.0 - 2026-10-18¶
Added¶
- The test kit,
harnessweaver.testing:harness_for_test(offline, fake clock, injected model, fake tools and deciders),scriptedreplies withtext/output/tool_call,FakeTool,FakeDecider,assert_traceandstep,assert_no_content(R9), andCassetteto record a real model's answers once and replay them offline. - The conformance suite (T5):
pytest --harnessweaver-conformance=<namespace>checks a plugin's metadata, that importing it is inert (S8), and that every contribution builds, returns valid results, is deterministic when it says so, blocks on timeout, leaks no raw content (R9), meets its latency class, and (for runners and adapters) fires only and all of its declared hooks (R4). Decorators takesample=for contributions that need settings. Harness.from_spec(model=...)replacesmodels.providerwith a provider object, for tests and offline runs.
0.4.0 - 2026-10-18¶
Added¶
- L0/L1 modules (DESIGNS No. 16, No. 20, No. 22):
- Guardrails: the spec's guards become middleware per stage (
input,context,tool_result,output,stream) with action overrides,log_onlyshadow mode (R8), parallel groups, bands from the declaring layer, and the built-in guardsregex.deny,pii.regex,secrets.scan,injection.heuristic,size.limit,topic.denyandllm.judge. - Validation: cel-lite business rules (
validation.rules) asoutput.beforemiddleware that asks for repairs (ADR-0023). - The offline
scriptedmodel provider (ScriptedModel): replies in order, rule matchers, streaming, failures. - Telemetry through the OpenTelemetry API:
harness.run,chat {model},execute_tool {tool},harness.guard,harness.decisionandharness.approvalspans, GenAI token-usage and duration metrics and aharness.costcounter, names pinned to semantic conventions 1.42.0, andcapture_contentwith post-redaction content only (R9). - Wrap style:
harness.instrument(client)finds an adapter from the client's type and governs its model calls, as their own small runs or inside a run or session (DESIGNS No. 15). Message.extensionsandModelResponse.extensionsfor provider data replayed unchanged with a turn (ADR-0022).HARNESSWEAVER_TEST_MODE=1(harnessweaver.ports.network_allowed) makes live providers refuse to call out; every test runs with it (R14).- Conformance fixtures suite
guards.
0.3.0 - 2026-10-17¶
Added¶
- The runtime (DESIGNS No. 7, No. 8, No. 15, No. 18):
Harness(hw.load/Harness.load,from_spec), withrun,submit,stream,result,approve,resume,cancel,session,explainandhooks. Loading resolves every name through the plugin registry, installs the spec'smiddleware:, builds each task's runner and fails if a runner can't reach a hook the baseline or the budgets need (R4).- The hook bus: bands, onion order, payload chaining,
Blockshort-circuits, fail modes and timeouts, baseline middleware must fail closed (R2), and every outcome traced (R13) in aRunTrace. RunContextwith governed operations for every integration style: model calls (alias resolution,allowed_aliases, streaming withstream.chunk), tool calls, routing, context, input and output checks, budgets (ADR-0012), deadlines and data labels (S3).- Built-in runners
direct(bounded tool loop, typed output with bounded repair),sessionandworkflow(step DAG, parallel steps, evaluator-optimizer loops), and standaloneSessions. - Approvals:
Suspendattool.beforeandoutput.before, checkpoints, approve / edit / reject, expiry, grants matched by call or output, resume after a crash, and cancellation (ADR-0021). Idempotency keys. - State stores
memoryandsqlite(ADR-0009), versioned prompts byname@version(ADR-0013), and the port Protocols inharnessweaver.ports. - The plugin contract (DESIGNS No. 13, ADR-0015):
Plugin, the@provider,@middleware,@decider,@runnerand@adapterdecorators,RESERVED_NAMESPACESandCONTRACT_VERSION. Discovery reads entry-point metadata without importing anything (S8); plugins load only when the spec references their namespace, from theharnessweaver.pluginsentry points orplugins.local;plugins.allow, namespace conflicts and contract ranges (V5) are checked at load; plugins can ship profiles (profile://acme.baseline@^1). The core's own runners and state stores are its built-in plugin. - A spec that uses a section whose module isn't built yet fails to load instead of running without that control (R4). Later versions add those modules, and 0.12.0 the last of them.
- Errors
NotFoundandRunInterrupted(RunBlocked,RunEscalated,RunSuspended) with codesnot_found,blocked,escalatedandsuspended(ADR-0020). - Conformance fixtures suites
bus,budgetsandplugins.
0.2.0 - 2026-10-17¶
Added¶
- The spec system in
harnessweaver.spec(load_spec,EffectiveSpec,HarnessSpec,ProfileSpec,Level): - YAML 1.2 (core schema) loading with duplicate-key errors (ADR-0017).
- Layering of the built-in
defaultsprofile, built-in profiles (profile://rag), org profiles (org://acme-baseline@^2, highest matching version fromHARNESSWEAVER_PROFILE_PATHor.harness/profiles), file profiles, the app spec and an environment overlay (harness.<env>.yaml, selected byHARNESSWEAVER_ENV). - Keyed-list merging by
idwith generated ids, locks (R1, ADR-0018), bounds that never clamp (R10), and provenance for every value. - Per-run budget overrides that may only lower overridable fields (
EffectiveSpec.run_budget). - Adoption levels L0-L4 with requirement codes, enforced against the org's
require_level(LevelTooLow). - cel-lite policy and validation rules compiled at load against their allowed names.
spec explainandspec diffbuilding blocks (render_explain,diff_specs,render_diff).- Built-in profiles:
defaults,extraction,rag,agent,workflow,chat. - Conformance fixtures suites
mergeandlevels.
0.1.0 - 2026-10-17¶
Added¶
- Contracts and core primitives (public API in
harnessweaver/__init__.py): Envelope,Principal,Labels,Budget, and the injectableClock(SystemClock,FakeClock).- The 13
HookPoints with their direction, the middlewareBands,execution_order, and a typed event payload per hook point (HookEvent). - The outcomes
Continue,Retry,Escalate,SuspendandBlock, with severity andmost_severe. Decision/DecisionRequestfor routing,PolicyEffectandtighten(R3),GuardResultand its sevenGuardActions.- Neutral model and tool contracts:
Message,ToolCall,ToolSpec,ModelRequest,ModelResponse,Usage,StreamChunk,ToolInfo,ToolResult,ContextItem,Acl,ApprovalRequest,ApprovalDecision,RunStatus. - The error taxonomy in
harnessweaver.errors, with stable codes andErrorInfo. - The
Durationtype ("250ms","20s","1h30m") and SemVer versions and npm-style ranges. - cel-lite, the built-in CEL-subset expression language (ADR-0004).
- JSON Schemas for every contract under
contracts/schemas, withmake schemas/make schemas-checkand a drift test (ADR-0005). - Language-neutral conformance fixtures under
contracts/fixtures/conformance(suitesdurations,semver,outcomes,hook_orderandexpr) and their Python runners. - Hypothesis property tests for outcome combination, cel-lite arithmetic and logic, durations and versions (T3).
- Extras that pull in first-party plugins:
harnessweaver[openai],[anthropic],[pydantic-ai],[monty],[otel],[cel],[cli],[sidecar]and[all]. - A test that keeps the core's runtime dependencies to a fixed list (RULES R6).