[Bug] Artifact publish incorrectly flags dynamic SQL concatenation as "pr_review" page
Bug Description
Title: Artifact publish rejected as a "pr_review" page when page JS builds a SQL string by concatenation (likely false positive) — page cannot declare any capability
Environment
- Claude Code 2.1.286 (CLI, macOS), Team plan account
- Artifact runtime contract 0.2.72
- Observed on 2026-10-07, about 19:55–21:30 JST (reproducible every time)
What happens
Publishing an internal single-page app as a new Artifact with capabilities {"downloads": true, "sample": {}, "mcp": {...}} fails with:
"the pr_review capabilities manifest failed validation: the review page capabilities declaration carries unknown capability families — only mcp and the artifact-publish capability (artifact, legacy spelling self) have review-page meaning, so nothing unexamined rides to the control plane"
Reducing the capabilities to mcp only then fails with:
"the pr_review capabilities manifest failed validation: a review page with no live binding must not declare an mcp manifest — its pinned scripts call no tool"
So tcan only be published with no capabilities at all, which makes the app unusable (it reads all its data through the viewer's connectors).
Minimal repro
Page file (publish as a new artifact, or as an update with url):
<title>probe</title>
<p>probe</p>
<script>
function toInt(v) { return String(Math.round(Number(v))); }
function f(id) {
return "SELECT name FROM customers WHERE id = " + toInt(id) + ' LIMIT 1';
}
</script>
Capabilities: {"downloads": true, "sample": {"images": true}, "mcp": {"servers": [{"server": "<any connected connector>", "tools": ["<tool>"]}]}}
Result: refused with the first error above.
Control: the same page with the literal query return "SELECT name FROM customers WHERE id = 1 LIMIT 1"; publishes normally with the same capabilities.
What we ruled out (each tested)
- Session-wide state: in the same session, three other app pages published fine with db, user, downloads, sample (images) and mcp.
- Page size: one of those pages padded to 6.6 MB still published fine; the failing app is 6.4 MB.
- The label parameter, the set of MCP servers, and a write tool in the manifest: removing each did not change the result.
- We bisected the real 6.4 MB app (141 bundled JS files) down to a single line of the same shape as the repro above. Another app that builds SQL with an escaping helper publishes fine, so the check seems to match a narrow textual pattern.
Why we think this is a false positive
- The concatenated value is coerced to an integer by a helper before it is put into the query; string values go through a quoting helper that doubles single quotes.
- The query is sent only to the viewer's own connector tool, which is documented as SELECT-only, and runs with the viewer's own credentials.
- The page is an ordinary internal tool, not a pull-request review page.
Impact
We are moving our team's internal apps from a personal plan account to our Team plan account. This app (an internal insurance comparison tool) cannot be republished on the Team plan, so our team cannot use it there. We have chosen not to rewrite the code just to avoid the check, because we do not know what the check is meant to protect against.
Questions
- Is this check intended? What exactly makes a page a "pr_review" / "review page", and what is a "live binding"?
- How should a legitimate internal app that composes queries for its own connector calls be written so that it can declare downloads, sample and mcp?
- Could the check and both error messages be documented on https://code.claude.com/docs/en/artifacts.md (it is not mentioned there today)?
A related, smaller note: the publish also refuses a page containing a literal U+FFFD character (we had one inside a regular expression); writing it as � works. That message was clear and easy to fix.
Environment Info
- Platform: darwin
- Terminal: cursor
- Version: 2.1.286
- Feedback ID: 3b1d3572-2620-4553-8082-169895391e8d
Errors
[]