Skip to content

feat(cli)!: derive omnigraph embed vectors from the schema's @embed declarations - #952

Draft
ragnorc wants to merge 7 commits into
ModernRelay:mainfrom
ragnorc:claude/embed-schema-authority
Draft

ragnorc wants to merge 7 commits into
ModernRelay:mainfrom
ragnorc:claude/embed-schema-authority

Conversation

@ragnorc

@ragnorc ragnorc commented Oct 12, 2026 •

Copy link
Copy Markdown
Contributor

#929 has merged (d6604a19), and this branch now merges current main. The change is 3f437a66, the conflict resolution is the merge commit 6d0d7ae3, and the review fix (--vector) is 00ad7927.

What & why

omnigraph embed filled vectors from a hand-written spec ({"dimension": N, "types": {"T": {"target": "embedding", "fields": ["title"]}}}, or a seed manifest's embeddings: entry). After #929 that spec restates exactly what the schema already declares: embedding: Vector(N) @embed("title", model="…") gives the target, the source, the dimension and the model. Nothing compared the two, so:

  • a spec naming body for a schema declaring @embed("title"), or a wrong dimension, silently wrote vectors a text nearest() cannot match;
  • unknown spec and manifest keys were ignored (the CLI seed fixture carried an embeddings.model: that did nothing);
  • the recorded @embed(model=…) was never compared with the provider model the pipeline resolves, although the query path refuses that mismatch (resolve_nearest_query_vec).

This PR makes the schema the only statement of what to embed:

  • --schema <schema.pg> replaces --spec. Every @embed declaration in the catalog (NodeType::embed_sources) is a vector the run fills, with that declaration's source property and its Vector(N) dimension. A type can now carry several vectors of different dimensions, which the spec could not express (one global dimension, one target per type).
  • Seed manifests name the schema with schema:. embeddings: is gone, and the manifest now refuses keys it does not read (deny_unknown_fields, nested sections included).
  • One model rule for both sides. EmbeddingConfig::embeds_with_model is the comparison the query path already made; resolve_nearest_query_vec now calls it, and omnigraph embed calls it for every selected vector with a recorded model, before it reads input or creates output.
  • --vector Type.property selects declared vectors (repeatable). A run fills or cleans only the named @embed vectors, and the model check covers only those, so a type declaring vectors of two models is filled by one run per provider while each run leaves the other vector as it was. Each name must be declared, on a type --type selects when both are given, and every --type must keep one. The source, dimension and model still come from the declaration; no spec comes back.
  • More loud failures: a --type naming a type with no @embed vector; a schema with none; and, by line, a selected record without source text whose vector is a required Vector(N) (a load would refuse that record anyway; --clean is unchanged).
  • JSON output: vectors (each processed vector's type, property, source, dimension) and schema replace dimension, which has no single value once a schema declares several vectors.

Why refuse the old spec instead of validating it

Validating a spec against the schema keeps two sources of truth plus a comparison, and the spec format cannot even describe valid schemas. --spec is removed outright, following the precedent of --allow-data-loss (scripts get clap's unknown-argument error; the breaking note says what to do). #929's breaking note shipped in v0.14.0, whose snapshot pins its digest, so this PR no longer edits it; this change's own breaking note says what replaces the spec. Main had already prepared the v0.15.0 snapshot, so, as later notes on main do, this PR regenerates docs/releases/v0.15.0.md (release_notes.py snapshot --replace, the recorded release date) and both notes land in that preview; both were shortened to the composer's 400- and 200-character limits, with the detail in the embeddings guide.

Why a schema file, not the graph's accepted schema

embed is a local-plane command (RFC 0010/0011): it reads and writes files and takes no graph address. RFC 0015 plans omnigraph embed --graph <scope> as a reconciler that writes to the graph, so a read-only graph flag now would collide with it. Seed files are often prepared before init, when no graph exists. omnigraph schema show prints a graph's accepted schema for anyone without the file; the docs say so.

Backing issue / RFC

Checklist

  • Change is focused (one logical change: the schema becomes the offline pipeline's authority)
  • Tests added/updated for behavior changes
  • Public docs updated: docs/user/search/embeddings.md (offline pipeline, seed manifests, the model and required-vector refusals), docs/dev/execution.md, skills/omnigraph/ (SKILL.md, references/search.md, references/commands.md); changelog embed-schema-authority.breaking.md and .fixed.md
  • Reviewed against docs/dev/invariants.md: invariant 8 (integrity failures are loud: model mismatch, unknown manifest keys, and a required vector with no source now fail instead of passing silently) and 12 (one source of truth: the spec that restated the schema is removed). No write-path, publication or storage change.

Local verification

  • Baseline before the change: cargo test -p omnigraph-cli --locked --bin omnigraph embed: 9 passed; cargo test -p omnigraph-cli --locked --test integration -- cli_data::embed_ cli_data::optimize_json_succeeds_on_local_graph: 5 passed.
  • cargo test -p omnigraph-cli --locked: unit 147 passed (6 ignored), integration 295 passed (8 ignored). The first run failed 57 server-backed tests because a -p omnigraph-cli run does not build omnigraph-server; after cargo build -p omnigraph-server --locked, those 57 passed with --exact.
  • cargo test -p omnigraph-engine --locked --test search nearest_string_: 4 passed (the query path's model checks through the shared rule).
  • Red checks: with check_model disabled, a_run_embeds_each_declared_source_at_its_declared_dimension fails; with the required-vector refusal disabled, a_required_vector_without_source_text_is_refused fails. Both pass restored.
  • cargo clippy -p omnigraph-cli -p omnigraph-engine --all-targets --locked -- -D warnings -W clippy::dbg_macro: clean. cargo fmt --all --check: clean.
  • python3 scripts/check-docs.py, bash scripts/check-agents-md.sh, typos on the changed files, python3 scripts/release_notes.py preview --working-tree: clean; the notes render.
  • Not run locally: the canonical workspace graph and the GQT corpus. The engine change is the extracted comparison above, and GQT refuses @embed schemas, so no case reaches this code; CI runs both.

Notes for reviewers

  • Test tier: GQT refuses @embed schemas because they resolve a provider from the process environment, so the regression tests are in-source in crates/omnigraph-cli/src/embed.rs. run_embed_job now takes the client as a parameter, so a whole run is tested against temp files with a mock client that resolves an explicit model: each stored vector must equal embed_query_text(source, N) at the declared dimension, and a provider resolving another model must be refused before the output file exists. One CLI test (cli_data::embed_reads_vectors_from_the_schema_and_refuses_another_model) covers the env-to-refusal wiring and that --spec is no longer accepted.
  • Seed fixture: it now writes schema.pg, and its pre-existing vector is 4-dimensional; it was 2-dimensional under a 4-dimension spec, which a load would have refused.
  • --type refusal is a behavior change: before, a --type naming a type the spec did not cover was a silent no-op.
  • --vector, not --target: --target is the CLI's global target flag. The model refusal now suggests --vector Type.property instead of --type, since --type cannot choose between two vectors of one type.
  • --spec error text: clap's "a similar argument exists: '--select'" tip on the removed flag is unhelpful; I left it rather than add a hidden flag only to refuse it, consistent with --allow-data-loss.
  • Parsing: the schema goes through parse_schema + build_catalog (admission mode), the same path as lint --schema.

ragnorc and others added 5 commits October 9, 2026 14:49
The offline pipeline embedded `type: <Type>\n<field>: <value>` while a text
`nearest($v, $q)` embeds `$q` verbatim, so a record whose source equaled the
query text did not rank at distance zero: unrelated vectors under the mock
provider, a systematic skew under a real model.

`@embed("source")` declares `vector = embed(source)` with one String source
(RFC 0015), and a query string carries no type or field to template, so the
pipeline is the side that changes: it now embeds the source value unchanged,
which is what the query path already does. The spec's `fields` must name
exactly that one property; a record with an absent, null or blank source gets
no vector instead of one embedded from its type name, and a re-embed removes
such a record's stale vector; a non-string source is refused.
The no-source path of `omnigraph embed` kept the target whenever
`embedding_missing` held, which is also true for an empty list: a record with
a blank source and `embedding: []` came out unchanged, with
`cleaned_records: 0`, and its load then failed with "expects 4 dimensions,
got 0". Selecting which records need embedding is not the same contract as
deciding what a record without source text may keep.

The no-source path now removes whatever the target holds (a stale vector, a
null, an empty placeholder) and reports `Removed` exactly when it removed
something, which `--clean` already counts the same way. The existing
`a_record_without_source_text_keeps_no_vector` test covers all three target
values; before this change it failed on the `[]` case with `Unchanged`
versus `Removed`.
…d` declarations

`omnigraph embed` took a hand-written spec (`{"dimension": N, "types": {"T":
{"target": …, "fields": […]}}}`, or a seed manifest's `embeddings:` entry)
that restated what `embedding: Vector(N) @embed("title", model="…")` already
declares, and nothing compared the two. A spec naming another source or
dimension silently wrote vectors a text `nearest()` could not match, unknown
spec keys were ignored, and a recorded `@embed` model was never checked
against the provider, although the query path refuses that mismatch.

The command now takes `--schema <schema.pg>` (a seed manifest names it with
`schema:`) and fills every `@embed` vector the catalog declares, with that
declaration's source and dimension. A type may carry several vectors of
different dimensions, which the spec could not express. `--spec` and the
manifest's `embeddings` entry are removed, and the manifest refuses keys it
does not read.

Before reading input or creating output, a run refuses a recorded model the
provider does not resolve, through `EmbeddingConfig::embeds_with_model`,
which `resolve_nearest_query_vec` now uses too. It also refuses a `--type`
without an `@embed` vector, a schema without one, and, by line, a selected
record without source text whose vector is a required `Vector(N)`. JSON
output reports `vectors` and `schema` in place of `dimension`.

@ragnorc ragnorc left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Recommendation: request changes before merge for the one P2 finding below. This is a COMMENT review because the authenticated account is the PR author.

Reviewed 3f437a6670a60c176968138134959681546b0a50 against its parent 3721e4640c0944c0b45e3cd830b851b8daa103a6 in a clean isolated checkout. This covers the final schema-authority commit. The prerequisite #929 is now merged.

The old file pipeline repeated the schema in an embedding spec. The two could disagree about the source, dimension, or model. This change reads targets from the compiler catalog, checks recorded models, and generates each declared vector. Users must replace --spec and the manifest's embeddings section with a schema path. It remains a file preparation command. Loading the result remains a separate graph operation.

Required change

The model check couples every vector on a node to one provider, even though the schema permits different models per property. Both model choices then refuse, and --type cannot select between properties. The inline comment gives the reproduction and the required regression. Preserve schema authority while allowing a run to select the declared vector it will generate. Do not restore a second source/dimension/model mapping or silently ignore a mismatched target.

Correctness, tradeoffs, and liability

The central fix is sound: remove duplicate declarations at the CLI boundary. The catalog-derived plan covers each node's declarations and dimensions. It uses the same model comparison as text nearest. Strict manifest decoding closes the ignored-key class. Required-vector refusal agrees with the loader.

This removes a configuration format and its drift. It adds an in-memory target plan, a small public model-comparison method, and a changed JSON summary. It adds no durable state, queue, or publication rule. Those costs are justified. Five future vector options can derive from the catalog instead of adding five parallel spec fields. The unresolved target-selection gap would otherwise force users to maintain reduced schema copies, which recreates the drift this change removes.

The intended workload is offline seed preparation. The loop streams records but still makes serial provider calls for each vector it generates. More declared targets can mean more calls. I did not measure throughput. The caller must supply the intended schema file. Existing vectors remain trusted in fill-missing mode; this is not a provenance audit or automatic re-embedding on source changes.

I surveyed the other 29 open PRs. The relevant connections are #793's vector-space contracts, #884's compiler-owned type expansion, #951's proposed views, and the skill edits in #811/#892. Keeping this work in the catalog and offline CLI fits those boundaries. Merge the overlapping skill edits carefully. The planner, merge/cache, deployment/admission, GQT corpus, and release/documentation work does not require another embedding authority. RFC 0015's future graph reconciler can reuse the same declarations and provider rule without moving network calls into ordinary writes.

I consulted the repository guides and the complete relevant Lance type, array, vector-search, and index documentation. I checked stock Lance 11.0.0 source (ab6b5bbe46009ed78746b444df8db59a8bc5d842). OmniGraph maps vectors to FixedSizeList<Float32>; Lance's scanner checks query dimensions. Neither check establishes an embedding model's identity. That remains OmniGraph's responsibility. The current Lance 14 beta does not justify a substrate dependency for this CLI fix. No storage or deployment behavior changes here.

Local validation and before/after proof

All Cargo commands used RUSTUP_TOOLCHAIN=1.97.1, CARGO_TARGET_DIR=/tmp/review-928/target, and RUSTFLAGS='--cfg tokio_unstable --cfg tokio_unstable'.

Command Parent Reviewed head
cargo test -p omnigraph-cli --locked --bin omnigraph embed 9 passed 14 passed
cargo test -p omnigraph-cli --locked --test integration cli_data::embed_ 4 passed 5 passed
cargo test -p omnigraph-engine --locked --test search nearest_string_ Not run 4 passed
cargo test -p omnigraph-cli --locked --test integration cli_data::embed_review_ 4 behavioral failures 4 passed
cargo test -p omnigraph-cli --locked --bin omnigraph review_seed_source_refuses_an_unread_model 1 behavioral failure 1 passed

The last two rows used temporary tests in the existing CLI owners. The four CLI cases were embed_review_schema_owns_source, embed_review_schema_owns_dimension, embed_review_recorded_model_mismatch_refuses, and embed_review_required_vector_without_source_refuses. The parent wrote the wrong source vector, wrote eight dimensions instead of four, accepted another model, and installed a row without its required vector. The fifth test showed that the parent accepted an unread sources.model key. All five passed on the reviewed head.

The same test source and behavioral assertions ran on both commits. One compatibility branch selected the parent's --spec interface or the head's --schema interface from embed --help. An unchanged command would only prove an unknown-flag failure. No compile, setup, skipped, or zero-test result counts as this proof. GQT cannot exercise the file CLI, and its runner explicitly refuses @embed schemas, so the existing Rust owners are appropriate. All temporary source/test edits were restored. No changes were pushed.

cargo fmt --all --check, git diff HEAD^ HEAD --check, changed-file typos, python scripts/check-docs.py (253 files), and bash scripts/check-agents-md.sh passed. The macOS linker emitted a compact-unwind size warning, but tests ran.

CI and limits

The exact head has green Fix Regression Gate and PR Title checks. These are not runtime test evidence. I found no workspace/GQT build checks on this draft head. I did not run the full workspace, live provider calls, cloud tests, or performance measurements. No separate optional finding.

Comment on lines +195 to +198
fn check_model(&self, config: &EmbeddingConfig) -> Result<()> {
for target in self.types.values().flatten() {
if let Some(recorded) = &target.model
&& !config.embeds_with_model(recorded)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

[P2] Let a run select one declared vector when a type uses several models

This loop requires every vector on a node to use the same provider model. The schema permits vec_a: Vector(4)? @embed("text", model="model-a") and vec_b: Vector(4)? @embed("text", model="model-b") on one Doc. With one row containing text: "hello", embed --schema schema.pg --input raw.jsonl --output out.jsonl --type Doc refuses vec_b under model-a and refuses vec_a under model-b. I reproduced both with the mock provider on this head. Changing --type cannot help, and the check also blocks a missing vector when the other model's vector is already present. On parent 3721e464, two runs with a spec selecting each target populated both properties.

Please preserve the ability to process an individual schema-declared target. Its source, dimension, and recorded model must still come from the catalog. A target selector can do that without restoring the parallel spec or adding automatic provider dispatch. Extend the existing CLI embedding case with two differently modeled vectors on one node: run each target with its matching model, preserve the other vector, and refuse a wrong model for the selected target. The invariant belongs at the selected vector, not at the entire node type.

…uthority

Keep main's v0.14.0 note for ModernRelay#929 as released, move the embed summary's
human writer into embed.rs with main's yaml_serde, update main's
embed report test to the schema form, keep main's condensed skill
reference, shorten this change's notes to the composer's limits and
regenerate the v0.15.0 snapshot.
…ed vectors

The model check held every vector on a node type to one provider model,
so a type declaring vectors of two models could be filled by neither run,
and --type could not choose between its properties.

--vector names the declared @embed vectors a run fills or cleans. Each must
be declared, on a type --type selects when both are given, and every
--type must keep one. The source, dimension and model still come from the
declaration, the model check covers only the selected vectors, and the
record's other vectors stay as they were.
@ragnorc

ragnorc commented Nov 8, 2026

Copy link
Copy Markdown
Contributor Author

Addressed the review of 3f437a66, and brought the branch up to date with main.

[P2] Let a run select one declared vector when a type uses several models (crates/omnigraph-cli/src/embed.rs:198): fixed in 00ad792.

  • omnigraph embed --vector Type.property (repeatable) names the declared @embed vectors a run fills or cleans. The source, dimension and model still come from that declaration. No spec, mapping or provider dispatch comes back.

  • check_model now covers only the selected vectors. On your schema, --vector Doc.vec_a under model-a fills vec_a and leaves vec_b as it was, and then --vector Doc.vec_b under model-b fills vec_b and keeps vec_a. Without --vector, every declared vector is still checked, and the refusal now suggests --vector Type.property instead of --type.

  • A --vector that the schema does not declare is refused. So is one on a type that --type leaves out, and so is a --type that keeps no selected vector. A mismatched target is never skipped silently.

  • I named it --vector because --target is the CLI's global target flag.

  • Tests: cli_data::embed_reads_vectors_from_the_schema_and_refuses_another_model now declares embedding (mock-model) and summary (model-b) on one Decision. It checks:

    • each model refused without --vector;
    • the wrong model refused for the selected vector, with no output written;
    • each vector filled by its own model's run;
    • the first vector unchanged after the second run;
    • an undeclared --vector refused.

    The in-source a_vector_selector_keeps_only_declared_vectors owns the selection and refusal rules. Before this commit, the CLI had no --vector flag, so the extended case cannot pass on the parent.

  • Docs: docs/user/search/embeddings.md, skills/omnigraph/references/search.md and references/commands.md. The breaking note names the flag.

Bringing the branch up to date: merge commit 6d0d7ae. The branch still carried #929's original commits, which main holds as the squash d6604a19, so I applied only 3f437a66 onto main and recorded that tree as the merge.

  • fix(cli)!: make omnigraph embed embed the exact @embed source text #929's breaking note shipped in v0.14.0, and that snapshot pins the note's digest. I kept the note as released, so this PR no longer edits it.
  • main moved the summary's human writer into embed.rs and switched to yaml_serde. Both are carried over with this PR's vectors format.
  • main's new commands::local::tests::embed_report_failure_keeps_the_completed_output_file used a spec. It now uses a schema.
  • main condensed the skill's CLI reference. I kept main's version, which no longer lists embed flags.
  • The composer now refuses notes over 400 characters (breaking) and 200 (fixed). Both notes were shortened, and the detail stays in the embeddings guide.
  • main already holds the prepared v0.15.0 snapshot. Like other notes added after it, this PR regenerates docs/releases/v0.15.0.md with release_notes.py snapshot --replace and the recorded date 2026-11-06.

Checks run locally on the merged tree:

  • cargo dev-test CLI lib embed, commands::local, and the integration cases cli_data::embed_ and cli_schema_config::skill: all passed.
  • cargo dev-clippy, cargo fmt --all --check, scripts/check-docs.py and scripts/check-agents-md.sh: clean.

I updated the PR description where decisions changed: the released #929 note, --vector, and the snapshot.

@ragnorc ragnorc left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Recommendation: ready after one mechanical fix (regenerate the v0.15.0 release-note snapshot; see Required). This is a COMMENT review because the authenticated reviewer is the PR author.

Reviewed 00ad792787c4a5a407ada619422acd6efa3c530b in a clean, isolated worktree. The previous review covered 3f437a66. This review covers the delta: the merge 6d0d7ae3 (main at ea4a16d5) and 00ad7927 (--vector Type.property). The merge changes only files this PR already owns.

Why, what, how, impact

Why. The previous review found one defect. The model check held every vector on a node type to one provider model. When one type declares two vectors with different recorded models, no run could fill either vector. --type could not select between them.

What. --vector Type.property (repeatable) names the declared @embed vectors that a run fills or cleans. The source, dimension and model still come from the schema declaration. The model check covers only the selected vectors. Other vectors in the record stay unchanged.

How. EmbedPlan::restrict_to_vectors (embed.rs#L223-L272) filters the catalog-derived plan. It refuses a malformed name, an undeclared vector, a vector on a type that --type excludes, and a --type that keeps no vector. check_model then runs over the filtered plan.

Impact. The P2 is fixed at the layer that owns it: the plan, which is the one source of what a run fills. No second mapping of source, dimension or model comes back. There is no automatic per-model provider dispatch. A type with two models now needs one run per provider, and each run is explicit.

Required before merge

  1. The generated v0.15.0 snapshot conflicts with main. git merge-tree origin/main 00ad7927 shows one conflict: docs/releases/v0.15.0.md. Main regenerated that snapshot after ea4a16d5. Do not merge the file by hand. Merge main, then run python3 scripts/release_notes.py snapshot --target HEAD --date 2026-11-06 --replace and verify. At the reviewed head, verify --version v0.15.0 --target HEAD passes. A simulated squash commit on ea4a16d5 with this diff also passes verify. If v0.15.0 is tagged before this PR merges, remove the snapshot edit. The two changelog.d/embed-schema-authority.* notes then belong to the next cycle. Either way, the breaking --spec removal must ship in a release whose notes name it.

Optional

  1. One owner for "which types this run processes" (inline comment). Row selection reads the raw --type set (EmbedJob::type_filter). The vectors come from the plan. The two can disagree, and the --json summary shows it. With Issue and Tag vectors and three rows (one Issue, two Tag), the same work gives two different reports:

    Arguments selected_records embedded_records vectors
    --vector Issue.embedding 3 1 Issue.embedding
    --type Issue --vector Issue.embedding 1 1 Issue.embedding
    --type Issue 1 1 Issue.embedding

    The cause predates this commit: without --type, rows of a type with no @embed vector also count as selected. --vector makes it easier to reach. A row is processed only when the plan has targets for its type. So select the row from the plan (job.plan.types.contains_key(type_name), then the --select predicates), and remove EmbedJob::type_filter. Then one structure answers both questions, and the next selector (for example a per-field recipe) cannot add a third answer. If you keep the current meaning, document selected_records as "rows of a selected type, whether or not it has a selected vector".

Correctness, tradeoffs and liability

  • Correctness. The refusals are complete for the inputs the flag accepts. split_once('.') is safe: type and property names cannot contain .. The filter runs after restrict_to, so error order is stable: an unknown --type is reported first. Clean mode removes only the selected vectors. Fill-missing and re-embed leave the other vectors byte-identical (verified below). An unselected required vector is not checked. That is correct for a selector, and load still refuses a record without it.
  • Liability added. One repeatable flag, one filter pass (about 50 lines), three error messages and docs/skill lines. No new state, format, protocol or publication rule. The flag names a declared field and restates nothing, so it cannot drift from the schema.
  • Liability removed. Users no longer need a reduced copy of the schema per model. That copy would recreate the drift this PR removes.
  • Five more changes of this kind. Each future per-field option can filter the same plan. EmbeddingConfig::embeds_with_model is the one comparison that the text nearest() and this pipeline share. RFC #1302 (GQ 3) proposes @embed recipes: the model revision plus encoding choices, possibly from a schema default. When that lands, extend embeds_with_model to compare the resolved recipe descriptor, not the model label. Then the CLI and nearest() stay on one rule. --vector selects a field, which is the unit a recipe binds to, so it fits that direction. Today the check compares only the model string. A matching label does not prove one embedding space. That limit predates this PR, and the RFC states it.
  • Workload. This is an offline seed-preparation command. Selecting fewer vectors makes fewer provider calls. Two models now need two passes over the file. That cost is small and explicit. I did not measure throughput.

Other open PRs: only #1302 (the recipe text above) and RFC drafts #884, #1015 and #1101 mention @embed. None conflicts with this flag. The skill edits in references/commands.md and references/search.md match the clap help.

Lance: the change does not touch storage. I read the Lance data-types guide (vectors are FixedSizeList<Float32, n>), the arrays guide, the vector-search quick start and the vector-index format page. Lance checks dimensions and stores no embedding-model identity, so the model and selection rules stay in Omnigraph's catalog and CLI.

Validation

All commands ran in the isolated worktree with CARGO_TARGET_DIR set to a shared warm target. Rust toolchain from rust-toolchain.toml.

Command 6d0d7ae3 (parent) 00ad7927 (head)
cargo test -p omnigraph-cli --locked --lib embed not run 19 passed
cargo test -p omnigraph-cli --locked --test integration cli_data::embed_ not run 5 passed
cargo test -p omnigraph-cli --locked --test integration cli_data::embed_review_two_models_on_one_type_fill_each_vector -- --exact (temporary) 1 failed: Decision.vec_b records @embed(model="model-b"), but the embedding provider resolves to model 'model-a' 1 passed

The temporary test extended the existing cli_data owner. Schema: one Decision with vec_a (model-a) and vec_b (model-b), both from intent. It fills vec_a with the model-a provider, then fills vec_b with the model-b provider from that output, and requires vec_a to stay identical. It used the same assertions on both commits. It selected with --vector when embed --help lists it, and with --type Decision on the parent, which has no other selector. So the parent failure is the behavioral refusal, not an unknown flag. GQT cannot express this: the file pipeline is a CLI process, and the GQT runner refuses @embed schemas. The PR's own extension of embed_reads_vectors_from_the_schema_and_refuses_another_model already covers this case, so no extra test is needed. I restored the temporary edit, and the worktree is clean.

Also passed at the head: cargo fmt --all --check, git diff --check ea4a16d5 HEAD, python3 scripts/check-docs.py (471 files) and python3 scripts/release_notes.py verify --version v0.15.0 --target HEAD. typos was not installed on this host. I did not run it.

CI on this exact head is green, including Test Workspace, Lint (clippy) and Format, GQ Logic Tests and Fix Regression Gate. I did not run the full workspace, live embedding providers or cloud backends.

};
let plan = EmbedPlan::from_schema_file(&schema)?
.restrict_to(&type_filter)?
.restrict_to_vectors(&args.vectors, &type_filter)?;

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

[Optional] The plan is filtered here, but row selection (L468) still reads the raw --type set. The two can disagree. With Issue and Tag vectors and rows Issue, Tag, Tag, --vector Issue.embedding reports selected_records: 3, while --type Issue --vector Issue.embedding reports 1 for the same work (one Issue.embedding filled in both). Without --type, rows of a type with no @embed vector also count. Suggest selecting the row from the plan (job.plan.types.contains_key(type_name), then the --select predicates) and removing EmbedJob::type_filter. Then the plan alone says which types a run processes.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants