1.2.0 release notes
Released on 25 September 2026: tag
v1.2.0,
package hiveloom==1.2.0 on PyPI.
uv tool install "hiveloom==1.2.0"
These notes are the 1.2.0 section of the project's
CHANGELOG.
For the ideas behind the headline features, read
Signal-driven evolution,
Delegation and the demo harnesses that prove them.
The signal, memory and collaboration release. Evolution now finds where a harness's evidence points before any model is asked — contrasting failing and successful runs feature by feature, counting mechanisms, and saying how much the sample can show — makes every proposal a prediction about that place, checks the prediction against the runs that follow, and can run a measured keep-or-revert loop on an eval. Memory scales and learns: a run sees the lessons that match its task, finished runs are reflected into reviewed lessons, and compaction keeps what earlier summaries established. A harness can hand its task to a fitter peer — locally or over MCP — and still verify the answer itself. It keeps working notes outside the conversation and narrows large stored results in place. The runtime recovers truncated turns and transient provider failures within declared budgets.
Added
-
Signal location (
hiveloom signal, free). Every ingested run is indexed with features — tools called or erroring, spills, steps violated, playbooks, memory entries shown, notes, lessons proposed, delegations, the executor model, task size, friction per component — never how it ended. The signal map contrasts failing and successful runs of one version feature by feature (Fisher exact, Benjamini-Hochberg across features), collapses features that mark the same runs, maps each to the spec levers that could change it and whether evolution may pull them, lists what failures share when nothing succeeded, counts friction mechanisms per component, attributes each failure to a loss class, and states the success-rate interval and the smallest change the sample could detect. An external failure label counts as a failure. The evolution report carries the map and one or two passing runs (their task and output only under thetrace_excerptsopt-in). -
Aimed proposals. The proposing prompt receives the signal map as its own section, and the contract gains "Locate, then aim". A proposal carries a
target— one of the map's targets ormetric:<objective>, the direction it will move, optionally by how much — which is stored with the evolution. -
Assessment (
hiveloom assess, free). Every applied evolution is checked against its own prediction on the runs of the new version, with the success rate as a guard: pair by pair when both versions ran the same eval cases (McNemar exact, sign test), unpaired otherwise (Fisher, Welch). Verdicts areconfirmed,refuted,regressed,inconclusive(with the runs that would settle it) andpending. Evolution records keep the proposal id, prediction, changed paths and a bounded diff. -
Measured evolution (
evolve --experiment eval.yaml --yes). Each round measures the current version on the eval, applies one aimed YAML change, runs the eval on the new version, assesses it pair by pair, and keeps a confirmed change or reverts it byte for byte (--rounds,--keep-inconclusive,--remeasure-baseline). Decisions are stored and reach the next round's history. Code changes are never applied in this mode. -
Relevance-selected memory (
memory.selection: relevant). A run is shown its pinned entries plus the entries that best match its task, up tomemory.max_selected, chosen deterministically once per run; the read-onlysearch_memorytool finds the rest. The selection is journalled asmemory_selectedand indexed, so the signal map shows whether a lesson helps.selectionandmax_selectedare frozen from evolution; entries gainpinned. -
Post-run reflection (
evolution.reflect, off by default). After a failed run (or any, withon: any) a strong model drafts at mostmax_lessonsdurable lessons from the run, preferring none to a speculative one; they are queued withtrigger: reflectthrough thepropose_memorypath and applied only by a human. Eval runs never reflect, and a cooldown bounds spend. Frozen from evolution. -
harnesses/signal-lab: an offline worked example of signal-driven evolution. Its scripted clerk fails on lowercase invoice ids;signallocates the failing tool, reflection drafts a lesson for review, andevolve --experimentreverts a refuted retry idea before keeping the uppercase rule the eval confirms (8 improved pairs, none worse). The learned rule is then shown only to the runs it matches. -
harnesses/delegation-lab: an offline worked example of delegation. A front desk with no ledger refers aledger-deskpeer (below_fitness) until the peer has earned a measured record, then hands the invoice question to it on start, re-verifies the answer with its own validator, and records the child inhiveloom lineage; a general question is routed to no one (none_fit). -
Every demo harness now states what it proves: each README opens with a one-line claim and a capabilities list, and ends with the evidence to look for and what to try;
harnesses/README.mdis a gallery indexed by capability. The demos gained, through the CLI:regex_output_filter,max_turns_hard_capandlogging.redactplus a "Ship it" section (quickstart); a house-style skill loaded withload_skill(example-summarizer);plan_then_actand an aimed evolution walkthrough (routing-lab);file_existsandcommand_succeedson the exported artifact (memory-lab); parallel tool execution (ticket-triage); auto-propose with trace excerpts (signal-lab).scripts/package_e2e.pydrives every offline demo end to end against the installed wheel. -
hiveloom guide signal: the signal-driven evolution reference (docs/signal-driven-evolution.md). -
Delegation between harnesses (
delegation, off by default). A run can hand its task to a peer harness from the local registry that is more specific or has better measured Hive fitness, and verify the answer with its own validators. Three runtime-enforced modes (on_start,on_verify_fail,model_choice, the last one registering a deferreddelegate__<peer>tool per eligible peer plus an activelist_peers), fitness floors, depth and cycle refusals, and a child cost cap carved out of the parent's remaining budget. Peers that do not qualify come back asreferrals. The model stays frozen;delegationis tunable by evolution. Seedocs/delegation.md. -
RunResult/run --jsongaindelegations,referralsanddelegated_cost_usd;runs.lineage_kinddistinguishes a delegated child from a fork, andHive.children(run_id)lists both. -
Harness-to-harness delegation over MCP.
hiveloom mcp serveaccepts a caller's lineage in the request_meta, links the peer run to its parent in the Hive, and refuses a chain deeper than--max-depth(default 3) or one that revisits a harness — asstatus: "error"data, before any spend. -
hiveloom mcp serve --concurrency Nbounds concurrent runs per server process (default unlimited); waiting is bounded by the caller's timeout. -
A peer run's artifacts now cross the wire in the
_hiveloomenvelope and land on the calling run'sRunResult.artifacts. -
hiveloom add mcp-servergained--timeout-seconds(default 30s,0 < t <= 600) to raise the connect/tool-call timeout for a slower peer harness or tool. -
Durable harness memory. A
memoryspec section whose entries render as a# Memorysystem-prompt section every run, managed withhiveloom memory list|show|add|forget. The executor never writes it; entries reachharness.yamlonly through that CLI or an applied evolution proposal, which may append or replace one entry at a time.memory.enabledand the entry/character/prompt budgets are frozen from evolution and hard-capped, and an over-budget entry is a validation error rather than a silent eviction. -
The opt-in
propose_memorybuiltin lets the executor offer a durable lesson from inside a run. It writes nothing: the lesson is redacted, checked against the harness's memory budgets, journaled asmemory_proposed, and queued as a gatedMutationProposalwithtrigger: executorthatproposals list|show|apply|rejecthandle unchanged. Identity and Hive path come from the run context, proposals dedup on the lesson's content,max_per_runbounds one run, and runs launched byhiveloom evalrecord the lesson without queueing. -
Run-scoped notes. The opt-in
notesbuiltin lets the executor write, read and delete named notes that live outside the conversation and so survive compaction. Only their index (name, size, first line) enters the system prompt. Notes are private to the run, redacted before the write, journaled asnote_written/note_deleted, bounded bymax_notes/max_note_bytes, and carried into a fork only through a hash-bound manifest replayed from the parent's verified journal. -
transform_resultreshapes a stored tool result in place —lines,head,tail,grep,json_path,count,sort,uniqueandconcat— so a large object can be narrowed without paging it through context. Output that still exceeds the inline budget becomes a derived object with its own handle. Every op is bounded in bytes scanned and emitted, and a pattern that repeats an unbounded quantifier is refused.grepalso takesraw: true: the bare matching lines, with no header and no numbers, up to 10 000 of them, refused rather than truncated. Its derived handle is therefore data, and can be passed tofile_writecontentso an export never passes through the model. -
Handle-typed tool parameters: a tool may declare which string parameters accept a stored-result handle (
@tool(..., handles=["content"]), orhandle_paramson aTool;file_writedeclarescontent). The runtime expands the handle under the run's authority at dispatch, capped at 4 MiB, while the journal and the provider keep the handle. An unresolvable handle is a tool error, never a silent literal. -
context.compaction.max_tokenscaps the output of the summarize call alone. It used to run withmodel.max_tokens, and a reasoning model given a 32k budget spent 545 s on one summary. -
harnesses/memory-lab: an offline worked example that puts the three memory layers on one task — a spilled log narrowed in place, findings kept in notes across compaction and a fork, a derived object handed tofile_writeby handle, a lesson the executor proposes, and a memory entry that reaches the spec only throughproposals apply. Its scripted provider parses every value it reports out of a real tool result. -
Evolution attempt memory: recent applied/rejected proposals inform subsequent proposals, and SDK callers can supply measured
AttemptRecordhistories. Review decisions and inconclusive experiments are not treated as proof of quality changes. -
Operator findings through
evolve --note(repeatable) andanalyze(analyst_notes=...), including opportunities without failed runs. -
Model capability metadata
max_output_tokens, checked againstmodel.max_tokens, and configurable OpenAI-compatibletimeout_seconds. -
Frozen
model.paramsfor provider request fields, forwarded by the Claude and OpenAI-compatible providers and preserved by runtime model overrides. -
Experimental
best_of_nloop policy (withloop.attempts, default 3): samples several independent attempts, rewinding the conversation between them, and submits the plurality answer. Attempts shareloop.max_turns, tool state and the cost guardrail. Its open limitations (journal replay of the rewind, answer equivalence, verification ownership) are listed indocs/evolution-migration.md.
Changed
-
A proposal from
evolve,evolve --propose, auto-propose or the HTTP control plane must name atargetfrom the signal map (or stateobjective_expectations); one without is sent back for repair within the existing three-call budget. A model or script that emitted the pre-1.2.0 proposal shape needs the field. -
Evolution's default attempt history is measured: each applied evolution with its assessment verdict or experiment decision, plus rejected proposals, instead of the unmeasured applied/rejected queue ledger.
-
Summarize-compaction carries the previous summary forward in
<previous-summary>with an instruction to keep what is still true, and anchors the summary to the agent's newest statement, instead of re-summarizing the earlier summary as transcript. -
Truncated model turns receive continuation feedback within the declared token budget. Three consecutive truncations or exhausted turns return
truncated(CLI exit 4), retaining partial output; passing verification can still establish success. Truncation is indexed as evolution evidence. -
context.tool_results.transforms(default on) offerstransform_resultbesideread_tool_result/search_tool_resultonce a result has spilled, so an existing harness that spills now sees a third retrieval tool. Set it tofalsefor the pre-1.2 retrieval surface, e.g. as an eval's control arm. -
Provider factories read a harness's
.envinstead ofload_dotenv-ing it into the process, so one process serving several harnesses can no longer hand harness B the credentials it found in harness A. Only the provider's key variable is read from.env; other variables in that file are no longer adopted into the process environment.
Fixed
- A blocked output is no longer handed back. When
regex_output_filter(or any output guardrail) blocked an answer and the model never produced a compliant one beforeloop.max_turns, the run returned the blocked text as itsoutput; a blocked delegated answer was returned withguardrail_haltthe same way. An output is now recorded as the run's answer only after the output guardrails pass; a turn-exhausted run returns no output and sayslast output blocked: …in its reason, and a blocked delegated answer is dropped. Found by the quickstart demo, where a generated AWS-style key id was blocked twice and still returned. - Auto-propose no longer drafts from runs inside an eval batch. A batch is a
measurement in progress, and each failing cell past
min_failurescould spend a strong-model call on partial evidence;propose_memoryand reflection already skipped eval runs. - Runs routed through their own playbooks (a playbook with its own model) are no longer held out of their version's fitness bucket as "swapped": only a model swap from outside the spec is.
- Compaction no longer discards the newest tool results. Summarize-compaction
kept only the last message, which for a tool turn is results whose calls had
just been summarized away. The orphan repair then dropped them too, so the
output the model had just asked for was neither summarized nor kept, and
runs re-issued the same calls after every compaction. The newest exchange
(the calls and their results) is now kept whole. It is folded into the
summary only when it alone exceeds half the budget.
truncate_oldestkeeps it the same way. - Evolution could add a dangerous tool (
shell) by appending one entry (tools.+,tools.<n>) or by renaming one (tools.<n>.builtin); only whole-list replacement was checked. Every path undertoolsis now gated. propose_memorywas refused on every harness that declaresevolution.objectives("proposal must name at least one configured metric objective"). A lesson predicts no metric, so the gate exempts a proposal that only appends tomemory.entries. The evolver's own proposals are still held to their objectives.- Two queued lessons with the same title got the same entry id, so the second could never be applied. On apply, a memory append now takes a free id, and one whose content is already in memory is refused instead of added twice.
- A memory entry's title is collapsed to one line when rendered, so a title cannot open a new heading in the system prompt.
- Fork carries each note as it was at the fork point, from the journaled
content, rather than re-reading the parent's current file. Notes the parent
rewrote or deleted later are no longer lost. A fork of a fork keeps what it
inherited (
notes_inheritednow journals the granted notes). Deleting an inherited note no longer deletes the fork's only copy. Two parallel writes of one note name can no longer leave it unreadable, and a resume that is granted fewer notes than its manifest lists records themissingnames. transform_result:- Line splitting is linear (it was quadratic: 88 s to
count35 MB). - A line over 1 MiB is kept in pieces rather than cut short.
- A scan that stops at the 32 MB ceiling says so rather than reporting partial counts as whole.
sortanduniquesplit lines on\nlike every other op.grepsays it stopped only when more lines actually match.- With
transforms: false, an oversized handle argument no longer points at a tool the run does not have.
- Line splitting is linear (it was quadratic: 88 s to
- Memory-layer bookkeeping holds when a turn's tool calls run in parallel.
notescould hold more thanmax_notesandpropose_memorycould queue more thanmax_per_run, because each read its limit before claiming the slot it was about to fill; the check and the reservation are now one step under one lock, and a note is staged and moved into place rather than unlinked and recreated.transform_resultno longer answers an oversized single-element result — ajson_pathselection, aconcat— with the truncation marker and no data at all: the element is cut at the output ceiling, so the result still becomes a derived object that can be narrowed again.concatreads under one running byte budget across the objects it joins rather than up to the scan ceiling for each. - Queued proposals survive review.
proposals applynow leaves a proposalpendingwhen it applied nothing (YAML declined, no code change approved) instead of marking itappliedand refusing the retry as "already applied";proposals apply --jsonwithout--yesis a usage error (exit 3) rather than a no-op that resolved the row. Memory proposals append with the reserved path segmentmemory.entries.+, resolved when the proposal is applied, so a queued lesson can no longer silently replace an entry added since — and an append-only proposal may apply after the harness version moved, so several lessons from one run all reach the spec. A negative list index is refused with a clear message. - Evolution repairs malformed proposals and correctable objective omissions within three total model calls. Inconsistent metric evidence fails before any call. Prompt evidence, history, notes, and the current spec are redacted; evidence sections have size limits. Updated findings/history invalidate stale proposal deduplication keys.
- OpenAI-compatible calls retry interrupted HTTP reads and transient error bodies returned with HTTP 200. Embedded context overflow remains recoverable. Blank visible content no longer discards reasoning, and oversized reasoning and provenance payloads are bounded without discarding useful answers.
- Provider parameters cannot replace harness-controlled identity, transcript, tools, output limits, streaming, or response count, and must be bounded JSON.
- Code-hook validation no longer mistakes
**kwargsfor positional arguments. hiveloom set/removecan now address an existing list item by numeric path segment (e.g.guardrails.0.value,mcp_servers.0.timeout_seconds) instead of the dotted-path walker silently clobbering the list into a dict. An out-of-range index is a clearSpecErrornaming the list's length.hiveloom setno longer YAML-parses a value going into a plainstrschema field (e.g.system_prompt); it keeps the CLI text verbatim, so": "and similar can't be misread as a YAML mapping. A validation failure that still occurs now hints at--file.hiveloom mcp serve'srun_*tools return every failure as data (status: "error"with the reason); the traceback goes to stderr vialogging, not to a caller that cannot read it.hiveloom mcp servestartup errors go to stderr with the proper exit code instead of writing JSON into stdout — the MCP protocol channel — which reached the caller as nothing but "Connection closed".- A stdio MCP child now inherits
HIVELOOM_HOME/HIVELOOM_DB(locations, not secrets), so a spawnedhiveloom mcp serveshares the parent's Hive and trust store instead of writing to a different one.
Removed
- The workbench's first-generation direct-run chat. The copilot-first
design replaced it and nothing reached it from the app entry any more: the
Chatthread,Composer,NewRun,NewHarness,HarnessList,BranchDialog, theuseRunworkspace hook,taskGuideandharnessesgrouping, and their tests are gone.MessageBodyandDelegationTrail— the only live exports of that thread — moved todevtools/ui/src/components/messages.tsx. - The API routes that only that UI reached:
POST /api/harnesses(harness creation now goes through the copilot'screate_harnesstool andhiveloom.construct),POST /api/harnesses/{id}/trust, andGET /api/runs/live. The live-run control surface (stop,messages,model,playbook,fork,export,resume) is unchanged.