Skip to content

mdcode: add the kcmd profiles command and default_profile config - #360

Closed
libei wants to merge 3 commits into
GoogleCloudPlatform:mainfrom
libei:mdcode-profiles-pr3-command-config
Closed

mdcode: add the kcmd profiles command and default_profile config#360
libei wants to merge 3 commits into
GoogleCloudPlatform:mainfrom
libei:mdcode-profiles-pr3-command-config

Conversation

@libei

@libei libei commented Aug 29, 2026

Copy link
Copy Markdown
Collaborator

Third and final PR implementing binding profiles (design: `docs/semantic-model/profiles.md`).

Stacked on #359 (which stacks on #358). Until those merge, this PR's diff also shows their commits. Review #358#359 → this.

Completes the feature with the reporting surface and the CI default.

What changes

  • `kcmd profiles` — lists each of a model's binding profiles and, per profile, its resolved deployment target and sources plus what it cannot answer (the unbound fields and the metrics/relationships that fall with them). Read-only: it merges and prunes each profile the way `push` does but deploys nothing and makes no network call, so coverage can be inspected before choosing a profile.
  • `default_profile` in `catalog.yaml` — the profile a bare `kcmd push` uses when `--profile` is omitted. Precedence: `--profile`, then `default_profile`, then the inline `default` binding. Round-trips through manifest load/save.

Sample output on the guide's example:

```
Model 'commerce' (commerce_eg):
profile 'analytical' (default)
target: //bigquery.googleapis.com/projects/acme-analytics/datasets/sales/propertyGraphs/commerce
sources:
Customer -> acme-analytics.sales.customer
Order -> acme-analytics.sales.orders
cannot answer:
field Customer.availableCredit (unbound)
profile 'operational'
target: //spanner.googleapis.com/.../propertyGraphs/commerce
sources:
Customer -> //spanner.googleapis.com/.../tables/Customer
Order -> //spanner.googleapis.com/.../tables/Orders
cannot answer:
field Customer.lifetimeValue (unbound)
metric avg_lifetime_value (field Customer.lifetimeValue is unbound)
```

Test

  • `tests/tool/profiles.test.ts` — the command's listing, default marking, resolved/normalized sources, withheld coverage; and `default_profile` load/save round-trip.
  • `npx tsc --noEmit` clean; semantic + tool suites green (520 pass).

libei added 3 commits August 29, 2026 21:10
…ader

The binding-profiles guide authors models with a readable surface the loader
did not accept. Teach the loader that surface so a single combined file using
it loads correctly (profiles themselves come next):

- entities: is an alias for datasets:
- a model-level deployment_target: URI folds into the GOOGLE custom_extensions
  block the deploy leg reads (the existing form still works; the two must agree
  when both are present)
- a field expression: may be a one-line string, shorthand for a single
  target-dialect variant
- a field may be marked unbound: true -- declared logically but with no
  physical column under this binding, structurally absent rather than null

Sugars are normalized before schema validation; unbound is carried onto the IR.
A field that is neither bound nor unbound is a load error naming the field.
Adds the mechanism behind binding profiles: one logical model paired at push
time with a chosen physical binding, deployed to BigQuery + Knowledge Catalog as
before. Deploy legs are unchanged; this is loader/push work.

- Profile discovery: SemanticModelLayout reads <model>.profiles/*.yaml beside
  the model (single-level, so a profile file is never taken for a model).
- Merge (resolve_profiles.mergeProfile): overlays a profile onto the logical
  model by name -- entity source, field expression, deployment target -- and
  enforces the binding-only contract (a profile may set physical bindings and
  may leave a field unbound; it may not add/remove elements or change what
  anything means). A field the profile omits is carried through as unbound.
- Availability pruning (resolve_profiles.pruneUnavailable): drops each unbound
  field and everything that depends on it -- a metric that reads it, a
  relationship whose join column is unbound, a cross-entity metric over a
  dropped relationship -- and returns a per-profile report of withheld coverage.
- kcmd push --profile <name>: merges the named profile before load, prunes after,
  then runs the existing validation + deploy. Orthogonal to --target. The
  implicit 'default' profile keeps today's inline single-file behavior.
- Sources may be resource-name URIs: a //bigquery.googleapis.com/... table is
  normalized to project.dataset.table; a non-BigQuery URI (Spanner/AlloyDB/...)
  rides through and is skipped by the BigQuery pre-flight probe.

Tested: mergeProfile (by-name merge, contract violations, omission->unbound,
non-mutation), pruneUnavailable (field/metric/relationship pruning + report),
profile discovery, and an end-to-end run of the guide's three-file example.
Completes binding profiles with the reporting surface and the CI default.

- kcmd profiles: lists each of a model's binding profiles and, per profile, its
  resolved deployment target and sources plus what it cannot answer (the unbound
  fields and the metrics/relationships that fall with them). Read-only -- it
  merges and prunes each profile the way push does but deploys nothing and makes
  no network call -- so coverage can be inspected before choosing a profile.
- default_profile in catalog.yaml: the profile a bare kcmd push uses when
  --profile is omitted. Precedence is --profile, then default_profile, then the
  inline 'default' binding. Round-trips through manifest load/save.

Tested: the profiles command's listing, default marking, resolved sources, and
withheld coverage; default_profile load/save round-trip.
@libei

libei commented Aug 30, 2026

Copy link
Copy Markdown
Collaborator Author

Folding into #358, which now carries the whole binding-profiles feature as one PR (four commits) after end-to-end live validation. Closing to keep a single review thread.

@libei libei closed this Aug 30, 2026
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.

1 participant