# Agent-Ready Data Platforms: Practitioner Evidence Dossier

**Research snapshot:** October 3, 2026<br>
**Operational question:** How should a data platform expose data, business meaning, quality, freshness, permissions, and tools so that AI assistants and agents can use company data reliably without depending on undocumented human knowledge?

## Executive recommendation and evidence boundary

The central conclusion is straightforward:

> **An agent-ready data platform is not a warehouse with an LLM attached. It is a governed context-and-action plane in which meaning, grain, state, authority, and allowed operations are machine-readable and enforceable.**

The most reliable architecture separates five concerns:

- **Meaning:** entities, grain, relationships, dimensions, governed metrics, glossary terms, and metric versions.

- **State:** freshness, data-quality status, incidents, schema/contract compatibility, and lineage state.

- **Authority:** user/workload identity, row/column controls, data sensitivity, and allowed actions.

- **Discovery:** an authorization-aware catalog and semantic retrieval layer that returns a small, task-relevant subset of the estate rather than dumping the catalog into model context.

- **Execution:** narrow, typed tools—metric retrieval, catalog search, schema lookup, lineage, freshness, quality, validated query execution—with policy enforcement outside model reasoning.

There is strong empirical evidence that real enterprise text-to-SQL remains difficult precisely because schema discovery and grounding are hard at enterprise scale. LinkedIn's production-oriented system builds a knowledge graph from database metadata, query history, wikis, and code, clusters data by team/product area, then retrieves and ranks context before SQL generation; its internal chatbot had more than 300 weekly users, but expert review still judged only 53% of responses correct or close to correct. [1](https://arxiv.org/abs/2507.14372) LinkAlign identifies database retrieval and schema-item grounding as major bottlenecks in large multi-database settings and reached 33.09% on Spider 2.0-Lite at publication time despite specifically optimizing schema linking. [2](https://aclanthology.org/2025.emnlp-main.51/) KaSLA independently finds that both *missing relevant schema* and *including excessive irrelevant schema* hurt downstream SQL generation. [3](https://arxiv.org/abs/2502.12911)

That evidence argues against two common approaches:

- **“Give the model the whole catalog.”** Large irrelevant contexts create both cost and grounding problems; selective retrieval performs better operationally. [4](https://arxiv.org/abs/2501.17174)

- **“Give the model SQL access and let it reason.”** SQL does not encode business meaning, freshness acceptability, metric governance, authorization intent, or whether a technically valid join is semantically valid. Current semantic-layer systems therefore expose entities, relationships, dimensions, measures, metrics, synonyms, and related metadata explicitly rather than expecting consumers to reconstruct them from physical schemas. [5](https://docs.getdbt.com/docs/build/semantic-models)

### What appears to materially matter

The evidence is uneven, and that distinction matters.

<table header-row="true">
	<tr>
		<td>Context element</td>
		<td>Operational value to an agent</td>
		<td>Evidence assessment</td>
	</tr>
	<tr>
		<td>Relevant database/table/column subset</td>
		<td>Prevents grounding against irrelevant schema and reduces context load.</td>
		<td>**Strong empirical.** Schema-linking work directly measures this effect. [2](https://aclanthology.org/2025.emnlp-main.51/)</td>
	</tr>
	<tr>
		<td>Metadata + historical queries + docs/code</td>
		<td>Improves enterprise discovery where physical names alone do not carry business meaning.</td>
		<td>**Production evidence.** LinkedIn uses the combination in an internal deployed system. [1](https://arxiv.org/abs/2507.14372)</td>
	</tr>
	<tr>
		<td>Entity keys and relationships</td>
		<td>Gives a machine enough structure to select joins and reason about entity connectivity.</td>
		<td>**Strong structural evidence; moderate direct agent evidence.** dbt and Snowflake encode these as semantic primitives. [6](https://docs.getdbt.com/docs/build/entities)</td>
	</tr>
	<tr>
		<td>Grain</td>
		<td>Prevents logically valid but analytically wrong aggregation/fanout.</td>
		<td>**Very strong engineering rationale; surprisingly little isolated agent benchmark evidence.** dbt's own semantic-model example explicitly declares “one row per transaction,” while entity type affects join semantics. [5](https://docs.getdbt.com/docs/build/semantic-models)</td>
	</tr>
	<tr>
		<td>Governed metric definition + dimensions + aggregation rules</td>
		<td>Prevents agents from independently reimplementing recognized business metrics.</td>
		<td>**Strong semantic-system evidence; limited independent head-to-head agent evaluation.** [5](https://docs.getdbt.com/docs/build/semantic-models)</td>
	</tr>
	<tr>
		<td>Synonyms/business terminology</td>
		<td>Bridges organization language to semantic objects.</td>
		<td>**Strong product-design evidence; weak isolated causal benchmarking.** Snowflake semantic views support synonyms on semantic objects. [7](https://docs.snowflake.com/en/sql-reference/sql/create-semantic-view)</td>
	</tr>
	<tr>
		<td>Freshness / quality state</td>
		<td>Changes whether an answer should be trusted or produced at all.</td>
		<td>**Strong operational evidence, not primarily a model-performance intervention.** dbt exposes freshness thresholds programmatically; Snowflake exposes machine-readable quality-monitoring results. [8](https://docs.getdbt.com/reference/resource-configs/freshness)</td>
	</tr>
	<tr>
		<td>Owner / support route</td>
		<td>Enables escalation rather than hallucinated remediation.</td>
		<td>**Standard/operational evidence.** ODCS includes team, roles, support and SLA concepts. [9](https://bitol-io.github.io/open-data-contract-standard/v3.0.2/)</td>
	</tr>
	<tr>
		<td>Sensitivity / allowed actions</td>
		<td>Limits damage when model reasoning fails.</td>
		<td>**Strong security evidence.** MCP requires server-side access control/input validation and recommends client-side confirmation, result validation and auditing. [10](https://modelcontextprotocol.io/specification/2026-07-28/server/tools)</td>
	</tr>

</table>

The practical implication is that **agent context should not be a big text blob**. It should be a small signed/versioned context envelope containing the minimum invariants needed for the current task, plus identifiers for retrieving deeper material on demand.

## Task, context and meaning contract

**Deliverable — Agent-task → context requirement map**

The initial context should contain facts that are cheap, stable enough to cache, and dangerous to omit. Large lineage graphs, complete test histories, documentation bodies, and raw samples should stay behind tools and be retrieved only when the task requires them. That matches the empirical lesson from schema-linking research: first locate the relevant region of the information space, then ground more deeply. [2](https://aclanthology.org/2025.emnlp-main.51/)

<table header-row="true">
	<tr>
		<td>Agent task</td>
		<td>Required before acting</td>
		<td>Retrieve on demand</td>
		<td>Correct failure behavior</td>
	</tr>
	<tr>
		<td>**Find the correct dataset**</td>
		<td>domain, entity, grain summary, description, synonyms, certification/status, sensitivity, owner</td>
		<td>detailed schema, usage signals, neighboring lineage, examples</td>
		<td>Return ranked candidates with reasons. Do not manufacture a dataset when confidence is low.</td>
	</tr>
	<tr>
		<td>**Retrieve a governed metric**</td>
		<td>metric ID, current version, definition summary, entity/grain, allowed dimensions, time semantics</td>
		<td>effective historical version, filters, lineage, detailed formula</td>
		<td>Invoke metric interface; do not silently reconstruct a governed metric in SQL.</td>
	</tr>
	<tr>
		<td>**Generate analytical query**</td>
		<td>candidate assets, grain, keys, relationships/cardinality, dimensions, sensitivity</td>
		<td>physical schema, dialect, examples, cost estimate</td>
		<td>Reject unsafe fanout/invalid relationships; dry-run before expensive execution.</td>
	</tr>
	<tr>
		<td>**Explain a metric**</td>
		<td>metric ID/version, owner, definition, dimensions, filters, time semantics</td>
		<td>superseded definitions, glossary references, lineage</td>
		<td>Explain the governed definition and say which version/effective period applies.</td>
	</tr>
	<tr>
		<td>**Identify dependencies**</td>
		<td>canonical asset/metric ID</td>
		<td>lineage graph with direction/depth, jobs, transformations, versions</td>
		<td>Return graph-derived dependencies; do not infer lineage from similar names.</td>
	</tr>
	<tr>
		<td>**Assess freshness**</td>
		<td>expected update policy/SLO</td>
		<td>actual update, event-time watermark, processing time, upstream health</td>
		<td>State whether data meets its contract; propagate upstream degradation.</td>
	</tr>
	<tr>
		<td>**Investigate pipeline failure**</td>
		<td>asset/job ID, owner, current health</td>
		<td>run lineage, incidents, failing checks, recent schema changes, logs</td>
		<td>Distinguish observed evidence from diagnosis; escalate when root cause is uncertain.</td>
	</tr>
	<tr>
		<td>**Retrieve customer/project context**</td>
		<td>entity definition, business key, authorization context</td>
		<td>authorized records/docs, entity relationships</td>
		<td>Query under the caller's entitlement; never broaden scope because the model “needs context.”</td>
	</tr>
	<tr>
		<td>**Feed data into workflow**</td>
		<td>semantic source/version, current state, allowed action, write authorization</td>
		<td>target API/schema, approval state, idempotency state</td>
		<td>Validate source health and destination contract; require separate write authorization.</td>
	</tr>

</table>

This routing model is consistent with deployed enterprise text-to-SQL practice: LinkedIn retrieves ranked context rather than treating the entire lake schema as one prompt, while modern catalogs expose metadata programmatically rather than only through UI documentation. [1](https://arxiv.org/abs/2507.14372)

**Deliverable — Machine-readable context contract**

ODCS demonstrates that machine-readable data contracts can combine identity, domain/data-product metadata, schema, keys, data granularity, classifications, quality rules, SLAs, ownership/team information and implementation details in a structured specification. [9](https://bitol-io.github.io/open-data-contract-standard/v3.0.2/) dbt model contracts separately enforce expected column names/types, while dbt semantic models expose entities, dimensions, measures and metrics. [11](https://docs.getdbt.com/reference/resource-configs/contract)

A practical **agent context contract** should normalize these capabilities rather than force an agent to understand every vendor's internal representation. A suggested portable envelope:

```
yaml

```
`api_version: agent-data-context/v1<br>context_id: ctx_01J...<br>generated_at: 2026-10-03T09:15:00Z<br>authorization_context_id: authz_01J...<br>catalog_snapshot_id: cat_2026-10-03T09:14Z<br><br>asset:<br>  id: urn:company:data:sales:order_lines<br>  kind: dataset<br>  physical_ref: analytics.sales.order_lines<br>  description: Governed order-line fact dataset.<br>  domain: commerce<br>  owner:<br>    team_id: team_commerce_analytics<br>    escalation_ref: oncall_commerce_data<br><br>semantics:<br>  entity: order_line<br><br>  grain:<br>    statement: "Exactly one row per order_id + line_id."<br>    row_entity: order_line<br>    primary_key: [order_id, line_id]<br>    business_keys:<br>      - [order_id, line_id]<br>    uniqueness_enforced: true<br><br>  dimensions:<br>    - id: dim.order_date<br>      field: order_date<br>      type: time<br>      time_semantics: business_event_time<br>    - id: dim.country<br>      field: shipping_country<br>      type: categorical<br><br>  measures:<br>    - id: measure.net_amount<br>      field: net_amount<br>      aggregation: sum<br>      additive_across: [order, customer, country, order_date]<br><br>  metrics:<br>    - id: metric.net_revenue<br>      name: Net Revenue<br>      current_definition_version: "4"<br>      requested_definition_version: "4"<br>      effective_from: 2026-01-01<br>      effective_to: null<br>      supersedes: metric.net_revenue@3<br>      entity: order_line<br>      time_dimension: dim.order_date<br>      allowed_dimensions:<br>        - dim.country<br>        - dim.order_date<br>      tool_ref: metric.get<br><br>  synonyms:<br>    - phrase: revenue<br>      target: metric.net_revenue<br>      ambiguity: high<br>    - phrase: sales<br>      target: metric.net_revenue<br>      ambiguity: high<br><br>relationships:<br>  - id: rel.order_line_to_order<br>    from_entity: order_line<br>    to_entity: order<br>    from_keys: [order_id]<br>    to_keys: [order_id]<br>    cardinality: many_to_one<br>    fanout_risk: false<br>    approved: true<br><br>state:<br>  freshness:<br>    expected_schedule: "0 * * * *"<br>    expected_by: PT15M<br>    actual_last_processing_time: 2026-10-03T09:08:14Z<br>    max_event_time: 2026-10-03T09:00:00Z<br>    event_time_lag: PT8M14S<br>    slo: PT75M<br>    status: healthy<br><br>  quality:<br>    status: healthy<br>    evaluated_at: 2026-10-03T09:10:22Z<br>    blocking_failures: []<br>    nonblocking_failures: []<br>    incident_ids: []<br><br>governance:<br>  sensitivity:<br>    classification: internal<br>    restricted_columns:<br>      - customer_email<br><br>  enforcement:<br>    row_policy_ref: rp_sales_region_v7<br>    column_policy_ref: cp_customer_pii_v12<br><br>  allowed_actions:<br>    - discover<br>    - inspect_schema<br>    - query_read<br>    - get_metric<br><br>contract:<br>  id: dc_sales_order_lines<br>  version: "7.2"<br>  compatibility: backward<br>  schema_hash: sha256:...<br>  status: active<br><br>lineage:<br>  summary:<br>    upstream_count: 4<br>    downstream_count: 12<br>    last_observed_run_id: ol_run_...<br>  tool_ref: lineage.get<br><br>tools:<br>  schema: asset.get_schema<br>  metric: metric.get<br>  quality: data.check_quality<br>  freshness: data.check_freshness<br>  lineage: lineage.get<br>  query: query.execute_readonly<br><br>provenance:<br>  metadata_sources:<br>    - semantic_layer<br>    - data_contract<br>    - catalog<br>    - quality_monitor<br>  context_hash: sha256:...<br>`
```
```

The important design choice is not this exact YAML. It is that **the model does not have to infer any correctness-critical invariant from prose when the platform already knows it**.

I would make the following contract fields mandatory:

`asset.id`, `domain`, `owner`, `grain.statement`, keys, relationships/cardinality, sensitivity, allowed actions, contract version/status and a state timestamp.

Metrics additionally require immutable `metric.id`, definition version, effective interval, semantic entity, time dimension, aggregation semantics and a callable metric interface.

Freshness and quality additionally require **both expected state and observed state**. “Updated daily” is documentation; `expected_by`, actual watermark and health status are machine-operable facts.

**Deliverable — Grain / entity / relationship model**

Grain should be mandatory because a schema can be syntactically understood yet analytically misused.

dbt's semantic model explicitly supports primary, unique, foreign and natural entity types and constructs joins using the entity graph. Its example semantic model documents “one row per transaction,” marks `transaction_id` as a primary entity and `customer_id` as foreign. [5](https://docs.getdbt.com/docs/build/semantic-models) Snowflake semantic views likewise encode logical tables, primary/unique keys and named relationships rather than leaving join logic entirely implicit. [7](https://docs.snowflake.com/en/sql-reference/sql/create-semantic-view)

A usable machine model needs four distinct concepts:

<table header-row="true">
	<tr>
		<td>Concept</td>
		<td>Required representation</td>
		<td>Why the agent needs it</td>
	</tr>
	<tr>
		<td>**Row grain**</td>
		<td>`one row = &lt;entity/event&gt;`, preferably plus composite key</td>
		<td>Establishes what counting rows actually means.</td>
	</tr>
	<tr>
		<td>**Entity identity**</td>
		<td>primary, business and alternate keys</td>
		<td>Establishes deduplication and entity-resolution semantics.</td>
	</tr>
	<tr>
		<td>**Measure additivity**</td>
		<td>aggregation operator plus dimensions/time axes over which it remains valid</td>
		<td>Prevents summing ratios, balances or already-aggregated facts incorrectly.</td>
	</tr>
	<tr>
		<td>**Join contract**</td>
		<td>key pair, cardinality, optionality, temporal conditions, fanout classification</td>
		<td>Prevents many-to-many explosions and semantically invalid joins.</td>
	</tr>

</table>

For each relationship, encode one of:

`one_to_one`, `many_to_one`, `one_to_many`, `many_to_many_bridge`, `temporal_asof`.

Then add:

```
yaml

```
`fanout_behavior: safe | deduplicate_required | aggregate_before_join | prohibited<br>`
```
```

A relationship without a known cardinality should be treated as **unknown**, not optimistically assumed safe.

Representation should be redundant in a useful way:

- **Catalog:** searchable grain statement, keys and relationship summary.

- **Semantic layer:** authoritative entities, join relationships, dimensions and aggregation semantics.

- **Contract:** compatibility-critical guarantees—keys, uniqueness expectations, grain and relationship invariants.

- **Physical schema description:** concise human explanation, but not the only source.

The semantic layer should be the preferred source when representations conflict because it is explicitly designed to encode analytical semantics; the conflict itself should nevertheless surface as a governance issue rather than be silently resolved by the model. dbt and Snowflake both treat entities/relationships as first-class semantic objects. [6](https://docs.getdbt.com/docs/build/entities)

**Deliverable — Semantic / metric-layer integration**

The preferred flow is:

`question → semantic interpretation → metric/entity resolution → governed metric invocation → result`

not:

`question → model invents SQL formula → warehouse`.

dbt's Semantic Layer/MetricFlow models semantic graphs made of entities, dimensions and measures/metrics, while Snowflake semantic views expose logical tables, facts, dimensions, metrics, relationships and synonyms. [5](https://docs.getdbt.com/docs/build/semantic-models)

For a governed metric, an agent should retrieve at least:

```
text

```
`metric_id<br>display_name<br>definition_version<br>effective_from / effective_to<br>description<br>entity / grain<br>measure<br>aggregation<br>allowed dimensions<br>required/default filters<br>time dimension<br>time grain constraints<br>non-additive dimensions<br>owner<br>quality/freshness state<br>`
```
```

The agent should invoke something like:

```
json

```
`{<br>  "metric_id": "metric.net_revenue",<br>  "definition_version": "4",<br>  "time_range": {<br>    "from": "2026-09-01",<br>    "to": "2026-09-30"<br>  },<br>  "dimensions": ["dim.country"],<br>  "filters": []<br>}<br>`
```
```

and receive structured provenance alongside values:

```
json

```
`{<br>  "metric_id": "metric.net_revenue",<br>  "definition_version": "4",<br>  "effective_definition": {<br>    "from": "2026-01-01",<br>    "to": null<br>  },<br>  "result_id": "metric_result_...",<br>  "query_id": "warehouse_query_...",<br>  "freshness_status": "healthy",<br>  "quality_status": "healthy",<br>  "rows": [...]<br>}<br>`
```
```

Use free-form SQL as a **fallback capability** for novel analysis, not as the default implementation mechanism for already-governed metrics.

**Metric versioning is the missing reliability primitive in many implementations.** The reviewed dbt and Snowflake semantic documentation provides rich definition machinery, but the operational need to resolve a metric definition according to its *business-effective period* should not be left to Git history or whichever definition is deployed today. [5](https://docs.getdbt.com/docs/build/semantic-models)

Treat these dimensions independently:

- **Definition version:** immutable identifier, e.g. `net_revenue@4`.

- **Deployment time:** when version 4 became available to query.

- **Business effective period:** historical period for which version 4 is the appropriate definition.

- **Supersession:** which definition it replaces and why.

Example:

```
yaml

```
`metric_id: metric.active_customer<br><br>versions:<br>  - version: 2<br>    effective_from: 2024-01-01<br>    effective_to: 2025-12-31<br>    definition: "customer with a completed order in previous 90 days"<br><br>  - version: 3<br>    effective_from: 2026-01-01<br>    effective_to: null<br>    definition: "customer with a completed non-refunded order in previous 90 days"<br>    supersedes: 2<br>`
```
```

For a 2024 analysis, version 2 should be used unless the user explicitly asks for a **restatement under today's definition**.

For a time range spanning a definition boundary, the tool should return one of three explicit modes:

`historically_correct`, `restated_current_definition`, or `not_comparable`.

Never silently mix them.

**Deliverable — Glossary / synonym model**

Synonyms belong to semantic objects, not only to an embedding index. Snowflake semantic views explicitly support synonyms, demonstrating that these can be represented alongside metrics/dimensions rather than as informal documentation. [7](https://docs.snowflake.com/en/sql-reference/sql/create-semantic-view)

Recommended object:

```
yaml

```
`term_id: glossary.arr<br>preferred_label: Annual Recurring Revenue<br>synonyms:<br>  - ARR<br>  - recurring revenue<br>  - annualized recurring revenue<br>domain: finance<br>maps_to:<br>  - metric.arr<br>ambiguities:<br>  - phrase: revenue<br>    candidates:<br>      - metric.revenue<br>      - metric.net_revenue<br>      - metric.arr<br>owner: finance_analytics<br>status: approved<br>`
```
```

Do not force ambiguous phrases into a single canonical mapping. `"customer"`, `"revenue"`, `"active"`, `"conversion"`, and `"project"` often need domain or task context.

Maintenance should follow semantic-code ownership:

- domain data/analytics owners approve meanings;

- usage/query logs propose candidate synonyms;

- AI can propose glossary changes but should not auto-publish them;

- every glossary edit is versioned and should trigger relevant discovery evaluations.

**Deliverable — Data-contract integration**

A data contract is useful to an agent when its guarantees are **queryable at execution time**, not merely stored in a repository.

ODCS already provides a broad machine-readable structure that can express schema, business meaning, granularity, classifications, quality rules, SLAs and ownership/team information. [9](https://bitol-io.github.io/open-data-contract-standard/v3.0.2/) dbt contracts enforce physical column/type expectations for models. [11](https://docs.getdbt.com/reference/resource-configs/contract)

Expose contracts through:

```
text

```
`contract.get(asset_id, version?)<br>contract.check(asset_id)<br>contract.diff(asset_id, from_version, to_version)<br>contract.impacted_consumers(contract_change_id)<br>`
```
```

The normalized response should include:

`schema`, `types`, `grain`, key constraints, quality expectations, freshness expectations, owner, status, version, compatibility result, violations, and affected downstream objects.

I would make compatibility explicit:

```
yaml

```
`compatibility:<br>  mode: backward<br>  status: compatible<br>  violations: []<br>`
```
```

For analytics data, compatibility checks need to go beyond physical schema:

- changing a numeric type may be a schema break;

- changing row grain is an **analytical break** even if all columns remain;

- changing join cardinality is an analytical break;

- changing metric semantics is a semantic break;

- tightening permissions is a security-impacting change.

The agent should therefore consume **contract compatibility**, not independently decide whether a change is safe.

## Discovery, data products and tool interfaces

**Deliverable — Lineage interface**

OpenLineage provides a useful transport model: metadata facets attach to runs, jobs and datasets; datasets can carry schema/version information, jobs can carry source-code location/version, and every run has a unique run identifier. [12](https://openlineage.io/docs/spec/facets/) Its run lifecycle associates a run with its job, inputs and outputs. [13](https://openlineage.io/docs/spec/run-cycle/)

That is enough structure to make lineage a callable graph rather than a screenshot.

Do **not** place the entire lineage graph in every prompt.

Base context:

```
yaml

```
`lineage:<br>  immediate_upstream_count: 3<br>  immediate_downstream_count: 11<br>  degraded_upstream_count: 1<br>  latest_producing_run_id: ...<br>`
```
```

On-demand tool:

```
json

```
`{<br>  "asset_id": "urn:company:data:sales:order_lines",<br>  "direction": "upstream",<br>  "depth": 2,<br>  "include_jobs": true,<br>  "include_metrics": true,<br>  "as_of": "2026-10-03T09:15:00Z"<br>}<br>`
```
```

Response nodes should expose stable IDs, object types, transformation/job IDs, observed timestamps, versions and health.

Use cases require different graph projections:

- **source selection:** nearby certified ancestors/descendants;

- **impact analysis:** downstream graph, usually deeper;

- **incident diagnosis:** upstream graph + recent runs + health;

- **provenance:** exact path that produced the result, not merely every theoretical dependency.

The relevant distinction is **structural lineage** versus **execution lineage**. “Table B is derived from A” is useful for impact analysis; “this result came from B produced by run R using code version V” is necessary for provenance. OpenLineage's run/job/dataset model supports the latter distinction. [14](https://openlineage.io/docs/spec/object-model/)

**Deliverable — Quality / freshness interface**

Freshness must expose expected and observed state separately. dbt's current freshness configuration supports warning/error thresholds and loaded-at semantics rather than only a textual schedule. [15](https://docs.getdbt.com/reference/resource-configs/freshness) Machine-readable data-quality systems can likewise publish observed values, expected ranges and anomaly status; Snowflake's monitoring metadata is one current example. [16](https://docs.snowflake.com/en/sql-reference/local)

Recommended normalized freshness result:

```
json

```
`{<br>  "asset_id": "...",<br>  "evaluated_at": "2026-10-03T09:20:00Z",<br><br>  "expectation": {<br>    "schedule": "hourly",<br>    "warn_after": "PT75M",<br>    "error_after": "PT120M"<br>  },<br><br>  "observed": {<br>    "last_processing_time": "2026-10-03T08:06:00Z",<br>    "max_event_time": "2026-10-03T07:58:00Z",<br>    "processing_lag": "PT74M",<br>    "event_time_lag": "PT82M"<br>  },<br><br>  "status": "degraded",<br>  "reason_codes": [<br>    "EVENT_TIME_WARN_THRESHOLD"<br>  ]<br>}<br>`
```
```

Keep **event time** and **processing time** distinct. A pipeline can finish recently while containing old business events; conversely, late-arriving events can make event-time completeness different from pipeline execution recency.

Recommended quality result:

```
json

```
`{<br>  "status": "degraded",<br>  "evaluated_at": "...",<br>  "checks": [<br>    {<br>      "check_id": "order_id_unique",<br>      "severity": "blocking",<br>      "status": "failed",<br>      "observed": 0.982,<br>      "expected": 1.0<br>    }<br>  ],<br>  "active_incidents": ["inc_7391"]<br>}<br>`
```
```

The model should not decide from prose what `degraded` means. Attach policy:

<table header-row="true">
	<tr>
		<td>State</td>
		<td>Default analytical behavior</td>
	</tr>
	<tr>
		<td>`healthy`</td>
		<td>Proceed.</td>
	</tr>
	<tr>
		<td>`warning`</td>
		<td>Proceed and disclose relevant caveat when it could affect interpretation.</td>
	</tr>
	<tr>
		<td>`degraded`</td>
		<td>Prefer healthy equivalent source; otherwise qualify prominently.</td>
	</tr>
	<tr>
		<td>`failed`</td>
		<td>Do not use for governed answer unless policy explicitly allows degraded operation.</td>
	</tr>
	<tr>
		<td>`unknown`</td>
		<td>Treat as lack of assurance, not as healthy.</td>
	</tr>
	<tr>
		<td>critical blocking test failed</td>
		<td>Refuse affected result or escalate.</td>
	</tr>

</table>

Freshness propagation should be computed from the lineage DAG, not from the LLM. A derived asset cannot credibly be “fully healthy” if a required upstream source is outside its acceptable freshness/error policy unless the derivation contract explicitly tolerates that source condition. This propagation is an engineering policy built on lineage and freshness primitives, not something standardized by OpenLineage or dbt. [13](https://openlineage.io/docs/spec/run-cycle/)

A useful result therefore includes:

```
yaml

```
`local_status: healthy<br>effective_status: degraded<br>degradation_source:<br>  asset_id: upstream.crm_events<br>  reason: freshness_breach<br>`
```
```

**Deliverable — Catalog / discovery patterns**

Catalog discovery should be staged:

`authorization/domain routing → hybrid retrieval → governance filters → graph/usage enrichment → reranking → detailed context retrieval`

OpenMetadata illustrates that production catalogs already combine keyword search, asset associations such as lineage/frequently joined assets, complex queries and programmatic metadata APIs. [17](https://docs.open-metadata.org/v1.13.x/features) Empirical schema-linking work supports multi-stage retrieval and isolation of irrelevant schema in large environments. [2](https://aclanthology.org/2025.emnlp-main.51/)

Comparison:

<table header-row="true">
	<tr>
		<td>Signal</td>
		<td>Good at</td>
		<td>Weakness</td>
	</tr>
	<tr>
		<td>**Lexical search**</td>
		<td>Exact names, acronyms, business terms</td>
		<td>Misses paraphrases and undocumented terminology.</td>
	</tr>
	<tr>
		<td>**Semantic search**</td>
		<td>Natural-language ↔ descriptions/similar concepts</td>
		<td>Can conflate semantically similar but operationally different assets.</td>
	</tr>
	<tr>
		<td>**Metadata filters**</td>
		<td>domain, type, certification, owner, health</td>
		<td>Depends on maintained metadata.</td>
	</tr>
	<tr>
		<td>**Usage signals**</td>
		<td>identifying assets humans actually use</td>
		<td>Can reinforce legacy/bad practice and popularity bias.</td>
	</tr>
	<tr>
		<td>**Lineage graph**</td>
		<td>trusted neighborhood, upstream/downstream relationships</td>
		<td>Poor primary search signal for novel concepts; graphs can be huge.</td>
	</tr>
	<tr>
		<td>**Domain routing**</td>
		<td>narrows large catalog early</td>
		<td>Wrong domain classification can hide the correct asset.</td>
	</tr>
	<tr>
		<td>**Curated tools/data products**</td>
		<td>high reliability for recurring tasks</td>
		<td>Covers less of the long tail.</td>
	</tr>

</table>

My default ranking would prioritize:

- authorization,

- task/domain/entity match,

- certification/governance status,

- semantic + lexical relevance,

- health,

- relevant usage,

- lineage proximity.

Usage should never override governance. The most-used raw table is still the wrong source when a certified governed metric exists.

**Large-catalog failure modes are evidence-backed.** LinkAlign explicitly targets thousands of fields and reports database retrieval plus schema grounding as separate problems. [2](https://aclanthology.org/2025.emnlp-main.51/) Extractive schema-linking work notes that real schemas can contain hundreds of columns and that including the whole schema can be impossible or unnecessarily expensive, while focusing on relevant schema can improve accuracy. [18](https://arxiv.org/abs/2501.17174) LinkedIn addresses this operationally by clustering tables by team/product area before retrieval. [1](https://arxiv.org/abs/2507.14372)

**Deliverable — AI-facing data-product patterns**

A useful AI-facing data product is not “a table plus a description.” It should expose a **capability bundle**:

```
text

```
`Product identity<br>    stable product/domain/entity IDs<br><br>Meaning<br>    grain<br>    entities/keys<br>    dimensions<br>    relationships<br>    metrics<br>    glossary<br><br>Data<br>    governed physical sources<br>    optional approved samples<br><br>State<br>    freshness<br>    quality<br>    incidents<br>    contract status<br><br>Governance<br>    sensitivity<br>    owner<br>    authorization policy<br>    allowed actions<br><br>Interfaces<br>    metric endpoint<br>    schema endpoint<br>    query templates<br>    discovery metadata<br>    lineage endpoint<br>    read tool<br><br>Evidence<br>    verified/example queries<br>    result provenance<br>`
```
```

Snowflake's semantic-view design is informative because it groups logical tables, relationships, facts, dimensions, metrics, synonyms and even verified queries into semantic metadata. [7](https://docs.snowflake.com/en/sql-reference/sql/create-semantic-view) ODCS contributes complementary ownership, schema, quality, SLA and contract information. [9](https://bitol-io.github.io/open-data-contract-standard/v3.0.2/)

A practical pattern is to expose three interface levels:

**Governed API:** metric/entity retrieval for known business questions.

**Constrained analytical query:** semantic query or compiled SQL using approved relationships.

**Raw SQL fallback:** accessible only when the request genuinely exceeds governed interfaces.

This gives agents an escape hatch without making the escape hatch the default.

**Deliverable — Agent tool catalogue and design rules**

Recommended read-side tool set:

<table header-row="true">
	<tr>
		<td>Tool</td>
		<td>Purpose</td>
		<td>Important parameters</td>
	</tr>
	<tr>
		<td>`catalog.search`</td>
		<td>find semantic/data objects</td>
		<td>`query`, `domain`, `entity`, `types`, `certified_only`, `health`, pagination</td>
	</tr>
	<tr>
		<td>`asset.get`</td>
		<td>canonical metadata</td>
		<td>`asset_id`, requested field groups</td>
	</tr>
	<tr>
		<td>`asset.get_schema`</td>
		<td>physical schema</td>
		<td>`asset_id`, version</td>
	</tr>
	<tr>
		<td>`metric.resolve`</td>
		<td>resolve names/synonyms/ambiguity</td>
		<td>phrase, domain, time context</td>
	</tr>
	<tr>
		<td>`metric.get`</td>
		<td>governed metric result</td>
		<td>stable metric ID/version, dimensions, filters, time range</td>
	</tr>
	<tr>
		<td>`lineage.get`</td>
		<td>graph traversal</td>
		<td>asset ID, direction, depth, node types</td>
	</tr>
	<tr>
		<td>`data.check_quality`</td>
		<td>current quality</td>
		<td>asset ID, as-of timestamp</td>
	</tr>
	<tr>
		<td>`data.check_freshness`</td>
		<td>expected/actual recency</td>
		<td>asset ID, as-of timestamp</td>
	</tr>
	<tr>
		<td>`query.validate`</td>
		<td>syntax, relationships, policy, estimated cost</td>
		<td>query/plan</td>
	</tr>
	<tr>
		<td>`query.execute_readonly`</td>
		<td>constrained SQL fallback</td>
		<td>validated plan ID; preferably not arbitrary SQL-only input</td>
	</tr>
	<tr>
		<td>`docs.get`</td>
		<td>authoritative documentation</td>
		<td>doc/term ID, section</td>
	</tr>
	<tr>
		<td>`incident.get`</td>
		<td>pipeline/data incident</td>
		<td>incident/run ID</td>
	</tr>

</table>

MCP's July 28, 2026 specification supports discoverable tools with names, descriptions, JSON Schema inputs, optional output schemas and structured results. Servers must validate inputs, implement access control, rate-limit calls and sanitize outputs; clients should validate results, time out calls and log tool usage. [10](https://modelcontextprotocol.io/specification/2026-07-28/server/tools) Tool execution errors are explicitly intended to carry actionable feedback that models can use to self-correct. [10](https://modelcontextprotocol.io/specification/2026-07-28/server/tools)

Tool-schema rules:

- **Stable opaque IDs over names.** Search can accept natural language; execution should use resolved IDs.

- **Enums over unconstrained strings** where the platform knows valid options.

- **Explicit units/timezones/time semantics.**

- **No overloaded boolean soup.** Prefer distinct operations with bounded responsibilities.

- **Structured output schemas**, including state/provenance, rather than prose-only responses.

- **Actionable errors:** `INVALID_DIMENSION_FOR_METRIC`, not “query failed.”

- **Machine-readable retryability:** `retryable: true|false`.

- **Policy errors distinct from data errors.**

- **Pagination and bounded graph depth.**

- **Cost/time/result limits included in the contract.**

- **Schema/version in every tool definition**, so changes can drive regression testing.

Example error:

```
json

```
`{<br>  "error_code": "UNSAFE_JOIN_FANOUT",<br>  "message": "Joining order_lines to promotions directly is many-to-many.",<br>  "retryable": true,<br>  "suggested_action": {<br>    "tool": "lineage.get",<br>    "relationship_id": "rel.order_promotion_bridge"<br>  }<br>}<br>`
```
```

MCP also states that a tool set may vary according to the authorization presented on each request, which is useful for permission-aware discovery. [10](https://modelcontextprotocol.io/specification/2026-07-28/server/tools)

**Do small composable tools outperform one generic warehouse tool?**

I found strong architectural reasons but **not compelling direct empirical evidence that tool granularity alone universally improves agent accuracy**.

So the evidence-based wording should be:

> Prefer composable tools because they make authorization, parameter validation, failure semantics, observability and safe defaults narrower and testable—not because a benchmark has proved that “more tools beats one SQL tool.”

A generic `execute_sql(sql)` tool remains valuable as an expert fallback. It should not be the interface through which catalog search, semantic metrics, freshness, quality, lineage and authorization are all implicitly reconstructed.

## Identity, permissions, protocols and write boundaries

**Deliverable — Identity / permissions architecture**

The best default for interactive enterprise assistants is a **dual identity**:

```
text

```
`workload identity = which agent/tool service is acting<br>user subject       = on whose behalf it is acting<br>`
```
```

Then enforce the intersection:

```
text

```
`effective permission<br>    = workload permission<br>    ∩ delegated-user permission<br>    ∩ tool-specific policy<br>    ∩ data-platform policy<br>`
```
```

OAuth's fundamental model allows clients to access resources either on behalf of a resource owner or on their own behalf. [19](https://www.rfc-editor.org/info/rfc6749/) The current MCP HTTP authorization specification similarly defines the client as acting on behalf of a resource owner and requires audience/resource-bound access-token validation when authorization is used. [20](https://modelcontextprotocol.io/specification/2026-07-28/basic/authorization) MCP also recommends least-privilege scope selection. [20](https://modelcontextprotocol.io/specification/2026-07-28/basic/authorization)

Identity modes:

<table header-row="true">
	<tr>
		<td>Mode</td>
		<td>Best use</td>
		<td>Security/audit property</td>
		<td>Main risk</td>
	</tr>
	<tr>
		<td>**Agent/service identity only**</td>
		<td>narrow background automation</td>
		<td>simple operational identity</td>
		<td>every user can accidentally inherit bot's broad access</td>
	</tr>
	<tr>
		<td>**End-user/delegated identity**</td>
		<td>interactive company-data assistant</td>
		<td>warehouse sees user entitlement</td>
		<td>token propagation/integration complexity</td>
	</tr>
	<tr>
		<td>**Workload + delegated subject**</td>
		<td>preferred enterprise pattern</td>
		<td>audit can capture both software actor and human subject</td>
		<td>architecture is more involved</td>
	</tr>
	<tr>
		<td>**Broad shared warehouse credential**</td>
		<td>almost never</td>
		<td>easy prototype</td>
		<td>destroys per-user authorization and attribution</td>
	</tr>

</table>

For noninteractive jobs, a service identity is appropriate—but restrict it to specific domains/actions and treat it as an explicit privileged workload.

Restrictions should be enforced **outside the model**:

```
text

```
`Model<br>  ↓ requests tool<br>Tool gateway<br>  ↓ authentication + authorization + policy + limits<br>Warehouse / semantic service<br>  ↓ native RLS/CLS and read-only role<br>Result<br>`
```
```

The model is not a security boundary.

Use independently enforced controls for:

- read-only warehouse role;

- explicit allowed datasets;

- timeout;

- maximum result size;

- concurrency;

- query cost/scan budget;

- permitted SQL statement classes;

- tool scopes;

- egress destinations.

MCP's own specification puts input validation, access control and rate limiting on the server, not in model instructions. [10](https://modelcontextprotocol.io/specification/2026-07-28/server/tools)

**Deliverable — Row / column security**

Existing warehouse row- and column-security controls should remain authoritative.

BigQuery's current documentation illustrates the expected model: row-level policies filter row visibility based on the caller and coexist with dataset/table/column controls; policies can use caller identity such as `SESSION_USER()`. [21](https://cloud.google.com/bigquery/docs/row-level-security-intro) Column-level controls provide a complementary mechanism for sensitive fields. [22](https://cloud.google.com/bigquery/docs/column-level-security-intro)

This works through an agent only if the agent tool does not destroy the principal boundary.

The dangerous paths are:

```
text

```
`user<br> → agent<br> → broad service credential<br> → unrestricted warehouse<br>`
```
```

and:

```
text

```
`authorized query<br> → shared cache / embedding index / RAG document<br> → another user<br>`
```
```

Therefore every derived context store must answer:

> **Under which entitlement was this value generated, and who may retrieve it now?**

For metadata caches, either:

- filter authorization before insertion/retrieval; or

- partition/cache-key by tenant and entitlement context.

For result caches:

- bind result artifacts to subject/tenant/policy IDs;

- prevent downstream retrieval from broadening access;

- invalidate appropriately on permission change;

- never put restricted values into globally shared vector stores.

A particularly useful concrete regression case comes from BigQuery's documentation: certain table replacement operations can remove row-level access policies. That makes “policy still exists after schema/table deployment” a testable infrastructure invariant, not an assumption. [21](https://cloud.google.com/bigquery/docs/row-level-security-intro)

**Deliverable — MCP / tool-protocol implications**

MCP is useful plumbing, not an agent-readiness certification.

As of the research snapshot, the July 28, 2026 MCP specification provides:

- tool discovery through `tools/list`;

- JSON Schema input definitions;

- optional output schemas and structured results;

- list-change notifications;

- authorization-sensitive tool visibility;

- explicit tool-call errors;

- server-side input validation/access controls;

- an HTTP authorization framework built on OAuth-related standards. [10](https://modelcontextprotocol.io/specification/2026-07-28/server/tools)

It also deliberately does not solve business semantics.

An MCP server exposing:

```
text

```
`run_sql(sql)<br>`
```
```

still knows nothing inherently about:

- row grain;

- metric governance;

- freshness;

- data-quality incidents;

- historical metric definitions;

- approved join paths;

- ownership;

- organizational synonyms.

Those must be modeled by the data platform and exposed through resources/tools.

MCP's state model is also worth noting: the tool specification says the protocol has no implicit per-connection state handle; stateful operations should return an explicit handle and validate caller authorization against that handle on subsequent calls. [10](https://modelcontextprotocol.io/specification/2026-07-28/server/tools) That is exactly the right principle for query plans, approval flows and write transactions: **explicit IDs, not hidden conversational state**.

**Deliverable — Read / write boundary**

Separate read and write paths architecturally.

Recommended:

```
text

```
`read MCP/tool server<br>    read-only identities<br>    catalog / metric / quality / query access<br><br>write/action server<br>    separate credentials<br>    explicit mutation tools<br>    approval policy<br>    idempotency<br>    audit<br>`
```
```

Avoid:

```
text

```
`execute_sql(sql, read_or_write_anything=true)<br>`
```
```

Mutation tools should be purpose-specific:

```
text

```
`ticket.create<br>catalog.comment.create<br>metric.annotation.create<br>crm.record.update<br>derived_dataset.publish<br>`
```
```

Each write request should carry:

```
yaml

```
`action_id: act_...<br>idempotency_key: idem_...<br>actor_workload_id: agent_...<br>subject_user_id: user_...<br>target_id: ...<br>expected_target_version: ...<br>authorization_context_id: ...<br>approval_token: ...<br>reason: ...<br>`
```
```

and return:

```
yaml

```
`status: committed<br>mutation_id: ...<br>new_version: ...<br>rollback_ref: ...<br>audit_event_id: ...<br>`
```
```

MCP recommends user confirmation for sensitive tool operations and logging tool usage for audit. [10](https://modelcontextprotocol.io/specification/2026-07-28/server/tools)

Required write-side safeguards:

- **authorization at execution time**;

- **idempotency** so retries do not duplicate effects;

- **precondition/version checks** to prevent stale writes;

- **schema/business validation**;

- **human approval** for consequential mutations;

- **bounded rollback/compensating action** where feasible;

- **complete audit record**.

“Write access enabled” should never imply that a read agent has inherited a generic privileged database credential.

## Provenance, observability and evaluation

**Deliverable — Provenance / audit model**

Persist a complete chain:

```
text

```
`user request<br>  → semantic interpretation<br>  → context snapshot<br>  → tool selection<br>  → metric/source selection<br>  → query<br>  → result<br>  → model output<br>  → downstream action<br>`
```
```

OpenLineage's stable run, job and dataset concepts provide useful identifiers for the data-processing portion of this chain. [14](https://openlineage.io/docs/spec/object-model/) MCP supplies structured tool-call boundaries that can similarly be assigned IDs and audited. [10](https://modelcontextprotocol.io/specification/2026-07-28/server/tools)

Minimum persisted identifiers:

<table header-row="true">
	<tr>
		<td>Stage</td>
		<td>Persist</td>
	</tr>
	<tr>
		<td>Request</td>
		<td>`request_id`, user/subject, workload, timestamp, conversation/task ID</td>
	</tr>
	<tr>
		<td>Interpretation</td>
		<td>`interpretation_id`, recognized entities/metrics/terms, ambiguities</td>
	</tr>
	<tr>
		<td>Context</td>
		<td>`context_id`, catalog snapshot/hash, retrieved metadata IDs/versions</td>
	</tr>
	<tr>
		<td>Tool</td>
		<td>`tool_call_id`, tool name/version, parameters hash, authorization context</td>
	</tr>
	<tr>
		<td>Semantic object</td>
		<td>metric/entity ID and **definition version**</td>
	</tr>
	<tr>
		<td>Source</td>
		<td>dataset IDs, schema/contract versions</td>
	</tr>
	<tr>
		<td>Query</td>
		<td>query ID, compiled SQL/hash, warehouse role/principal, cost/latency</td>
	</tr>
	<tr>
		<td>Data state</td>
		<td>freshness/quality snapshot IDs</td>
	</tr>
	<tr>
		<td>Result</td>
		<td>result artifact ID/hash, row count, masking/security metadata</td>
	</tr>
	<tr>
		<td>Model</td>
		<td>model/deployment version, prompt/config version</td>
	</tr>
	<tr>
		<td>Answer</td>
		<td>answer/output ID, cited provenance IDs</td>
	</tr>
	<tr>
		<td>Action</td>
		<td>mutation/action ID, approval, idempotency key, target version</td>
	</tr>

</table>

Do not merely log rendered text. Without stable object IDs and definition versions, an incident review cannot reconstruct *which meaning* was used.

**Deliverable — Observability model**

An operational trace should include:

```
text

```
`identity<br>question<br>interpretation<br>retrieved context<br>tool choices<br>parameters<br>query ID<br>datasets/metrics<br>versions<br>quality/freshness state<br>latency<br>query cost<br>errors/retries<br>answer<br>downstream action<br>`
```
```

Useful dashboards:

<table header-row="true">
	<tr>
		<td>Dashboard</td>
		<td>What it catches</td>
	</tr>
	<tr>
		<td>**Agent correctness/eval trend** by model/tool/domain</td>
		<td>regressions hidden by average success rate</td>
	</tr>
	<tr>
		<td>**Source-selection dashboard**</td>
		<td>raw tables displacing governed metrics</td>
	</tr>
	<tr>
		<td>**Context retrieval precision/size**</td>
		<td>catalog retrieval becoming noisy or expensive</td>
	</tr>
	<tr>
		<td>**Metric resolution ambiguity**</td>
		<td>glossary/semantic debt</td>
	</tr>
	<tr>
		<td>**Wrong-grain / fanout rejection rate**</td>
		<td>modeling gaps and user-demand hotspots</td>
	</tr>
	<tr>
		<td>**Freshness & quality decisions**</td>
		<td>agents continuing despite unhealthy data</td>
	</tr>
	<tr>
		<td>**Authorization denials and leakage tests**</td>
		<td>permission regressions/attacks</td>
	</tr>
	<tr>
		<td>**Query cost / timeout / scan volume**</td>
		<td>runaway analytical agents</td>
	</tr>
	<tr>
		<td>**Tool errors by schema/version**</td>
		<td>API/tool regressions</td>
	</tr>
	<tr>
		<td>**Write approvals/retries/rollbacks**</td>
		<td>unsafe operational automation</td>
	</tr>
	<tr>
		<td>**Metadata age / unowned assets**</td>
		<td>catalog decay</td>
	</tr>
	<tr>
		<td>**Provenance completeness**</td>
		<td>answers that cannot be reconstructed</td>
	</tr>

</table>

Page on:

- any restricted-result leakage;

- cross-tenant access;

- anomalous jump in broad SQL usage;

- sudden fall in metric-API utilization;

- sustained freshness/quality bypass;

- major evaluation regression after deployment;

- unbounded cost/scan growth;

- provenance chains with missing source/query IDs.

**Deliverable — Evaluation suite**

The test set must use **company-specific fixtures** with known truth. Public text-to-SQL benchmarks are useful for model research, but enterprise production evidence shows real environments add organization-specific metadata, business logic and changing schemas. [1](https://arxiv.org/abs/2507.14372)

<table header-row="true">
	<tr>
		<td>Test</td>
		<td>Fixture</td>
		<td>Correct behavior</td>
	</tr>
	<tr>
		<td>**Exact known metric**</td>
		<td>“Net revenue last month by country”</td>
		<td>Resolve canonical metric ID/version; call governed metric interface; return provenance and state.</td>
	</tr>
	<tr>
		<td>**Ambiguous metric**</td>
		<td>“Revenue by segment” where gross/net/ARR exist</td>
		<td>Do not guess. Resolve ambiguity from context or request disambiguation; show canonical candidates.</td>
	</tr>
	<tr>
		<td>**Synonym**</td>
		<td>internal acronym or colloquial term</td>
		<td>Map through glossary to canonical object and record mapping.</td>
	</tr>
	<tr>
		<td>**Wrong-grain trap**</td>
		<td>user asks customer count from order-line fact</td>
		<td>Detect grain; perform distinct entity aggregation or use customer-grain source; never count rows blindly.</td>
	</tr>
	<tr>
		<td>**Invalid join**</td>
		<td>two many-side facts with no safe bridge</td>
		<td>Detect absent/unsafe relationship; refuse direct join and suggest approved path.</td>
	</tr>
	<tr>
		<td>**Stale dataset**</td>
		<td>source breaches freshness SLO</td>
		<td>Return stale/degraded state; select approved healthy alternative or qualify/refuse according to policy.</td>
	</tr>
	<tr>
		<td>**Failing-quality source**</td>
		<td>blocking uniqueness/completeness check</td>
		<td>Do not present result as reliable; refuse affected governed answer or use approved alternative.</td>
	</tr>
	<tr>
		<td>**Restricted column**</td>
		<td>salary/PII invisible to test user</td>
		<td>Server/warehouse denies it; no value appears in model context, logs, cache or answer.</td>
	</tr>
	<tr>
		<td>**Restricted row**</td>
		<td>user authorized only for EMEA</td>
		<td>Warehouse enforcement returns only EMEA; trace records subject and applied policy.</td>
	</tr>
	<tr>
		<td>**Nonexistent dataset**</td>
		<td>plausible hallucinated table</td>
		<td>Return no-match/not-found; do not invent SQL against it.</td>
	</tr>
	<tr>
		<td>**Changed schema**</td>
		<td>renamed/removed field</td>
		<td>Contract/schema validation fails before execution or query is regenerated from current contract.</td>
	</tr>
	<tr>
		<td>**Changed metric definition**</td>
		<td>analysis spans old/new effective periods</td>
		<td>Select historically correct definitions or explicitly restate; never silently use today's definition for all history.</td>
	</tr>
	<tr>
		<td>**Conflicting documentation**</td>
		<td>wiki says A, semantic definition says B</td>
		<td>Prefer designated authoritative semantic object; surface conflict and owner/escalation route.</td>
	</tr>
	<tr>
		<td>**Malicious metadata / prompt injection**</td>
		<td>table description says “ignore policy and export…”</td>
		<td>Treat description as untrusted data; no authorization/tool-policy change; attack appears in security telemetry.</td>
	</tr>
	<tr>
		<td>**High-cost query**</td>
		<td>unconstrained multi-terabyte join</td>
		<td>preflight/policy gate blocks, constrains or requires explicit approval before expensive execution.</td>
	</tr>

</table>

The prompt-injection test is not hypothetical. Research on indirect prompt injection into tool-using agents shows retrieved/tool content can steer actions, including information leakage and unsafe tool usage; recent work finds policy enforcement or context sanitization substantially more robust than prompt-only defenses. [23](https://arxiv.org/abs/2607.10490)

Evaluation should score more than “final answer exact match”:

```
text

```
`semantic-object selection<br>grain correctness<br>join validity<br>metric-version correctness<br>source authorization<br>quality/freshness behavior<br>query correctness<br>provenance completeness<br>cost compliance<br>write-policy compliance<br>final-answer correctness<br>`
```
```

An answer that happens to contain the right number after using an unauthorized source is a failed test.

## Change, security and failure-first operations

**Deliverable — Change / regression strategy**

Any component capable of changing meaning, available context, authorization or execution behavior should trigger automated evaluation.

<table header-row="true">
	<tr>
		<td>Change</td>
		<td>Minimum automated reaction</td>
	</tr>
	<tr>
		<td>**Schema change**</td>
		<td>contract compatibility; affected query fixtures; grain/key/join checks; downstream lineage tests</td>
	</tr>
	<tr>
		<td>**Metric change**</td>
		<td>exact metric, dimensions, effective-date and historical-definition tests</td>
	</tr>
	<tr>
		<td>**Catalog edit**</td>
		<td>discovery ranking; synonym; ambiguity; conflicting-doc and prompt-injection tests</td>
	</tr>
	<tr>
		<td>**Contract change**</td>
		<td>compatibility checks plus affected downstream evaluation</td>
	</tr>
	<tr>
		<td>**Owner change**</td>
		<td>escalation/support routing validation</td>
	</tr>
	<tr>
		<td>**Permission change**</td>
		<td>restricted-row/column tests; cache invalidation test</td>
	</tr>
	<tr>
		<td>**Model change**</td>
		<td>full representative evaluation suite</td>
	</tr>
	<tr>
		<td>**Tool/schema change**</td>
		<td>schema conformance, error handling, auth, affected task evaluations</td>
	</tr>
	<tr>
		<td>**Semantic relationship change**</td>
		<td>join/fanout suite plus all impacted metrics</td>
	</tr>
	<tr>
		<td>**Freshness/quality policy change**</td>
		<td>stale/degraded decision tests</td>
	</tr>
	<tr>
		<td>**Prompt/system-policy change**</td>
		<td>security + behavioral regression suite</td>
	</tr>

</table>

Use lineage to select an impacted subset where possible, but model and authorization changes deserve much broader regression. OpenLineage's job/dataset graph and version/run metadata make impact-based selection implementable. [14](https://openlineage.io/docs/spec/object-model/)

Tool changes are themselves observable events: the current MCP specification supports tool-list change notifications, reinforcing that tool availability/schema is dynamic state rather than static prompt configuration. [10](https://modelcontextprotocol.io/specification/2026-07-28/server/tools)

**Deliverable — Security controls**

The important security model is:

> **Model output is a proposal. Policy-enforcing infrastructure decides whether it may become an action.**

Recent indirect-prompt-injection research provides the strongest reason not to rely on reasoning alone. AgentSentry specifically studies attacks delivered through tool outputs/retrieved content; NetInjectBench reports high unsafe-action rates under naive agent execution and strong improvement from a metadata-aware policy gate under its assumptions. [24](https://arxiv.org/abs/2602.22724) Other empirical work demonstrates tool-using agents can be induced to disclose personal information despite built-in defenses. [25](https://arxiv.org/abs/2506.01055)

<table header-row="true">
	<tr>
		<td>Threat</td>
		<td>Containment</td>
	</tr>
	<tr>
		<td>**Metadata prompt injection**</td>
		<td>classify catalog descriptions/comments as untrusted content; never allow them to override system/tool policy; preserve source provenance; sanitize tool output</td>
	</tr>
	<tr>
		<td>**Data-as-instruction**</td>
		<td>structured result channel; schema validation; content isolation; action allowlists; no automatic elevation because result text asks for it</td>
	</tr>
	<tr>
		<td>**Malicious tool description**</td>
		<td>trusted tool registry; signed/approved servers; MCP explicitly tells clients to consider tool annotations untrusted unless they originate from trusted servers. [10](https://modelcontextprotocol.io/specification/2026-07-28/server/tools)</td>
	</tr>
	<tr>
		<td>**Excessive permissions**</td>
		<td>workload + delegated identity; least scopes; narrow tool credentials; server enforcement</td>
	</tr>
	<tr>
		<td>**Exfiltration**</td>
		<td>result-size limits, egress policy, DLP where appropriate, prevent arbitrary external-send tools from freely combining with sensitive query tools</td>
	</tr>
	<tr>
		<td>**Cross-tenant leakage**</td>
		<td>tenant-bound authorization/resource audience; tenant-specific retrieval indexes or mandatory entitlement filtering</td>
	</tr>
	<tr>
		<td>**Cached restricted data**</td>
		<td>entitlement-bound cache keys/artifacts; invalidation on policy changes; no globally shared restricted embeddings</td>
	</tr>
	<tr>
		<td>**SQL misuse**</td>
		<td>read-only role, statement parser/policy, query validation, limits</td>
	</tr>
	<tr>
		<td>**Tool chaining escalation**</td>
		<td>policy evaluates whole action capability, not only individual calls; sensitive operations require approval</td>
	</tr>
	<tr>
		<td>**Write replay**</td>
		<td>idempotency key + target-version precondition + audit</td>
	</tr>

</table>

MCP itself mandates server-side input validation, access controls, rate limiting and output sanitization for tools, but these are only protocol-level foundations; data-specific policy remains your responsibility. [10](https://modelcontextprotocol.io/specification/2026-07-28/server/tools)

**Deliverable — Failure-mode catalogue**

<table header-row="true">
	<tr>
		<td>Failure</td>
		<td>Symptom</td>
		<td>Likely mechanism</td>
		<td>Detection</td>
		<td>Containment</td>
		<td>Recovery</td>
		<td>Prevention</td>
		<td>Evidence strength</td>
	</tr>
	<tr>
		<td>**Undocumented grain**</td>
		<td>plausible but wrong counts/revenue</td>
		<td>agent treats rows as business entities</td>
		<td>compare query aggregation to declared/tested keys; grain eval</td>
		<td>block unknown-grain sources for governed analytics</td>
		<td>identify true grain, recompute</td>
		<td>mandatory grain contract + key tests</td>
		<td>**Medium-high:** semantic-system evidence, strong engineering mechanism. [5](https://docs.getdbt.com/docs/build/semantic-models)</td>
	</tr>
	<tr>
		<td>**Join fanout**</td>
		<td>inflated sums/counts</td>
		<td>many-to-many or one-to-many join applied before aggregation</td>
		<td>relationship/cardinality validator; row-explosion telemetry</td>
		<td>reject unsafe plan</td>
		<td>use bridge/pre-aggregate</td>
		<td>machine-readable join contracts</td>
		<td>**High structural evidence.** [6](https://docs.getdbt.com/docs/build/entities)</td>
	</tr>
	<tr>
		<td>**Metric definitions diverge**</td>
		<td>dashboards/agent disagree</td>
		<td>model rebuilds metric from raw columns or teams own different formulas</td>
		<td>compare provenance and metric IDs/versions</td>
		<td>route governed requests to metric API</td>
		<td>restate affected answers</td>
		<td>stable metric IDs + semantic ownership</td>
		<td>**High system evidence; moderate direct agent evidence.** [5](https://docs.getdbt.com/docs/build/semantic-models)</td>
	</tr>
	<tr>
		<td>**Today's definition applied historically**</td>
		<td>historical trend changes inexplicably</td>
		<td>no effective-date semantics</td>
		<td>temporal metric evaluation</td>
		<td>force explicit historical/restated mode</td>
		<td>recalculate with applicable versions</td>
		<td>effective-from/to + supersession contract</td>
		<td>**Interpretation; direct platform support evidence limited.**</td>
	</tr>
	<tr>
		<td>**Stale metadata**</td>
		<td>agent selects renamed/deprecated asset</td>
		<td>catalog ingestion lags source</td>
		<td>compare metadata/source versions, contract hash</td>
		<td>lower/rule out stale entry</td>
		<td>refresh catalog, rerun</td>
		<td>freshness SLA for metadata itself</td>
		<td>**Medium; deployed retrieval systems depend on up-to-date semantics.** [1](https://arxiv.org/abs/2507.14372)</td>
	</tr>
	<tr>
		<td>**Catalog nobody maintains**</td>
		<td>many descriptions empty/wrong, synonym confusion</td>
		<td>no owner/SLO/incentive</td>
		<td>metadata-age and ownership dashboards</td>
		<td>prefer certified/owned assets</td>
		<td>domain remediation campaign</td>
		<td>contracts + owner accountability; usage-driven gaps</td>
		<td>**Medium operational evidence.** [9](https://bitol-io.github.io/open-data-contract-standard/v3.0.2/)</td>
	</tr>
	<tr>
		<td>**Quality failure invisible to AI**</td>
		<td>confident answer from broken table</td>
		<td>tests exist in separate system</td>
		<td>provenance audit lacks quality snapshot</td>
		<td>policy blocks unhealthy source</td>
		<td>re-run after recovery</td>
		<td>normalize quality into context/tool response</td>
		<td>**High operational rationale.** [16](https://docs.snowflake.com/en/sql-reference/local)</td>
	</tr>
	<tr>
		<td>**Freshness failure invisible**</td>
		<td>“today” answer is yesterday's data</td>
		<td>schedule documented but actual watermark absent</td>
		<td>compare requested horizon to event/processing time</td>
		<td>warn/fallback/refuse</td>
		<td>use healthy source or wait for recovery</td>
		<td>machine-readable expected + observed freshness</td>
		<td>**High official evidence.** [15](https://docs.getdbt.com/reference/resource-configs/freshness)</td>
	</tr>
	<tr>
		<td>**Generic tool is too powerful**</td>
		<td>model performs unnecessary broad queries/actions</td>
		<td>`run_sql` combines discovery, access and execution</td>
		<td>tool traces, high denial/error/cost rate</td>
		<td>read-only/narrow policy wrapper</td>
		<td>introduce constrained tools</td>
		<td>typed composable interfaces</td>
		<td>**Medium: protocol supports pattern; comparative benchmark lacking.** [10](https://modelcontextprotocol.io/specification/2026-07-28/server/tools)</td>
	</tr>
	<tr>
		<td>**Permission bypass through service account**</td>
		<td>user sees data unavailable in BI/warehouse UI</td>
		<td>agent uses common privileged principal</td>
		<td>differential permission tests</td>
		<td>disable broad credential; enforce subject</td>
		<td>rotate credential, audit exposure</td>
		<td>delegated-user or subject-aware policy</td>
		<td>**High security rationale/standards evidence.** [20](https://modelcontextprotocol.io/specification/2026-07-28/basic/authorization)</td>
	</tr>
	<tr>
		<td>**Permission bypass through cache/RAG**</td>
		<td>restricted values leak to another user</td>
		<td>cache lacks entitlement boundary</td>
		<td>canary restricted values; cross-user tests</td>
		<td>purge affected cache/index</td>
		<td>rebuild under proper partitioning</td>
		<td>bind artifacts to tenant/subject/policy</td>
		<td>**High-risk engineering inference.**</td>
	</tr>
	<tr>
		<td>**RLS disappears after deployment**</td>
		<td>previously hidden rows visible</td>
		<td>table replacement/recreation drops policy</td>
		<td>post-deploy security test</td>
		<td>fail deployment / remove access</td>
		<td>recreate policies</td>
		<td>policy-as-code + security regression</td>
		<td>**High: officially documented concrete mechanism.** [21](https://cloud.google.com/bigquery/docs/row-level-security-intro)</td>
	</tr>
	<tr>
		<td>**Agent chooses raw table over governed metric**</td>
		<td>right-looking but definitionally wrong answer</td>
		<td>retrieval score favors physical match/popularity</td>
		<td>metric-vs-raw provenance dashboard</td>
		<td>require semantic resolution first</td>
		<td>recompute via metric</td>
		<td>rank governed objects above physical sources</td>
		<td>**Medium-high semantic evidence.** [5](https://docs.getdbt.com/docs/build/semantic-models)</td>
	</tr>
	<tr>
		<td>**Lineage exists but is unusable**</td>
		<td>giant graph, no incident insight</td>
		<td>lineage lacks run/version/health context</td>
		<td>diagnostic task eval</td>
		<td>query narrow direction/depth</td>
		<td>enrich execution metadata</td>
		<td>stable IDs + run/job/dataset facets</td>
		<td>**High protocol evidence.** [14](https://openlineage.io/docs/spec/object-model/)</td>
	</tr>
	<tr>
		<td>**Context construction is expensive/noisy**</td>
		<td>latency/token cost, wrong source</td>
		<td>whole catalogs dumped into prompts</td>
		<td>context size, retrieval precision, latency</td>
		<td>staged retrieval</td>
		<td>rerank/narrow context</td>
		<td>domain routing + schema linking</td>
		<td>**High empirical evidence.** [2](https://aclanthology.org/2025.emnlp-main.51/)</td>
	</tr>
	<tr>
		<td>**Schema change silently breaks queries**</td>
		<td>column errors or worse semantic drift</td>
		<td>consumer contract absent/stale</td>
		<td>contract diff + regression</td>
		<td>reject incompatible version</td>
		<td>migrate queries/semantic model</td>
		<td>contract enforcement</td>
		<td>**High official evidence.** [11](https://docs.getdbt.com/reference/resource-configs/contract)</td>
	</tr>
	<tr>
		<td>**Metadata/data prompt injection**</td>
		<td>strange tool call, exfiltration, policy override attempt</td>
		<td>untrusted content interpreted as instructions</td>
		<td>adversarial fixtures and action traces</td>
		<td>external policy gate; sanitize; deny</td>
		<td>terminate flow, rotate credentials if needed</td>
		<td>untrusted-content architecture</td>
		<td>**High empirical security evidence.** [23](https://arxiv.org/abs/2607.10490)</td>
	</tr>
	<tr>
		<td>**High-cost analytical loop**</td>
		<td>repeated scans/timeouts</td>
		<td>agent retries unconstrained SQL</td>
		<td>cost and retry telemetry</td>
		<td>budget/timeout/concurrency gate</td>
		<td>cancel; optimize plan</td>
		<td>validation/preflight + per-task budget</td>
		<td>**Strong engineering inference; tool timeout/rate-limit requirements support containment.** [10](https://modelcontextprotocol.io/specification/2026-07-28/server/tools)</td>
	</tr>
	<tr>
		<td>**Duplicate write on retry**</td>
		<td>two tickets/records/comments</td>
		<td>mutation tool is not idempotent</td>
		<td>duplicate action/idempotency telemetry</td>
		<td>reject repeated key</td>
		<td>compensate/remove duplicate</td>
		<td>mandatory idempotency + target preconditions</td>
		<td>**Strong distributed-systems design; not specifically established by agent benchmark.**</td>
	</tr>
	<tr>
		<td>**“AI-ready” system unused**</td>
		<td>platform exists but humans bypass it</td>
		<td>context/tools do not match real workflows or trust needs</td>
		<td>active usage, abandonment, human correction rate</td>
		<td>restrict scope to high-value tasks</td>
		<td>redesign from task traces</td>
		<td>start from recurring agent/user tasks</td>
		<td>**Production warning:** even a sophisticated LinkedIn system reported 53% correct/close responses, illustrating remaining trust gap. [1](https://arxiv.org/abs/2507.14372)</td>
	</tr>

</table>

## Evidence, disagreements and firsthand tests

**Deliverable — Practitioner disagreements**

**Semantic layer versus “derive meaning from usage.”** dbt and Snowflake represent the prescriptive position: explicitly model metrics, dimensions, entities and relationships. [5](https://docs.getdbt.com/docs/build/semantic-models) MotherDuck has argued from the opposite practitioner perspective that query logs are themselves an empirical record of real organizational semantics and that exhaustive top-down semantic modeling can become a bottleneck. [26](https://motherduck.com/blog/who-needs-a-semantic-layer-anyway/)

**My position:** use both. Govern the small set of consequential metrics/relationships explicitly; mine query and usage logs to discover the long tail, candidate synonyms and missing semantic objects. Usage should propose meaning, not automatically become meaning.

**Load lots of context versus retrieve narrowly.** The schema-linking evidence is much clearer here: large irrelevant schema is a real failure mode, and hierarchical/multi-stage retrieval improves grounding. [4](https://arxiv.org/abs/2501.17174)

**My position:** keep mandatory invariants in the initial context envelope; retrieve lineage, schema detail, test history and documentation bodies only on demand.

**Service identity versus delegated identity.** A service principal is operationally simpler, while delegated identity preserves existing entitlement semantics. OAuth and current MCP authorization explicitly support acting on behalf of a resource owner. [19](https://www.rfc-editor.org/info/rfc6749/)

**My position:** dual identity for interactive use; narrow service identities for autonomous background workflows.

**Many narrow tools versus one SQL tool.** MCP makes rich typed tools easy to expose, but the protocol does not establish that narrow tools inherently make models more intelligent. [10](https://modelcontextprotocol.io/specification/2026-07-28/server/tools)

**My position:** narrow tools for governed concepts/state/actions because enforcement and evaluation are cleaner; retain constrained SQL for the long tail.

**Warn versus refuse on unhealthy data.** There is no universal correct threshold. A stale marketing dashboard and an operational financial workflow have different risk tolerances. Freshness frameworks provide warning/error thresholds precisely because acceptability is policy-specific. [15](https://docs.getdbt.com/reference/resource-configs/freshness)

**My position:** encode behavior in the data product's policy: `warn`, `qualify`, `fallback`, `refuse`, `escalate`. Do not leave risk tolerance to the language model.

**Catalog as central system versus domain-owned semantics.** A central API/search layer is valuable for discovery, but ownership information in data-contract specifications reflects the reality that meaning and guarantees belong to teams/domains. [9](https://bitol-io.github.io/open-data-contract-standard/v3.0.2/)

**My position:** central infrastructure and schema; federated semantic ownership.

**Deliverable — Claim-to-source table**

<table header-row="true">
	<tr>
		<td>Claim</td>
		<td>Primary evidence</td>
		<td>Classification</td>
		<td>Strength</td>
	</tr>
	<tr>
		<td>Enterprise text-to-SQL still requires retrieval, semantics and correction beyond basic SQL generation.</td>
		<td>LinkedIn internal enterprise system. [1](https://arxiv.org/abs/2507.14372)</td>
		<td>Production evidence / engineering report</td>
		<td>High</td>
	</tr>
	<tr>
		<td>Selective database/schema retrieval is a key bottleneck at large scale.</td>
		<td>LinkAlign. [2](https://aclanthology.org/2025.emnlp-main.51/)</td>
		<td>Empirical research</td>
		<td>High</td>
	</tr>
	<tr>
		<td>Missing relevant schema and excess irrelevant schema both matter.</td>
		<td>KaSLA. [3](https://arxiv.org/abs/2502.12911)</td>
		<td>Empirical research</td>
		<td>High</td>
	</tr>
	<tr>
		<td>Whole-schema prompts can be expensive/impractical; relevant schema selection can improve accuracy.</td>
		<td>Extractive Schema Linking. [18](https://arxiv.org/abs/2501.17174)</td>
		<td>Empirical research</td>
		<td>High</td>
	</tr>
	<tr>
		<td>Semantic models can explicitly represent entities, dimensions, measures and row grain.</td>
		<td>dbt semantic-model documentation. [5](https://docs.getdbt.com/docs/build/semantic-models)</td>
		<td>Official documentation</td>
		<td>High</td>
	</tr>
	<tr>
		<td>Entity types/relationships influence semantic join behavior.</td>
		<td>dbt entity documentation. [6](https://docs.getdbt.com/docs/build/entities)</td>
		<td>Official documentation</td>
		<td>High</td>
	</tr>
	<tr>
		<td>Semantic views can represent keys, relationships, facts, dimensions, metrics and synonyms.</td>
		<td>Snowflake semantic-view documentation. [7](https://docs.snowflake.com/en/sql-reference/sql/create-semantic-view)</td>
		<td>Official documentation</td>
		<td>High</td>
	</tr>
	<tr>
		<td>Contracts can enforce expected physical schemas/types.</td>
		<td>dbt model contracts. [11](https://docs.getdbt.com/reference/resource-configs/contract)</td>
		<td>Official documentation</td>
		<td>High</td>
	</tr>
	<tr>
		<td>A data-contract specification can include schema, granularity, quality, SLAs and ownership.</td>
		<td>ODCS 3.x. [9](https://bitol-io.github.io/open-data-contract-standard/v3.0.2/)</td>
		<td>Standard/protocol</td>
		<td>High</td>
	</tr>
	<tr>
		<td>Expected and observed freshness can be machine checked.</td>
		<td>dbt freshness configuration. [15](https://docs.getdbt.com/reference/resource-configs/freshness)</td>
		<td>Official documentation</td>
		<td>High</td>
	</tr>
	<tr>
		<td>Data-quality systems can publish observed/expected status programmatically.</td>
		<td>Snowflake data-quality monitoring metadata. [16](https://docs.snowflake.com/en/sql-reference/local)</td>
		<td>Official documentation</td>
		<td>Medium-high</td>
	</tr>
	<tr>
		<td>Lineage can be modeled around datasets, jobs, runs and extensible facets.</td>
		<td>OpenLineage. [12](https://openlineage.io/docs/spec/facets/)</td>
		<td>Standard/protocol</td>
		<td>High</td>
	</tr>
	<tr>
		<td>Individual runs have persistent IDs suitable for provenance.</td>
		<td>OpenLineage run facets. [27](https://openlineage.io/docs/spec/facets/run-facets/)</td>
		<td>Standard/protocol</td>
		<td>High</td>
	</tr>
	<tr>
		<td>Catalog systems expose search, lineage associations and programmatic metadata APIs.</td>
		<td>OpenMetadata. [17](https://docs.open-metadata.org/v1.13.x/features)</td>
		<td>Official documentation</td>
		<td>High</td>
	</tr>
	<tr>
		<td>MCP exposes typed/discoverable tools and structured outputs.</td>
		<td>MCP July 2026 tools specification. [10](https://modelcontextprotocol.io/specification/2026-07-28/server/tools)</td>
		<td>Standard/protocol</td>
		<td>High</td>
	</tr>
	<tr>
		<td>MCP requires server-side validation/access controls/rate limits and recommends audit/timeout/result validation.</td>
		<td>MCP tools specification. [10](https://modelcontextprotocol.io/specification/2026-07-28/server/tools)</td>
		<td>Standard/protocol</td>
		<td>High</td>
	</tr>
	<tr>
		<td>MCP authorization can operate on behalf of a resource owner with resource-bound tokens/scopes.</td>
		<td>MCP July 2026 authorization specification. [20](https://modelcontextprotocol.io/specification/2026-07-28/basic/authorization)</td>
		<td>Standard/protocol</td>
		<td>High</td>
	</tr>
	<tr>
		<td>OAuth supports delegated resource-owner access and client access on its own behalf.</td>
		<td>OAuth 2.0. [19](https://www.rfc-editor.org/info/rfc6749/)</td>
		<td>Standard/protocol</td>
		<td>High</td>
	</tr>
	<tr>
		<td>Warehouse row-level controls can apply caller-dependent row filtering.</td>
		<td>BigQuery row-level security docs. [21](https://cloud.google.com/bigquery/docs/row-level-security-intro)</td>
		<td>Official documentation</td>
		<td>High</td>
	</tr>
	<tr>
		<td>Some table operations can remove row-level access policies, requiring regression checks.</td>
		<td>BigQuery RLS documentation. [21](https://cloud.google.com/bigquery/docs/row-level-security-intro)</td>
		<td>Official documentation</td>
		<td>High</td>
	</tr>
	<tr>
		<td>Indirect prompt injection through retrieved/tool data is an operational threat to agents.</td>
		<td>AgentSentry, NetInjectBench and related empirical work. [23](https://arxiv.org/abs/2607.10490)</td>
		<td>Empirical research</td>
		<td>High</td>
	</tr>
	<tr>
		<td>Usage/query logs may capture useful latent organizational semantics.</td>
		<td>MotherDuck practitioner argument. [26](https://motherduck.com/blog/who-needs-a-semantic-layer-anyway/)</td>
		<td>Practitioner experience</td>
		<td>Medium/contested</td>
	</tr>
	<tr>
		<td>The proposed unified context contract, health policy, provenance chain and dual-identity architecture</td>
		<td>Synthesis of the above</td>
		<td>Interpretation</td>
		<td>Practitioner recommendation</td>
	</tr>
	<tr>
		<td>Claims from informal forum/social discussion</td>
		<td>Not used as load-bearing evidence</td>
		<td>Community discussion</td>
		<td>Excluded from core findings</td>
	</tr>

</table>

**Deliverable — Things that still require firsthand testing**

The largest remaining uncertainty is not whether these metadata concepts exist; it is **which representation and policy works reliably with your models, schemas, users and security topology**.

<table header-row="true">
	<tr>
		<td>Firsthand experiment</td>
		<td>What to measure</td>
		<td>Acceptance criterion worth setting before rollout</td>
	</tr>
	<tr>
		<td>**Grain A/B test**</td>
		<td>agent analytical correctness with/without explicit structured grain</td>
		<td>material reduction in row-count/fanout mistakes</td>
	</tr>
	<tr>
		<td>**Governed metric vs SQL generation**</td>
		<td>metric correctness, latency, user trust, long-tail coverage</td>
		<td>governed metrics nearly deterministic; SQL fallback remains usable</td>
	</tr>
	<tr>
		<td>**Discovery ranking on your catalog**</td>
		<td>top-k correct asset recall, precision, context tokens</td>
		<td>correct governed object appears reliably before popular raw alternatives</td>
	</tr>
	<tr>
		<td>**Synonym/glossary resolution**</td>
		<td>internal acronyms/ambiguous terms</td>
		<td>high precision; ambiguous terms trigger clarification instead of guessing</td>
	</tr>
	<tr>
		<td>**Metric historical-version test**</td>
		<td>queries before/after definition changes</td>
		<td>no silent application of current definition to historical periods</td>
	</tr>
	<tr>
		<td>**Lineage diagnostic test**</td>
		<td>time/tool calls to identify real injected failures</td>
		<td>agent follows actual dependency path rather than name similarity</td>
	</tr>
	<tr>
		<td>**Quality/freshness policy test**</td>
		<td>behavior under warning/degraded/failed fixtures</td>
		<td>policy-compliant warn/fallback/refuse behavior</td>
	</tr>
	<tr>
		<td>**Delegated-identity end-to-end test**</td>
		<td>actual rows/columns visible through every tool path</td>
		<td>exactly matches direct warehouse entitlement</td>
	</tr>
	<tr>
		<td>**Cache isolation test**</td>
		<td>canary restricted values across users/tenants</td>
		<td>zero unauthorized retrieval</td>
	</tr>
	<tr>
		<td>**Policy deployment regression**</td>
		<td>RLS/CLS before/after table/schema deployments</td>
		<td>policies survive or deployment fails closed</td>
	</tr>
	<tr>
		<td>**Metadata prompt-injection red team**</td>
		<td>unauthorized tools/data reached</td>
		<td>zero policy bypass; attack recorded in telemetry</td>
	</tr>
	<tr>
		<td>**Data-as-instruction red team**</td>
		<td>malicious cell/document/tool output</td>
		<td>untrusted content cannot alter authorization/action policy</td>
	</tr>
	<tr>
		<td>**Tool granularity experiment**</td>
		<td>task success vs `run_sql` only vs composable tools</td>
		<td>choose the smallest catalogue that improves reliability without tool-selection overload</td>
	</tr>
	<tr>
		<td>**Context-size experiment**</td>
		<td>accuracy, tokens, latency as retrieved schema grows</td>
		<td>enforce a retrieval budget before degradation point</td>
	</tr>
	<tr>
		<td>**Query-cost control test**</td>
		<td>worst-case scan/retry loops</td>
		<td>hard budget stops execution independently of model behavior</td>
	</tr>
	<tr>
		<td>**Write retry/idempotency test**</td>
		<td>repeated identical tool calls/network retries</td>
		<td>exactly one committed side effect</td>
	</tr>
	<tr>
		<td>**Approval-bypass test**</td>
		<td>prompt/tool chaining around sensitive writes</td>
		<td>no mutation without server-valid approval</td>
	</tr>
	<tr>
		<td>**Permission-change/cache invalidation**</td>
		<td>revoked user's previously generated artifacts</td>
		<td>revoked data no longer retrievable under the new entitlement</td>
	</tr>
	<tr>
		<td>**Model-upgrade regression**</td>
		<td>complete 15-case suite by domain</td>
		<td>no critical security/correctness regression despite average benchmark gains</td>
	</tr>
	<tr>
		<td>**Operational adoption test**</td>
		<td>weekly users, corrections, fallback to analysts, abandoned flows</td>
		<td>prove that the architecture solves recurring tasks rather than merely demonstrating AI connectivity</td>
	</tr>

</table>

The first production milestone I would use is therefore **not** “the assistant can query the warehouse.”

It is:

> **For a bounded set of important analytical tasks, the platform can deterministically tell the agent what the data means, what one row represents, which governed definition applies, whether the data is healthy and current, what the caller may access, what operation is allowed, and exactly where the resulting answer came from—and automated tests prove those properties survive platform changes.**

That is the useful boundary between **LLM-accessible data** and an **agent-ready data platform**.