Skip to content

ways corpus exits 0 after every embedding pass is killed, writing a corpus with no vectors #358

Description

@aaronsb

What happens

On a macOS box where way-embed is killed on exec, ways corpus --verbose produces this — and exits 0:

WARNING: EN embedding generation failed (signal: 9 (SIGKILL))
Generating combined corpus with English embeddings...
WARNING: combined embedding generation failed (signal: 9 (SIGKILL))
[ways corpus] fitting calibration (ADR-156)
[ways corpus]   lane[en]: 134 similarity pairs via .../minilm-l6-v2.gguf
[ways corpus]   lane[multi]: 134 similarity pairs via .../multilingual-minilm-l12-v2-q8.gguf
[ways corpus] writing manifest
Manifest written: .../embed-manifest.json
[ways corpus] done

Every embedding pass died. Both calibration lanes silently produced nothing (note: no calibration[en] / calibration[multi] lines follow the lane[...] lines). A ways-corpus.jsonl with zero embedding vectors was written, a manifest was stamped over it, and the command reported success.

The downstream symptom is ways match reporting ERROR: embedding engine unavailable — semantic matching silently degraded to keyword-only, with a corpus on disk that looks valid.

Why this matters

ADR-125 names the embedding engine required. The code treats it as optional: run_generate warns and continues, fit_lane maps any failure to "lane left uncalibrated", and run() writes the manifest regardless.

This is also why the original bug report was "ways corpus hangs building the combined corpus". It never hung. It failed instantly and said nothing, three times, then declared success. Diagnosis cost a day.

Suggested shape (needs thought, not a drive-by fix)

A corpus with no vectors should not be written as if valid. But the obvious fix — exit non-zero — is not obviously safe: ways corpus --if-stale --quiet runs on every SessionStart (settings.json), so a hard failure there could break sessions on a machine whose engine is broken, which is exactly the machine that most needs a working session.

Options, roughly:

  1. Exit non-zero when the EN pass fails, and make the SessionStart hook tolerate it.
  2. Keep exit 0, but refuse to overwrite a good corpus/manifest with a vectorless one.
  3. Write the manifest with an explicit "embeddings": false and have the matcher say so loudly, once, rather than per-query.

(2) is the least invasive and preserves the most useful property: a previously-good corpus keeps working when a rebuild is sabotaged.

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions