Skip to content

fix(lore): embed-rebuild refuses when no embedder is wired#96

Open
mvanhorn wants to merge 2 commits into
mathomhaus:mainfrom
mvanhorn:osc/88-embed-rebuild-null-embedder
Open

fix(lore): embed-rebuild refuses when no embedder is wired#96
mvanhorn wants to merge 2 commits into
mathomhaus:mainfrom
mvanhorn:osc/88-embed-rebuild-null-embedder

Conversation

@mvanhorn
Copy link
Copy Markdown
Contributor

Summary

guild lore embed-rebuild previously read meta.embedder_state, saw enabled, then proceeded to wipe lore_vectors and re-encode using embed.NewNullEmbedder() as a placeholder pending QUEST-212. Every encode returned ErrEmbedderDisabled, so coverage stayed at 0% even while lore_health reported the embedder as enabled. Net effect: silent destruction of vectors with no usable rebuild.

This change pulls *EmbedDeps via embedFromDeps(ctx, d) (the same contract appraise_cmd already uses) and refuses to rebuild when the embedder is not wired:

  • meta says disabled OR embedder is not wired -> Disabled: true with a reason explaining the wiring gap, and existing vectors are left untouched.
  • meta says enabled AND embedder is wired -> the real embedder is used for the encode loop and coverage returns >0%.

The previous "placeholder NullEmbedder pending QUEST-212" block is removed.

Acceptance (matches the issue's acceptance section)

  • After embed-rebuild with a wired embedder, vector coverage returns to >0% for projects with active embedders.
  • The "embedder disabled" log line and the lore_health "enabled" report no longer contradict: rebuild now refuses up-front when the embedder isn't wired and emits a clear reason instead of silently no-opping.
  • Regression test covers the rebuild-then-encode happy path AND the new "embedder not wired" refusal path.

Tests

internal/lore/embed_rebuild_test.go (new):

  • TestEmbedRebuild_RefusesWhenEmbedderNotWired - seeds initial vectors through the existing Inscribe path with a fake 384-dim embedder, then calls EmbedRebuildCommand.Handler with stubCommandDeps(nil). Asserts Disabled == true, a non-empty reason, and that the lore_vectors row count is unchanged (no silent wipe).
  • TestEmbedRebuild_HappyPathWithWiredEmbedder - seeds entries without vectors (deps == nil during inscribe), then calls the handler with stubCommandDeps(&EmbedDeps{Embedder: fake, ModelID: "test-model"}). Asserts Disabled == false, Encoded > 0, and that the row count after rebuild matches the reported Encoded.

The fake embedder is a small inline type returning a deterministic 384-dim float32 slice; 384 matches the production BGE dimension so the existing Tx2 vector-write path accepts the encode.

go test ./internal/lore/..., go build ./..., go vet ./...: clean.

Files

  • internal/lore/embed_rebuild_cmd.go - replace the NullEmbedder placeholder with an embedFromDeps + !ed.Enabled() -> Disabled gate.
  • internal/lore/embed_rebuild_test.go (new) - regression tests for both branches.

Closes #88.

Before this change, EmbedRebuildCommand checked meta.embedder_state,
saw "enabled", then proceeded to wipe lore_vectors and re-encode using
embed.NewNullEmbedder() as a placeholder. Every encode returned
ErrEmbedderDisabled, so coverage stayed at 0% even while lore_health
reported the embedder as enabled. Net effect: silent destruction of
vectors with no usable rebuild.

This change pulls *EmbedDeps via embedFromDeps(ctx, d) (the same
contract appraise_cmd uses) and refuses to rebuild when the embedder
is not wired:

- meta says disabled OR embedder is not wired -> Disabled: true with
  a reason explaining the wiring gap, and existing vectors are not
  touched.
- meta says enabled AND embedder is wired -> the real embedder is
  used for the encode loop and coverage returns >0%.

The previous "placeholder NullEmbedder pending QUEST-212" block is
removed.

internal/lore/embed_rebuild_test.go adds two table-free tests:
- TestEmbedRebuild_RefusesWhenEmbedderNotWired: seeds initial vectors,
  calls the handler with stubCommandDeps(nil), asserts Disabled=true
  and vector rows still match the pre-call count.
- TestEmbedRebuild_HappyPathWithWiredEmbedder: seeds entries without
  vectors, calls the handler with a wired EmbedDeps holding a 384-dim
  fake embedder, asserts Encoded>0 and lore_vectors row count matches.

Closes mathomhaus#88
@github-actions github-actions Bot added the area: lore Lore knowledge archive label May 20, 2026
ProjectID: pid,
Disabled: true,
Reason: "embedder runtime is not wired into this surface (QUEST-212 pending); embed-rebuild cannot encode and will not wipe existing vectors",
}, nil
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Reason string here is rendered straight into CLI output via embed_rebuild_cmd.go:108 (🔮 [embed-rebuild] skipped: <reason>), so end users running guild lore embed-rebuild will see "(QUEST-212 pending)" in their terminal. Internal quest IDs are meaningless outside this install and shouldn't leak into user-facing artifacts.

Suggest dropping the parenthetical entirely:

Reason: "embedder runtime is not wired into this surface; embed-rebuild cannot encode and will not wipe existing vectors",

Same information, no internal reference. The QUEST-212 cite still belongs in the package comment / commit body if you want to preserve the traceability for future maintainers.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: lore Lore knowledge archive

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[bug] lore embed-rebuild leaves coverage at 0% while lore_health reports embedder enabled

2 participants