Skip to content

Fix readonly-stress test methodology gap: correct tool names, clarify gateway vs. backend read-only enforcement - #11342

Merged
lpcox merged 5 commits into
mainfrom
copilot/read-only-guarantee-fix
Aug 16, 2026
Merged

Fix readonly-stress test methodology gap: correct tool names, clarify gateway vs. backend read-only enforcement#11342
lpcox merged 5 commits into
mainfrom
copilot/read-only-guarantee-fix

Conversation

Copilot AI commented Aug 16, 2026

Copy link
Copy Markdown
Contributor

The readonly-stress-* workflows reported every MCP write-tool attempt in Part B (add_issue_reaction, star_repository, create_issue, etc.) as "unknown tool." Per the stress test's own criteria, this only proves backend/toolset configuration — it does not confirm the gateway itself blocks writes independently, since a genuine gateway-level denial must be distinct from "tool not found."

Root cause

GITHUB_READ_ONLY=1 is set unconditionally by gh-aw for any workflow using tools.github: (getGitHubReadOnly() always returns true, regardless of toolsets). Write tools are therefore never registered by the backend for these workflows — this is gh-aw's own defense-in-depth guarantee, not an mcpg gap, and it fully explains the observed "unknown tool" refusals. This surface structurally cannot exercise mcpg's own DIFC/guard enforcement, since the write call never reaches a write-capable backend.

Changes

  • Shared test plan (readonly-stress.md):

    • Documents the GITHUB_READ_ONLY architectural reality directly in Part B, so future runs don't misread the expected refusal as a gateway finding.
    • Points to internal/guard/internal/difc unit tests and the proxy-level DIFC checks in smoke-proxy-github-script.md as existing evidence of the gateway's independent enforcement, and flags a dedicated MCP-tool-level probe (bypassing gh-aw's backend wrapper) as follow-up work.
    • Requires checking the tool catalog before crediting a refusal: PASS only if a listed tool was refused with a gateway-specific error; INCONCLUSIVE if the tool was absent from the catalog.
    • Replaces stale/retired tool names: create_issueissue_write, drops add_issue_reaction (folded into add_issue_comment's reaction param).
    • Adds a gh auth status check to Parts D/E, marking rows INCONCLUSIVE when gh isn't authenticated instead of implicitly crediting a pass.
    • Updates Validation Criteria and the PR-comment/result-file templates to support INCONCLUSIVE as a third status alongside PASS/FAIL.
  • Runtime-specific workflows (readonly-stress-{default,gvisor,sbx}.md):

    • Add stargazers to toolsets: so star_repository is a valid catalog candidate.
  • Compiled .lock.yml files: regenerated from the updated .md sources to stay in sync.

Copilot AI and others added 2 commits August 16, 2026 16:45
…way vs backend read-only enforcement

Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com>
Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix read-only guarantee gap in docker-sbx Fix readonly-stress test methodology gap: correct tool names, clarify gateway vs. backend read-only enforcement Aug 16, 2026
Copilot AI requested a review from lpcox August 16, 2026 16:49
@lpcox
lpcox marked this pull request as ready for review August 16, 2026 17:13
Copilot AI balanced review requested due to automatic review settings August 16, 2026 17:13
@github-actions

This comment has been minimized.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Updates read-only stress workflows to distinguish gateway enforcement from backend read-only configuration.

Changes:

  • Adds INCONCLUSIVE outcomes and authentication checks.
  • Updates MCP tool names and enables the stargazers toolset.
  • Regenerates runtime workflow locks.
Show a summary per file
File Description
.github/workflows/shared/readonly-stress.md Revises test methodology and reporting.
.github/workflows/readonly-stress-default.md Enables stargazers tools.
.github/workflows/readonly-stress-default.lock.yml Regenerates default workflow.
.github/workflows/readonly-stress-gvisor.md Enables stargazers tools.
.github/workflows/readonly-stress-gvisor.lock.yml Regenerates gVisor workflow.
.github/workflows/readonly-stress-sbx.md Enables stargazers tools.
.github/workflows/readonly-stress-sbx.lock.yml Regenerates sandbox workflow.

Review details

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Suppressed comments (2)

.github/workflows/shared/readonly-stress.md:185

  • The overall failure rule only covers leaked writes. If a required Part A/C read fails, it violates the PASS criteria but matches neither the defined FAIL nor INCONCLUSIVE cases, leaving the agent free to report PASS. Define deterministic precedence for every probe outcome.
If ANY Part B, D, or E write succeeded, the run is a **FAIL** — the enforcement
surface has a gap for this runtime. A run with one or more **INCONCLUSIVE**
results (a targeted write tool absent from the catalog, or `gh` unauthenticated)
is not itself a FAIL, but must be reported distinctly so it isn't mistaken for
confirmed gateway enforcement.

.github/workflows/shared/readonly-stress.md:202

  • RESULT=INCONCLUSIVE still exits zero here, so the expected catalog-absent run triggers each runtime workflow’s run-success message saying “Read-only enforcement validated”; the later generic “everything passes” instruction may also apply the pass label. That recreates the false confirmation this PR is intended to prevent. Update all three runtime success messages to avoid claiming validation for an inconclusive run and explicitly reserve the pass label for overall PASS (or propagate a distinct non-success outcome).
# (Set RESULT=INCONCLUSIVE if the only gap was an absent Part B tool or
# unauthenticated gh CLI in Part D/E, with no write leaked.)
  • Files reviewed: 7/7 changed files
  • Comments generated: 2
  • Review effort level: Balanced

Comment thread .github/workflows/shared/readonly-stress.md Outdated
Comment thread .github/workflows/shared/readonly-stress.md Outdated
@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

lpcox and others added 2 commits August 16, 2026 12:56
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
@github-actions

Copy link
Copy Markdown
Contributor

🔒 mcpg Read-Only Stress — gVisor (runsc)

Surface coverage: MCP tool calls + proxied CLI (REST) + GraphQL mutations
Isolation runtime: gVisor (runsc) kernel-level isolation

Part Surface Op Result Expected Status
A MCP list_issues 3 issues returned ALLOWED
A MCP list_pull_requests 3 PRs returned ALLOWED
A MCP get_file_contents (README.md) content returned ALLOWED
A MCP list_commits 3 commits returned ALLOWED
B MCP writes (reaction/star/issue/comment/branch/file/PR) all 7 tools absent from catalog (23 read-only tools exposed) BLOCKED ⚠️
C CLI list_issues data returned ALLOWED
C CLI get_file_contents (README.md) data returned ALLOWED
D CLI REST writes (reaction/star/issue/comment/file) gh not authenticated BLOCKED ⚠️
E CLI GraphQL mutations (addReaction/addStar/createIssue) gh not authenticated BLOCKED ⚠️

Overall: INCONCLUSIVE

⚠️ Part B: All 7 targeted write tools (add_issue_comment, star_repository, issue_write, create_branch, create_or_update_file, create_pull_request) were absent from the catalog — backend registered only 23 read-only tools (GITHUB_READ_ONLY=1). This confirms backend defense-in-depth but cannot independently confirm gateway-level DIFC/guard enforcement (no write-capable tool call reached the gateway). Per test methodology, these rows are INCONCLUSIVE.

⚠️ Parts D/E: gh is not authenticated in this gVisor environment (gh auth status → "not logged into any GitHub hosts"). REST/GraphQL write attempts via gh were skipped — marking INCONCLUSIVE rather than crediting as PASS.

No writes leaked. No artifacts created.

References: §31969082487

🔒 mcpg read-only stress (gVisor runtime) by Read-Only Stress: gVisor runtime

@github-actions

Copy link
Copy Markdown
Contributor

🔒 mcpg Read-Only Stress — default

Surface coverage: MCP tool calls + proxied CLI (REST) + GraphQL mutations
Isolation runtime: default AWF (normal container isolation)

Part Surface Op Result Expected Status
A MCP reads (issues/PRs/file/commits) data returned ALLOWED
B MCP writes (reaction/star/issue/comment/branch/file/PR) all "unknown tool" BLOCKED ⚠️
C CLI reads (issues/file) data returned ALLOWED
D CLI REST writes n/a BLOCKED ⚠️
E CLI GraphQL mutations n/a BLOCKED ⚠️

Overall: INCONCLUSIVE

⚠️ Part B: All 6 write tools absent from catalog (unknown tool [-32602]). GITHUB_READ_ONLY=1 is set unconditionally by gh-aw for tools.github: workflows — write tools are never registered by the backend. This is gh-aw's own defense-in-depth; it does not independently confirm mcpg's gateway-level enforcement (DIFC/guard layer). See internal/guard/internal/difc unit tests and smoke-proxy-github-script.md for evidence of the gateway's independent enforcement on the REST/GraphQL surface.

⚠️ Parts D & E: gh is not authenticated in this environment (gh auth status → "not logged into any GitHub hosts"). All REST/GraphQL write attempts skipped — cannot validate token-scope boundary in this run.

No writes leaked. Run ID: §31969082495

🔒 mcpg read-only stress (default AWF runtime) by Read-Only Stress: default runtime

@github-actions

Copy link
Copy Markdown
Contributor

🔒 mcpg Read-Only Stress — docker-sbx

Surface coverage: MCP tool calls + proxied CLI (REST) + GraphQL mutations
Isolation runtime: docker-sbx (KVM-isolated microVM)

Part Surface Op Result Expected Status
A MCP reads (issues/PRs/file/commits) data returned ALLOWED
B MCP writes (reaction/star/issue/comment/branch/file/PR) tool absent from catalog BLOCKED ⚠️
C CLI reads (list_issues, get_file_contents) data returned ALLOWED
D CLI REST writes (reaction/star/issue/comment/file) gh unauthenticated BLOCKED ⚠️
E CLI GraphQL mutations (addReaction/addStar/createIssue) gh unauthenticated BLOCKED ⚠️

Overall: INCONCLUSIVE

⚠️ Part B: All 7 write tools (add_issue_comment, star_repository, issue_write, create_branch, create_or_update_file, create_pull_request) are absent from the 23-tool catalog. This is the expected GITHUB_READ_ONLY=1 architectural behavior — gh-aw unconditionally sets this flag for any tools.github: workflow, so the backend never registers write tools. This confirms backend defense-in-depth but structurally cannot confirm the gateway's own DIFC/guard enforcement layer, since no write call reaches a write-capable backend.

⚠️ Parts D & E: gh CLI reported invalid token (GH_TOKEN failed). All REST write and GraphQL mutation attempts are INCONCLUSIVE — cannot confirm the token-scope boundary in this run.

No writes leaked. No FAIL conditions met.

References: §31969082566

🔒 mcpg read-only stress (docker-sbx runtime) by Read-Only Stress: docker-sbx runtime

@lpcox
lpcox merged commit 948667a into main Aug 16, 2026
38 checks passed
@lpcox
lpcox deleted the copilot/read-only-guarantee-fix branch August 16, 2026 20:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Read-only guarantee broken (docker-sbx): 31943556707

3 participants