Skip to content

Releases: Doucs91/hivelore

v0.62.0 — everything the two 2026-09-05 field reports asked for

Choose a tag to compare

@Doucs91 Doucs91 released this 06 Sep 00:15

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.md and
    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-line frontend/public/images/README.md drew 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

Choose a tag to compare

@Doucs91 Doucs91 released this 05 Sep 22:52

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

Choose a tag to compare

@Doucs91 Doucs91 released this 04 Sep 14:34

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 error finding 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 every enforce stage.
  • The enforcement-score-below-threshold finding (already hidden from the interactive gate in
    0.58.0 — which was the admission that it was not useful).
  • enforcement.scoreThreshold from the config schema and both posture defaults.
  • score from the enforce --json report, and baseline_health from the core gate verdict.
  • computeBaselineHealth, buildBaselineHealthFinding and the BaselineHealth type 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

Choose a tag to compare

@Doucs91 Doucs91 released this 04 Sep 14:09
  • enforce finish now verifies every publishable package against the registry. It asked about
    one representative package, which assumes publication is atomic. It is not: publish:all runs one
    pnpm publish per 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/mcp did not; since cli pins its
    siblings exactly, every npm i -g @hivelore/cli failed with
    ETARGET No matching version found for @hivelore/mcp@0.60.0, while finish reported a bland
    "publish is the next step" about core and said nothing about the break.
  • New npm-publication-incoherent verdict (warn). The severity split is preserved: nothing
    published yet stays informational (the normal state at finish time, 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

Choose a tag to compare

@Doucs91 Doucs91 released this 04 Sep 06:02

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 list paths could not express. A production-only
    lesson (no any) keeps paths: ['**'] and adds exclude: ['**/*.test.*', '**/__tests__/**', …]
    instead of being narrowed file by file. A lesson that IS about tests leaves it empty and still
    fires there. propose_sensor takes it and documents when to set it.
  • Documentation files are out of scope for content sensors. A .md/.rst/.adoc reached
    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 a sensor-waived finding, so the exception stays auditable — and the
    block message and the CLAUDE.md hard-rules block both tell agents it exists.
  • propose_sensor now 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-incomplete no longer blocks finish (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 discredited github-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 memories mem_save had 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.yml push triggers are rendered from the repo's own integration branches
    (main/master/develop, detected at init), not hardcoded to main/master. In a gitflow
    repo the sync job simply never ran: two blocking sensors lived in .ai/memories/ for six days
    without ever reaching CLAUDE.md — rules that failed commits, invisible to every agent expected
    to obey them.
  • The workflow now calls hivelore bridges sync. sync only touches .ai/; the breadcrumbs
    agents actually read are written by bridges sync, which was called nowhere. Both are covered by
    the generated-workflow regression tests.

The haivehivelore 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 doctor detects it; --fix removes it. Only entries whose command is the dead binary
    are touched.
  • A stale haive key 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-missing and bootstrap-incomplete join 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

Choose a tag to compare

@Doucs91 Doucs91 released this 04 Sep 05:07

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 every attempt, whose entire worth is stopping a repeat of a known dead end. The order is
    now: validated first, then attempt, then decision/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 CSS bg-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_fired is 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 a git 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.yml is fixed in three places: PR checks (memory-check, stale-check,
    eval-gate) now run on every PR, not only PRs into main/master (gitflow repos merge into
    develop, and two blocking sensors stayed invisible for six days because of this); pr-stale-check
    now declares contents: read so actions/checkout works on private repos; and the stale count is
    read from the real "N stale" figure instead of grep -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

Choose a tag to compare

@Doucs91 Doucs91 released this 01 Sep 16:51

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
    each mem_session_end overwrote 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 one mem_get <id> away. Applied to both the MCP and CLI
    session-end writers via the pure buildRecapWithHistory.

propose_sensor points at AST for parseable languages (§3.1)

  • The kind guidance now recommends ast for 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/napi engine is not installed — but the
    nudge is now explicit rather than buried.

v0.58.0 — Stop the noise

Choose a tag to compare

@Doucs91 Doucs91 released this 01 Sep 15:49

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_tried no longer pre-fill a proposed_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 to propose_sensor (which validates) or to hivelore 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. renderPreventionComment now 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-threshold and decision-coverage-missing no 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 for doctor, CI, and --explain, and
    they never blocked. The /100 currency-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 with mem_get <id>.

A narrower brittleness heuristic (§5.3)

  • A /100 divisor 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

Choose a tag to compare

@Doucs91 Doucs91 released this 01 Sep 14:18

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). One git action 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_score of 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 at mean + ½σ 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

Choose a tag to compare

@Doucs91 Doucs91 released this 01 Sep 05:35

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 naming LocalDate.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 bare exit 2 no one could read (field report §4.2).
    Applied to this repo's workflow and to the template hivelore init writes 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.