Releases: Doucs91/hivelore
Release list
v0.62.0 — everything the two 2026-09-05 field reports asked for
Two agents wrote independent reports the same day, from opposite seats — one reviewing and writing
the task sheets, one executing them — and agreed on more than they disagreed. This release works
through their combined list. Nothing here removes a capability; every item removes noise, time, or a
false assurance.
enforce check no longer reports a green it never evaluated
--stage local — the stage the generated CLAUDE.md tells every agent to run before its final
response — skipped the anti-pattern + sensor diff scan and still printed ✓ gate passed. It was
visible only in --json, as an info among six green lines. Twelve PR descriptions in one report
and eight in the other carried "hivelore enforce check: 0 issues" as a guarantee covering the one
check that differentiates the product, which that stage had never run.
local now scans the worktree diff (git diff HEAD — staged AND unstaged, because an agent asks
before it stages) with the same rules the commit hook uses, so what the agent is told matches what
will happen at commit. Evaluations from a preview are logged to the sensor ledger as manual, never
as pre-commit, so prevention statistics stay honest. Where a scan genuinely is deferred — pre-push,
whose commits were each scanned as they were made — the pass line now says
… 0 issue(s), 1 deferred (antipattern-gate) instead of a bare "passed".
briefing-loaded says whose briefing it found
The strict requireBriefingFirst check asked "does any marker in the directory fall inside the 12 h
TTL". An agent that never called get_briefing once across eight PRs passed it eight times, on a
marker another session had left at 23:58 the night before. The finding now names the marker's
provenance and age — briefing-loaded for this session, briefing-marker-present (with "this check
confirms a marker, not that the current agent read anything") for someone else's — and
requireBriefingFirst: "session" makes the strict reading actually strict. enforce check --session
plus $HIVELORE_SESSION_ID / $HAIVE_SESSION_ID / $CLAUDE_SESSION_ID give the CLI a session to
scope to.
hivelore sensors coverage — where a rule can be broken with the gate silent
A "no hardcoded credentials, even in tests" sensor was scoped to frontend/src, backend/src/test/java
while the secret went into docker-compose.yml. GitGuardian caught what the corpus had described
nine days earlier: the right rule, the right file and the right moment all existed, and the scope
kept them apart. A sensor scope should follow the INTENT of the rule, not the directory where the
mistake was first seen — a scope that is too narrow is worse than no sensor, because it reads as
coverage.
The new command runs every regex sensor's own pattern against the whole tracked tree and lists the
matches its scope excludes, widest hole and block sensors first. Documentation is skipped (a content
sensor can never fire on a doc reached through a scope, so it is never a real hole).
The session recap is dated, and expires
## Last session was printed undated at the top of every session. In one repo it was eight days and
thirty PRs old, described a product name that had since been decided and a payment integration that
had since shipped. The briefing now carries as_of, age_days, commits_since and stale, the
header reads ## Last session — 2026-08-27, 8d ago, 30 commit(s) since, and past 7 days or 25
commits the stale claim is replaced by the last five commit subjects — always true, free, and enough
as a safety net. Better nothing than a confident wrong answer.
pr-memory-check: no more comment about the roadmap
- High-churn files are excluded from matching. Four of eight PRs matched
docs/roadmap.mdand
nothing else — four identical comments, enough for a reviewer to stop reading the block. Files
touched by ≥40% of the last 60 commits carry no information in a diff; they are now measured from
the repo's own history (never a guessed filename list) and dropped. With too little history to
measure, nothing is excluded. When only such files matched, no comment is posted at all. - Matching is on path segments, not string suffixes.
changeNorm.endsWith(anchorNorm)matched
any two paths sharing a tail, which is how a twenty-linefrontend/public/images/README.mddrew a
comment about monorepo version tagging. - Sensors armed on the changed files are listed first — the memories that can actually refuse the
change, which is what would have informed the two PRs the old comment got wrong.
finish stops waiting on things that cannot refuse the change
Six times in one week finish blocked on github-actions-pending because a SonarQube workflow — not
a required check — was still running: 5 to 12 minutes each, on pushes whose build, tests and gate had
all passed. Advisory workflows are now advisory whether they failed or have not finished. And a job
the runner never started (an exhausted Actions minutes budget stops every job in an account) is
classified as infrastructure rather than a verdict on the code: reported, never blocking. A run that
failed with steps that ran and did not fail stays blocking — the ambiguous case fails closed.
code-map.json leaves the git tree
It accounted for 40 of one repo's 88 commits, forced eight git stashes in a single day to allow a
branch switch, and rode into three unrelated feature PRs as a 105/75/25-line diff — a machine-written
file in a repo whose own convention says "never commit a generated artifact". It now lives in
.ai/.cache/code-map.json. An existing tracked copy is still read, and is deleted on the next write,
so the migration is one deletion in one commit instead of a file that keeps coming back. hivelore init adds it to .gitignore.
pr-eval-gate stays out of the way until it can fail
Twenty runs, twenty times "score 100/100 · no baseline · regression gate skipped", each paying a cold
npm install -g of the CLI out of the account's Actions minutes. The generated job now checks for
.ai/eval/baseline.json BEFORE installing anything, and says once, as a notice, how to enable it.
Uncaptured failures are surfaced where the agent can still act
The counter existed and was right — seven hard failures never written down in a session that shipped
eight PRs — but it only appeared at finish, when the agent has stopped working. It now runs at
pre-push too, and instead of "call mem_tried" it hands over a hivelore memory tried command
already filled in with the failure the harness observed.
v0.61.1 — the per-tool-call hooks stop costing 2 s each
Two independent field reports on 2026-09-05 (§3 and §7) measured the same thing from two different
seats: hivelore enforce pre-tool-use and hivelore observe each took ~2.3 s per invocation, on a
trivial payload, with user time of 0.29 s. Both hooks run on every Edit, Write and Bash,
so an ordinary session of 200-300 tool calls paid 15 to 20 minutes of pure waiting, in two-second
slices nobody notices one at a time.
It was not Node startup and not corpus loading. readStdin armed a 2 s hard cap
(setTimeout(finish, 2000)) so a stuck hook could never block Claude — but never cleared it. The
payload was read in milliseconds; the timer then held the event loop open for the remaining ~2 s
before the process could exit. The hard cap is still there, now cleared on finish and unref'd, so
it protects against a stuck stdin without charging every healthy call for it.
Measured on the same machine as the reports, same command, same payload:
| Hook | Before | After |
|---|---|---|
hivelore enforce pre-tool-use |
2.39 s | 0.27 s |
hivelore observe |
2.38 s | 0.26 s |
≈ 4.7 s per tool call becomes ≈ 0.53 s — a 9× cut, and the 15-20 minutes per session goes away
without removing a hook, narrowing a matcher, or changing what either hook does. test/hook-latency.test.ts
spawns both hooks and fails if either takes more than 1.5 s to exit, so the leak cannot come back
unseen.
v0.61.0 — Remove the knowledge-layer health score
Asked for by three consecutive field reports (2026-09-01, 09-02 §3.5, 09-04 §6), each saying the same
three things: it moved without a visible cause, nobody knew how to raise it, and it never blocked.
Reading what it computed explains all three. It was 100 − Σ penalties of the findings this invocation produced, with content catches excluded — so:
- an empty repo with no corpus at all scored 100%, the maximum;
- a repo with 44 memories and 16 sensors scored 0% as soon as one
errorfinding with impact 100
fired — an uncommitted file, a red CI. That is the reported "0% with a full corpus": not a bug, the
formula; - the observed "95% then 87%, unchanged corpus" was exactly
100 − 8 (briefing-missing) − 5 (bootstrap-incomplete), the first appearing and disappearing with the freshness of a local marker; - a block sensor catching a real mistake — the knowledge layer doing its job — moved it by zero
points, being classified as a content catch.
It measured how many complaints an invocation produced, and called that the health of the knowledge
layer. A number whose name designates something other than what it measures cannot be acted on.
Removed
- The
knowledge-layer health: N% (target M%)line from everyenforcestage. - The
enforcement-score-below-thresholdfinding (already hidden from the interactive gate in
0.58.0 — which was the admission that it was not useful). enforcement.scoreThresholdfrom the config schema and both posture defaults.scorefrom theenforce --jsonreport, andbaseline_healthfrom the core gate verdict.computeBaselineHealth,buildBaselineHealthFindingand theBaselineHealthtype from
@hivelore/core.
Nothing else changes: what refused a change, and why, is reported exactly as before.
v0.60.1 — finish checks the whole lockstep set
enforce finishnow verifies every publishable package against the registry. It asked about
one representative package, which assumes publication is atomic. It is not:publish:allruns one
pnpm publishper package, so any of them can fail alone (expired OTP, 403) while the others land.
That produces a PARTIAL publish — a registry set that cannot install itself — and nothing watched
for it. On 0.60.0, core/cli/embeddings shipped and@hivelore/mcpdid not; since cli pins its
siblings exactly, everynpm i -g @hivelore/clifailed with
ETARGET No matching version found for @hivelore/mcp@0.60.0, whilefinishreported a bland
"publish is the next step" about core and said nothing about the break.- New
npm-publication-incoherentverdict (warn). The severity split is preserved: nothing
published yet stays informational (the normal state atfinishtime, before publishing), a tagged
version the registry skipped stays a warning, and a partially-published set is now a warning too —
because dependents are broken at that moment. A package the registry could not be reached for is
never counted as behind.
v0.60.0 — Scope, exceptions, and a gate you can actually pass
Continues the 2026-09-02 and 2026-09-04 field reports where 0.59.0 stopped. That release fixed what
the breadcrumb contained; this one fixes what the gate does — the two reports agree that the
damage is no longer missing features but rules that fire where they shouldn't and gates that cannot
be passed, both of which teach agents to ignore enforcement wholesale.
Sensors get a scope (09-02 §3.2, 09-04 §3.1)
sensor.exclude— a per-sensor negation listpathscould not express. A production-only
lesson (no any) keepspaths: ['**']and addsexclude: ['**/*.test.*', '**/__tests__/**', …]
instead of being narrowed file by file. A lesson that IS about tests leaves it empty and still
fires there.propose_sensortakes it and documents when to set it.- Documentation files are out of scope for content sensors. A
.md/.rst/.adocreached
through a wildcard or prefix scope is never matched — example code in prose does not ship. A
sensor that names the doc file exactly still fires on it, so an intentional doc rule works.
Sensors get an exception mechanism (09-04 §3.1)
// hivelore:allow <memory-id> — <reason>waives one line for one sensor. Before this, a false
positive had exactly two outlets: rewrite correct code, or delete the rule — and "a linter with no
exception mechanism ends up disabled" costs the whole rule rather than the one line. The waiver is
end-of-line only (a waiver on the preceding line silently covers the next one, which is how a
one-off becomes a blanket suppression), requires a reason, and must name the rule it excuses.
Every use is reported as asensor-waivedfinding, so the exception stays auditable — and the
block message and theCLAUDE.mdhard-rules block both tell agents it exists.propose_sensornow says when a pattern matches its own memory's prose — the 09-04 false
positive was detectable three days before it fired. It is a caveat, not a rejection: documenting a
rule normally means quoting what it forbids.
enforce finish stops blocking on what it does not own (09-02 §3.3, 09-04 §5)
The exit gate mixed three natures of check and blocked on all of them. Only knowledge blocks now:
bootstrap-incompleteno longer blocksfinish(warns instead). Bootstrapping a repo is a
project, not a precondition for finishing one bugfix — and its prescribed fix is an MCP prompt,
unreachable in exactly the sessions where the MCP layer is down. It made the gate unpassable for
entire sessions, which also discreditedgithub-actions-pass, the one check catching a real agent
defect.- A CI run that failed only on infrastructure steps (artifact upload over the account's storage
quota, cache, toolchain setup) is a warning, not a blocker. The build and the tests ran and passed;
a knowledge tool has no business gating on someone's billing state. Classified from which STEPS
failed — no log download — and any doubt keeps the gate blocking. - Untracked-only worktrees warn instead of blocking. Nothing tracked was modified, so no work is
at risk — and the untracked files were routinely the memoriesmem_savehad just written, the tool
manufacturing its own blocker. Real uncommitted work still blocks.
Generated workflows reach the branches repos actually use (09-04 §4.3)
hivelore-sync.ymlpush triggers are rendered from the repo's own integration branches
(main/master/develop, detected atinit), not hardcoded tomain/master. In a gitflow
repo the sync job simply never ran: two blocking sensors lived in.ai/memories/for six days
without ever reachingCLAUDE.md— rules that failed commits, invisible to every agent expected
to obey them.- The workflow now calls
hivelore bridges sync.synconly touches.ai/; the breadcrumbs
agents actually read are written bybridges sync, which was called nowhere. Both are covered by
the generated-workflow regression tests.
The haive → hivelore rename is finally swept (09-04 §7.5)
Reported three times as a repo bug, because the repo is where everyone looked — hivelore init has
always written a correct project .mcp.json. The failing entry lives in the USER-scope client config
(~/.claude.json and friends), survives reinstalls, and fails with ENOENT: haive-mcp at the start
of every session in every project.
hivelore doctordetects it;--fixremoves it. Only entries whose command is the dead binary
are touched.- A stale
haivekey no longer counts as "already configured" — that check was why setup skipped
writing the working entry, keeping the broken one alive.
Quieter gate (09-02 §3.4)
briefing-missingandbootstrap-incompletejoin the standing-state codes hidden from the
interactive gate (still in doctor/CI/--explain). They never blocked and printed identically on
all ~20 commits of a session — the mechanism by which a tool's output stops being read.
v0.59.0 — Rank the breadcrumb, finish the comment fix, stop breaking git
The 09-02 and 09-04 field reports converged: the one artefact that delivers value — the memory block
injected into CLAUDE.md — was being filled by ls | sort | head, and three shipped defects were
eroding trust around it. This release fixes the highest-yield items from both reports' priority lists.
The breadcrumb is ranked, not alphabetically sorted (§1, §1.1)
- The
CLAUDE.md/AGENTS.md memory list now ranks by value, not filename. Sorting by id (which is
date-prefixed) surfaced the 8 OLDEST memories and buried everything written after day one —
including everyattempt, whose entire worth is stopping a repeat of a known dead end. The order is
now: validated first, thenattempt, thendecision/architecture, then the rest, most-recent
first within each tier. - A block sensor's breadcrumb is now its corrective instruction, not its regex. The full pattern
was ~63% of the breadcrumb budget and taught the agent nothing the hook doesn't already enforce.
Sensors finish learning to read comments (§3.1)
- Multi-line block comments are now stripped before a regex sensor matches. The v0.57.7 fix was
per-line, so a forbidden token on a MIDDLE line of a/* … */block (which starts with neither the
opener nor*) still tripped the sensor — exactly the recurring CSSbg-emerald-600-in-a-comment
false positive. Comment state is now tracked across lines. String literals are still kept, so a
sensor can target a bad literal in a string.
A tool that runs during git commit no longer writes tracked files (§3.1 09-02)
sensor.last_firedis no longer stamped into the memory.md. Writing a git-tracked file from
inside the pre-commit/pre-push hook dirtied the tree mid-commit and aborted agit checkout,
stranding the next command on the wrong branch. Prevention proof lives in the gitignored cache
(usage.json+ the prevention log), where it belongs.
Smaller, sharper (§2.1, §4)
- A blocked gate now names the escape hatch: "Blocked wrongly? Flag it with
report_friction."
Friction was only ever reported into commit messages because the one natural moment — the block —
never mentioned the tool. - The generated
hivelore-sync.ymlis fixed in three places: PR checks (memory-check, stale-check,
eval-gate) now run on every PR, not only PRs intomain/master(gitflow repos merge into
develop, and two blocking sensors stayed invisible for six days because of this);pr-stale-check
now declarescontents: readsoactions/checkoutworks on private repos; and the stale count is
read from the real "N stale" figure instead ofgrep -c stale, which counted the "0 stale" summary
line and posted a false "stale memories detected" alarm on every PR.
v0.58.1 — Recaps keep their history; sensors nudge toward AST
The recap stops forgetting the last three sessions (§5.6)
- A topic-upsert recap now archives the outgoing session into a bounded per-session history instead
of overwriting it. The field report could not tell what the previous three sessions did, because
eachmem_session_endoverwrote the last (revision_count: 12, history gone). The newest recap
still sits at the top — so the briefing head keeps surfacing only the latest goal + next steps
(v0.58.0) — with each prior session kept as one compact dated entry beneath a## Session history
heading, capped at 6. The full record is onemem_get <id>away. Applied to both the MCP and CLI
session-end writers via the purebuildRecapWithHistory.
propose_sensor points at AST for parseable languages (§3.1)
- The
kindguidance now recommendsastfor a parseable language when the engine is available.
A structural AST match can never false-positive on a comment or a string; regex (still the default,
since it needs no extra engine) now strips comments before matching but deliberately keeps string
literals so it can still target a bad literal. The default is unchanged — flipping it to AST would
break sensor creation everywhere the optional@ast-grep/napiengine is not installed — but the
nudge is now explicit rather than buried.
v0.58.0 — Stop the noise
Acts on the "remove / transform" backlog from the 2026-09-01 field report (§5, §7). The theme is
subtraction: every item here removes output that was noise, keeping the signal that earns trust.
Sensor seeds: no more prose-mined patterns (§5.1)
mem_save/mem_triedno longer pre-fill aproposed_sensor_seed. The prose miner produced
unusable patterns 7 times out of 7 in the field, and a visibly wrong first suggestion teaches agents
to stop reading the sensor loop — the most valuable thing Hivelore does. The tools now report the
open loop and route topropose_sensor(which validates) or tohivelore sensors propose --from-fix <ref>, which mines the ACTUAL fix diff — the only trustworthy auto-source, and it is kept. The
gate-miss draft points at the revert diff instead of guessing from the commit subject.
The prevention receipt stops advertising zero (§5.7)
- No PR comment when nothing fired and the window is empty. The pre-commit gate blocks locally, so
by design almost nothing reaches review — yet the receipt posted "No documented sensor fired · Weekly
total: 0" on every PR, including docs-only ones.renderPreventionCommentnow returns empty in that
case and the CI workflow skips the comment. When a sensor actually fires, or the rolling window has
activity, the receipt posts as before.
A quieter interactive gate (§5.4)
enforcement-score-below-thresholdanddecision-coverage-missingno longer print on interactive
runs. They describe the repo's standing baseline, not the change being committed, and were the two
most frequent, least actionable nags. They stay in the report fordoctor, CI, and--explain, and
they never blocked. The/100currency-divisor case below also stops being mislabelled.
Must-read recaps stop costing 3000 tokens per briefing (§5.6)
- The briefing head carries only the recap's goal + next steps, not the whole goal/accomplished/
discoveries/next-steps wall. A session with five briefings paid five times to re-read a recap the
agent had just written. The full recap stays in the corpus, reachable withmem_get <id>.
A narrower brittleness heuristic (§5.3)
- A
/100divisor is no longer flagged as a hardcoded line number. The heuristic that keeps
brittle line-number patterns out of block sensors mistook the decimal base of a minor-currency
conversion for a line number, forcing the author to widen an exact pattern into an approximate one
(1[0-9]{2}, which also accepts/137). A number preceded by a literal/, an escaped\*/\/,
or a[/*]char class is now recognised as arithmetic; a bare quantifier*is not.
v0.57.8 — A gate that names its stage, and a briefing floor that adapts
Two more items from the 2026-09-01 field report: the gate that appeared to print twice (§4.4) and the
semantic ranking that stopped discriminating on a broad corpus (§4.1).
The pass line names its stage
Hivelore gate passed (pre-commit)/(pre-push)(field report §4.4). Onegitaction fires two
hooks — pre-commit then pre-push — each with its own check count. Unlabelled, the two lines read as a
single gate printed twice with mismatched numbers ("7 then 6"), and the report author could not tell
whether it was two hooks, two passes, or a display bug. The stage is now named, so the two lines are
self-explaining.
The briefing floor adapts to the corpus
- Semantic-only hits are floored at the corpus distribution, not an absolute cutoff (field report
§4.1). On ~40 memories cosine scores compress into a narrow band (everything > 0.55), so an absolute
min_semantic_scoreof 0 sorted nothing and the same three or four unrelated memories surfaced at
~0.6 for every task. The default now applies an adaptive floor atmean + ½σof the hit
distribution: it trims the undifferentiated mass on a compressed distribution, always keeps the top
hit, never drops below an explicit caller floor, and does nothing below 12 samples so small corpora
(and the retrieval eval) are untouched. Retrieval recall held at 98% with no new misses.
v0.57.7 — Stop the sensors punishing the docs that explain them
A fourth field report (nine sessions, 40 memories, 16 sensors) found the sensor layer had crossed from
neutral to net-negative: the only three sensors that ever fired all fired on prose — a CSS comment, a
Javadoc, and a correct test — costing a CI cycle each and training the author to stop naming the rule
next to the code it guards. This release fixes the three highest-yield, deterministic items from that
report (§8).
Sensors match code, not the comments that document them
- Regex sensors blank comment spans before matching (field report §3.1). A CSS block comment naming
bg-emerald-600, a Javadoc line namingLocalDate.now(), and a//line comment no longer trip the
sensors that enforce those rules. String literals are left intact on purpose — many sensors
legitimately target a bad literal inside a string (a hardcoded colour class, URL, or secret), so a
className="bg-emerald-600"still fires. Covers C-family, hash, CSS/SCSS, SQL, and HTML/Markdown
comment syntaxes (stripCommentsForScan), reporting the original line.
The CI gate says why it refused
- Enforcement failures now print the findings to the job log and
$GITHUB_STEP_SUMMARY— code,
severity, matched line, and fix — instead of a bareexit 2no one could read (field report §4.2).
Applied to this repo's workflow and to the templatehivelore initwrites into every repo.
Must-read memories arrive with their substance
format: "actions"no longer truncates a memory to its opening sentence (field report §5.2). A
memory that opens with a short delivery accroche ("Lot 8, livré le…") then carries its real content in
the following prose now returns as many paragraphs as fit the budget, not just the first.