> ## Content Index
> Fetch the complete content index at: https://devinside.cloud/llms.txt
> Use this file to discover other available public pages before exploring further.

# A Read-Only SQL Diagnostic Agent Needs an Enforced Boundary
- URL: https://devinside.cloud/a-read-only-sql-diagnostic-agent-needs-an-enforced-boundary/
- Published: 2026-10-15T12:15:00.000Z
- Updated: 2026-11-12T17:08:33.000Z
- Description: A diagnostic agent’s read-only claim is tested through actual SQL permission denials, fixed query templates and evidence-based hypothesis rejection.
- Author: Marcin Borecki
- Tags: Engineering, AI engineering and data, #eng14, #devinside-review

START WITH THE MECHANISM

Interactive companion and laboratory update: 12 November 2026\. The editorial date places the topic on the timeline; this current explanation and its measured evidence retain their actual preparation and execution dates.

THE FULL INVESTIGATION

## A Read-Only SQL Diagnostic Agent Needs an Enforced Boundary

Editorial context: 15 October 2026 · Prepared and updated: 12 November 2026

Dates and technology context

This is a retrospective editorial timeline. Topic context: 15 October 2026\. First publicly available: 12 November 2026\. Current text, visuals and laboratory revisions were prepared on 12 November 2026.

The actual .NET11 framework invocation, UPDATE/DROP/read denials, principal switching and plan-hypothesis rejection were executed 2026-11-12.

Calling an agent read-only is a description of intent, not evidence of a permission boundary. A prompt can ask the agent to avoid changes while the connection it uses still has administrative rights. A tool can expose a method named inspect while accepting arbitrary SQL. A diagnostic query can reveal data outside the intended tenant even when it never writes a row.

The useful question is what the system will actually refuse. Can the effective database principal update the approved view? Can it drop the base table? Can it read another tenant's rows? Can a proposal bypass the tool's fixed vocabulary? Can a confident performance explanation survive comparison with the actual plan?

This article answers those questions in a bounded, executed laboratory. A real Microsoft Agent Framework function call invokes a parameterized SQL tool against SQL Server 2022\. The tool switches to a database user with a limited set of grants. Fixed negative tests confirm engine-level denial of update, drop and unauthorized reads. A separate evidence check rejects an incorrect hypothesis about the query's access path.

The provider is scripted, not a real model. The database is synthetic and disposable. Execution took place on 12 November 2026 using .NET 11.0.0, Microsoft.Agents.AI 1.28.0, Microsoft.Extensions.AI 11.0.0 and Microsoft.Data.SqlClient 5.1.6\. The result is a concrete authority and evidence boundary, not a claim that an autonomous database administrator has been deployed safely.

## Define read-only in terms of permitted observations

The fixture has ten thousand order rows divided between two tenant values. AgentReader may select from the tenant-one view and from a diagnostic-summary view. The summary exposes a fixed object label and the known total row count. It does not expose the other tenant's individual order records or amounts.

That distinction is important because read-only is not the same thing as unrestricted visibility. A query can disclose sensitive rows without changing them. A useful diagnostic scope identifies which data and metadata are permitted, which questions they support and which operations remain outside the tool's authority.

The approved tool returns only a count and sum for one bounded region in tenant one's view. The region is an integer from zero through nine. The executable SQL comes from an allowlisted dictionary. The proposal cannot choose a table, column or arbitrary statement.

The database user also receives SHOWPLAN permission so that the diagnostic path can capture an actual execution plan. That is a deliberate expansion of metadata visibility. It should not be hidden inside the phrase read-only, because plan information can reveal implementation details beyond the final aggregate.

The fixture's scope is intentionally small enough to test directly. A production diagnostic agent may need several views, stored procedures or server-level observations. Each addition should have an explicit reason and a negative test for the adjacent forbidden capability. A broad diagnostic role should not be inferred merely because one approved query needs more information.

The end condition is not that the agent says it behaved safely. It is that the effective principal can perform the approved observation, the database rejects the selected forbidden operations, the tool refuses unsupported proposals before execution and the returned result matches an independent expectation.

## Separate the trusted fixture host from the restricted tool context

01 / Watch the mechanism

### Show the effective permission boundary

![The trusted fixture creates the database, while tool sessions switch to AgentReader before queries.](https://devinside.cloud/content/images/2026/11/devinside/ENG14-v1.svg)

Show the effective permission boundaryActual database-user impersonation in the trusted fixture. Not process isolation or a separately provisioned production loginShow the effective permission boundaryActual database-user impersonation in the trusted fixtureTrusted setup hostowns lab schemaTool connectionEXECUTE ASAgentReaderDatabase grantsapprovedobservationsNot process isolation or a separately provisioned production login

Play animation[Enlarge diagram](https://devinside.cloud/content/images/2026/11/devinside/ENG14-v1.svg)

Static view. Play to see how the mechanism unfolds.

The model-facing input is a bounded proposal, not arbitrary code. The drawing makes the host’s administrative role visible instead of hiding it behind read-only wording.

Interpretation and evidence
- Verify the effective principal.
- Separate setup from tool authority.
- Do not claim process isolation.
Actual fixture values where labelled; otherwise conceptual explanation. Animation speed is not a timing measurement.

The harness creates a fresh database and user inside its own SQL Server container. Its setup connection is administrative because it must create tables, views and permissions. That credential remains in the wrapper's memory and child environment, and saved output redacts the connection string.

The query path opens a new connection with pooling disabled and executes a fixed context switch:

```sql
EXECUTE AS USER=N'AgentReader' WITH NO REVERT;

```

The user is created WITHOUT LOGIN. This avoids creating another reusable credential for the local exercise. SQL Server then performs permission checks in the selected database-user context. The NO REVERT choice and disposal of the nonpooled connection keep that session from returning to the administrative context through the tool's normal path. [Microsoft: EXECUTE AS](https://learn.microsoft.com/en-us/sql/t-sql/statements/execute-as-transact-sql?view=sql-server-ver16&ref=devinside.cloud)

This arrangement proves a database execution-context boundary within a trusted host. It does not prove process isolation from arbitrary code execution. The administrative connection material still exists in the laboratory process environment, and setup code can create privileged connections. A production design that requires stronger isolation should provision a genuinely restricted connection identity and separate administrative setup from runtime execution.

The distinction should be stated rather than hidden. The agent provider in this fixture can propose function arguments, not execute arbitrary C# or read the host environment. The application-owned adapter selects SQL and establishes the restricted context. The threat boundary being tested is therefore untrusted proposal data crossing a fixed tool interface.

Pooling is disabled for this fixture so that every query's impersonated session has a simple lifetime. That choice adds connection cost and is not a universal recommendation for production SQL access. If a production design uses pooling and context switching, it needs its own verified reset and identity lifecycle rather than borrowing a timing or safety claim from this isolated arrangement.

## Grant the approved views, not the base table

The base table contains identifiers, tenant values, region values and amounts. Tenant1Orders exposes only the rows whose tenant value is one. Tenant2Orders exposes the other set. The diagnostic summary returns the object label and row count from the base table.

The actual grants are narrow:

```sql
CREATE USER AgentReader WITHOUT LOGIN;
GRANT SELECT ON dbo.Tenant1Orders TO AgentReader;
GRANT SELECT ON dbo.DiagnosticSummary TO AgentReader;
GRANT SHOWPLAN TO AgentReader;

```

There is no grant to update the view, select the base table, select Tenant2Orders or alter the schema. The views and base table share the intended owner in this fixture, allowing the approved view query to work without granting direct table access. The permission tests verify the resulting behavior instead of relying only on the script's appearance.

The diagnostic view's total row count is intentionally allowed. That does not mean all cross-tenant metadata is automatically acceptable. It means this fixture's contract permits that particular summary while forbidding row-level access to the other tenant. A production contract may choose a narrower summary or a separate administrative diagnostic surface.

The SHOWPLAN grant is likewise explicit. STATISTICS XML requires both the ability to execute the statement and appropriate SHOWPLAN permissions for referenced databases. The tool does not assume that SELECT alone implies access to actual plans. [Microsoft: SET STATISTICS XML permissions](https://learn.microsoft.com/en-us/sql/t-sql/statements/set-statistics-xml-transact-sql?view=sql-server-ver16&ref=devinside.cloud)

Permission design is easier to review when each grant has a consumer. The tenant view supports the approved aggregate. The diagnostic view supports the known summary. SHOWPLAN supports the saved plan. If a grant has no concrete diagnostic consumer, it should not be added merely because a future agent might find it useful.

## Ask the database to reject the forbidden operations

02 / A concrete example

### Read-only is an observed refusal matrix

![The restricted principal can read the diagnostic view but cannot update, drop or read unauthorized rows.](https://devinside.cloud/content/images/2026/11/devinside/ENG14-v2.svg)

[Enlarge diagram](https://devinside.cloud/content/images/2026/11/devinside/ENG14-v2.svg)

These fixed negative statements test SQL permissions independently of tool argument validation. The unchanged-state check follows the denials.

Interpretation and evidence
- Test forbidden neighbors.
- Retain actual error numbers.
- Verify state after mutation attempts.
Actual fixture values where labelled; otherwise conceptual explanation. Animation speed is not a timing measurement.

The harness first queries USER\_NAME together with the diagnostic-summary row count. The result must identify AgentReader and report ten thousand known rows. This confirms the effective context for a permitted operation before testing the forbidden ones.

It then issues four fixed laboratory-owned negative statements through fresh AgentReader sessions. UPDATE against Tenant1Orders is denied with SQL error 229\. DROP TABLE against Orders is denied with error 3701 in this execution. Selecting Tenant2Orders is denied with 229, and direct selection from Orders is also denied with 229.

These statements are authored test cases, not model-generated SQL that slipped through the proposal interface. Their purpose is to verify the engine boundary independently of application validation. The proposal path separately rejects nonempty SQL before opening a connection.

After the denied mutation attempts, an administrative verification confirms that the base table still exists, contains ten thousand rows and retains the expected amount sum of 505,000\. That check matters because an error message alone should not be treated as proof that no earlier part of an unintended operation changed state.

The negative DROP error is preserved as observed. The article does not normalize every denial into one preferred error number or claim that the same message must appear under every SQL Server configuration. The acceptance condition is that the operation is refused and the fixture remains intact.

The test covers selected capabilities, not every possible SQL statement. It does not attempt privilege escalation, cross-database ownership-chain attacks or arbitrary code execution. Its value is a concrete permission matrix tied to the tool's declared responsibilities, with actual engine responses and unchanged-state evidence.

## Keep unsafe proposals outside the SQL channel

03 / See the idea

### Do not make one guard do every job

![Application validation refuses unsafe proposals while database permissions deny fixed forbidden operations.](https://devinside.cloud/content/images/2026/11/devinside/ENG14-v3.svg)

[Enlarge diagram](https://devinside.cloud/content/images/2026/11/devinside/ENG14-v3.svg)

The tests address different failure paths. Passing one does not remove the need to preserve the other boundary.

Interpretation and evidence
- Reject syntax before execution.
- Keep database restrictions real.
- Do not conflate refusal layers.
Actual fixture values where labelled; otherwise conceptual explanation. Animation speed is not a timing measurement.

Database permissions are a second boundary, not a reason to expose a raw SQL terminal to a model. The tool accepts a typed plan and maps approved template keys to fixed statements. A nonempty Sql field returns free\_sql\_forbidden. Another tenant returns tenant\_scope\_mismatch. Unknown templates and unsupported region numbers have their own rejection reasons.

The execution counter is checked around every rejected proposal. It must remain unchanged. This proves that those values were rejected before reaching the query adapter, rather than merely failing later as SQL syntax or permission errors.

The real framework invocation still occurs. Microsoft.Agents.AI and FunctionInvokingChatClient call the tool once, and the scripted IChatClient receives the structured result before returning its final acknowledgement. Each case checks two provider calls and one tool call. [Microsoft.Agents.AI 1.28.0](https://www.nuget.org/packages/Microsoft.Agents.AI/1.28.0?ref=devinside.cloud), [Microsoft.Extensions.AI 11.0.0](https://www.nuget.org/packages/Microsoft.Extensions.AI/11.0.0?ref=devinside.cloud)

That distinction makes refusal a normal product behavior. A tool can return a bounded rejection without crashing the host or pretending the requested observation succeeded. The caller receives a reason that can support clarification, a different approved operation or a human review.

The scripted provider does not test prompt-injection resistance or language-model compliance. It deliberately supplies known proposals so that the adapter's behavior is deterministic. A real model evaluation would be another layer, and its success rate would not replace these permission and conformance checks.

The architecture therefore has several independent responsibilities: proposal generation, argument validation, executable-template selection, database permissions and result interpretation. A weakness in one layer should not silently expand the authority of all the others.

## Inspect the actual plan without granting a general investigation license

The diagnostic path executes the approved aggregate with STATISTICS XML and STATISTICS IO enabled. It saves the actual XML plan and informational I/O messages. The normal timing path executes the same approved statement without plan collection.

The dataset and index are controlled. Orders has a clustered identifier key and an index on Tenant and Region including Amount. The approved tenant view filters to five thousand odd identifiers. For region three, the expected result is count one thousand and total 49,000.

The direct integer predicate's Index Seek reads one thousand rows and produces one thousand. The conversion comparison template's Index Seek reads five thousand rows and produces the same one thousand. The diagnostic logical reads are six and seventeen. Both captured statements return one aggregate row.

This is enough to reject a simplistic explanation based only on the operator name. It is also enough to reject the claim that the direct query must scan all ten thousand base rows. The evidence does not authorize inspecting unrelated tables or collecting arbitrary query text from other users.

The plan is a diagnostic artifact with its own visibility implications. Even a read-only tool can expose schema and query structure through such output. The fixture keeps that artifact local and tied to the approved query. A production service should decide which parts an agent may see and which parts require a more privileged human workflow.

The actual plan also has limits. Not every operator reports every runtime counter. The parser preserves absent counters as null. It does not convert missing data into zero work or use the plan's estimated cost as a measured wall-clock duration. The evidence layer should be conservative enough that fluent explanation cannot turn a missing field into a confident fact.

## Make a hypothesis earn its place in the answer

04 / A concrete example

### Diagnostic evidence can reject the agent’s story

![The saved actual plan contradicts a proposed full-table-scan explanation.](https://devinside.cloud/content/images/2026/11/devinside/ENG14-v4.svg)

[Enlarge diagram](https://devinside.cloud/content/images/2026/11/devinside/ENG14-v4.svg)

The diagnostic boundary can produce a useful finding without permission to apply changes. A recommendation remains separate from database mutation.

Interpretation and evidence
- Keep observations authoritative.
- Distinguish inference from fact.
- Route changes through another authority.
Actual fixture values where labelled; otherwise conceptual explanation. Animation speed is not a timing measurement.

The fixture supplies a deliberately wrong hypothesis: the indexed regional aggregate must scan the entire base table. A deterministic check parses the actual plan and finds the Index Seek. The saved access counters show one thousand rows read. The hypothesis is recorded as unsupported.

This check is not a model reasoning score. The provider does not analyze XML, and no remote model is asked to diagnose the database. The experiment establishes that an application can bind a proposed explanation to explicit evidence instead of trusting the wording alone.

The conversion template adds a more subtle example. It also says Index Seek, yet it reads five thousand rows. An explanation that equates seek with minimal work would be wrong. The relevant question is which key prefix constrains access and how many rows are examined before the residual condition yields the final set.

A diagnostic agent should distinguish observation, interpretation and recommendation. The observation is the saved operator and counters. The interpretation is that one template examines more approved-tenant rows for this parameter. A recommendation might be to prefer the direct integer template for this operation. That recommendation remains scoped to the tested query shape and data.

It would be unsupported to jump from this result to creating indexes on production, changing database configuration or rewriting arbitrary customer queries. The tool in this fixture has no such authority. A useful diagnosis can end with a bounded proposed change and evidence for human review rather than an automatic mutation.

The boundary is not an obstacle to expertise. It encourages explanations that can be checked. An agent that knows when the evidence contradicts its initial hypothesis is more useful than one that confidently turns every slow operation into the same generic tuning advice.

## Interpret the observed permission matrix precisely

| Capability tested           | Actual result          | Evidence                          |
| --------------------------- | ---------------------- | --------------------------------- |
| Approved diagnostic summary | Allowed as AgentReader | Known row count 10,000            |
| Approved tenant aggregate   | Allowed                | Independent count and sum matched |
| UPDATE approved view        | Denied                 | SQL error 229                     |
| DROP base table             | Denied                 | SQL error 3701                    |
| Read other-tenant view      | Denied                 | SQL error 229                     |
| Read base table directly    | Denied                 | SQL error 229                     |
| Nonempty proposed SQL       | Rejected before SQL    | Query counter unchanged           |

The matrix contains two kinds of refusal. SQL Server enforces the denied database statements. Application validation enforces the proposal rejection before execution. Keeping them separate helps a reviewer understand which layer would still protect the system if another layer changed.

The table does not prove that AgentReader has no possible side effect through every feature of the database engine. It proves the selected grants and denials exercised by this fixture. A broader deployment needs a permission review based on its actual schema, modules and server configuration.

Likewise, the approved diagnostic summary does not imply access to all dynamic management views. The fixture uses an application-owned view with a bounded result. Server-level diagnostics often have different permission requirements and disclosure implications. They should be added deliberately, not inferred from a successful local summary query.

The principal is created WITHOUT LOGIN, and the trusted host performs the context switch. That is useful for a reproducible test without another exported password. It should remain labelled as impersonation-based verification rather than a claim that a production agent connects with a separately isolated low-privilege credential.

The final unchanged-state check closes the negative mutation cases. It confirms the table's existence, row count and amount sum after all denials. The tool's read-only label is therefore supported by actual refusals and preserved state for the selected operations, not only by a prompt or a method name.

## Give diagnostics a resource boundary as well as a permission boundary

05 / A concrete example

### Budget diagnostic work as well as authority

![Two approved reads require different source-row and cached-page accesses despite the same aggregate.](https://devinside.cloud/content/images/2026/11/devinside/ENG14-v5.svg)

[Enlarge diagram](https://devinside.cloud/content/images/2026/11/devinside/ENG14-v5.svg)

A read-only query still consumes database resources. The tool needs a bounded workload contract as well as a permission contract.

Interpretation and evidence
- Measure approved query work.
- Bound duration and concurrency.
- Avoid blanket overhead claims.
Actual fixture values where labelled; otherwise conceptual explanation. Animation speed is not a timing measurement.

The SQL container is limited to two CPUs and four GiB of memory and bound to an ephemeral loopback port. Queries use MAXDOP 1 and a five-second command timeout. The wrapper bounds the child process and removes its own container in finally. These limits keep the demonstration finite and separate from user production resources.

The approved query vocabulary is small, but a read-only query can still consume resources. The conversion template proves that semantically equivalent reads can examine more rows. Permissions alone do not guarantee acceptable CPU, I/O or memory behavior.

The timing companion compares direct adapter execution, agent invocation of the integer template and agent invocation of the conversion template. It preserves connection setup and command durations separately. Because the provider is scripted and pooling is disabled, those observations should not be described as model latency or production connection-pool performance.

The companion timing run records five observations per mode. The agent using the integer predicate has a median SQL-command duration of 1.1498 milliseconds, while the agent using the conversion predicate has 1.8770 milliseconds. Their complete run medians are 14.6792 and 14.8782 milliseconds, with overlapping observed ranges of 12.8000–21.6186 and 13.3900–17.4289 milliseconds.

Those numbers do not establish a general agent overhead or a service-level latency. Connection opening and the restricted-context switch are included in the larger run boundary, and pooling is disabled. A local scripted provider contributes no remote inference latency. The table belongs to this controlled lifecycle, not to an arbitrary production agent.

The direct adapter comparison also has an overlapping range, with a median run duration of 15.4980 milliseconds. Its median being above the agent-index median is a warning against attributing small differences causally from five samples. The raw observations remain available instead of being reduced to a convenient percentage.

The measured access-work difference is clearer than a universal elapsed-time claim. The same aggregate requires six or seventeen logical reads in the diagnostic runs, and the actual access operators read one thousand or five thousand rows. A small local timing sample can vary for reasons outside those counters.

A production diagnostic tool should define which observations justify its resource use and how that use is bounded. It may need query timeouts, concurrency limits, result-size limits and a separate policy for expensive plan collection. The current fixture demonstrates one bounded design, not a ready-made budget for every database.

## Keep recommendations outside the mutation channel

A read-only diagnostic agent can still be useful when it cannot apply its own recommendations. It can collect approved evidence, compare hypotheses, identify uncertainty and produce a proposed change with a verification plan. The application can then route that proposal to a separate authorized workflow.

That separation is especially valuable for database tuning because the right change may depend on a workload not represented by one query. An index that improves this aggregate may increase write cost or storage. A plan that looks inefficient for one parameter may be appropriate for another distribution. A safe diagnostic boundary should not collapse those tradeoffs into an automatic action.

The current tool does not expose CREATE INDEX, UPDATE, DROP or configuration commands. Its fixed negative tests demonstrate that selected mutations are denied under AgentReader. The fact that the trusted fixture setup can create an index does not mean the agent tool receives that capability.

The same separation applies to tenant scope. A model's explanation can request more evidence, but it cannot authorize access to another tenant by saying the comparison would be helpful. The application must decide whether a new approved diagnostic surface is warranted and test its permissions independently.

This is a practical way to make expertise reviewable. The diagnostic result can say what was observed, which hypothesis failed and what experiment would distinguish the remaining explanations. It does not need to impersonate a production administrator to be valuable.

## Recheck the boundary when capabilities grow

The permission fixture should evolve with the tool, not remain a historical green test after new capabilities are added. A new diagnostic view can expose more data. A stored procedure can execute under another owner. A new template can require additional metadata. Each change can alter the effective authority even when the public tool still has a read-only name.

The useful regression question is specific: does the newly approved observation work while the previously forbidden mutation or scope remains refused? The current four database denials and four proposal rejections provide a small baseline. They are not a comprehensive permission audit of an unrelated schema.

The same rule applies to hosting. Replacing impersonation with a dedicated login, adding connection pooling or moving the tool into another process changes the identity lifecycle. Those changes should be verified at their real boundary rather than justified by this fixture's successful EXECUTE AS test.

A diagnostic agent becomes easier to trust when its capability growth is visible and testable. The goal is not to freeze it permanently at a trivial query, but to make each added responsibility come with an explicit consumer, authority rule and observable refusal case.

## Deliver a boundary that another engineer can verify

The evidence package includes the exact source, dependency lock, SQL view and grant definitions, permission outcomes, unchanged-state check, actual plans, I/O messages and framework tool-call counts. The raw reports distinguish application rejection from database denial and deterministic provider behavior from model reasoning.

The strongest conclusion is specific. The real framework invoked a fixed SQL tool under a verified restricted database context. Approved observations succeeded. Selected mutations and unauthorized reads were refused by SQL Server. Unsupported proposals never reached SQL. An incorrect performance hypothesis was rejected using actual plan evidence.

The remaining limits are equally specific. There is no arbitrary-code containment, no production login isolation, no real-model quality evaluation and no broad server-administration capability. The diagnostic view and SHOWPLAN grant expose only the scope described by the fixture, and the container is removed when the run ends.

That is what makes the read-only claim useful. It is a testable statement about an implemented boundary, accompanied by the cases that boundary accepts and refuses. A prompt can express the intention, but the database and application must make the intention true.

## Source code and execution evidence

[Download the complete lab, source record and raw execution results](https://devinside.cloud/content/files/2026/11/ENG14-lab.zip). This package supports the specific execution claims in this article. It is not a production certification.