Skip to content

FEAT-066: scry-mcp — analyze + query as agent tools, verify structurally absent - #190

Merged
avrabe merged 2 commits into
mainfrom
feat-066-mcp-server
Aug 28, 2026
Merged

FEAT-066: scry-mcp — analyze + query as agent tools, verify structurally absent#190
avrabe merged 2 commits into
mainfrom
feat-066-mcp-server

Conversation

@avrabe

@avrabe avrabe commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

New publishable crate crates/scry-mcp (scry-sai-mcp), modelled on scry-viz: lib +
[[bin]] stdio loop. Hand-rolled JSON-RPC 2.0 on the existing serde_json workspace
dep — no MCP SDK, no new external dependency to clear cargo-deny.

analyze returns counts only (by class, by code, trap verdicts, gaps by kind) — never
HTML, never a dump. query wraps FEAT-067's filter with total_matches and a limit cap.

AC2 is the point of this PR

verify must be absent rather than present-and-unreliable — the deferral enforced
by the tool list, not by documentation.

Same family as DD-022: a deferral a consumer can rely on must be structural. And the
reason is now measured, not cautious — REQ-021 found discharged is 0 on real inputs and
every verdict degrades to uncertain. Exposing that over MCP puts an always-uncertain
verdict directly into an agent's tool loop. That rationale lives in the
tool_definitions() doc comment, so nobody "helpfully" adds it later.

I nearly reported a good test as weak

My first independent mutant — a duplicate "name" line inside the query object —
applied (count asserted) and compiled (0 errors) and the test still passed. It
changed nothing: JSON keeps the last key, so no third tool ever appeared.

Mutating properly (a whole third tool object appended to the json! array) turns it red:

left: ["analyze", "query", "verify"]

The mutation ladder has three rungs: applied → compiled → actually creates the
condition.
Only the third proves anything, and all three look identical in the output.

The agent's own five mutants were each applied-asserted, compiled, and killed exactly one
test — including the AC2 one.

Release-machinery note — verified, not introduced here

cargo package -p scry-sai-mcp fails verify against crates.io scry-sai-core 3.2.7,
which predates the Query API. scry-sai-viz on main fails identically (it uses
FEAT-065's VerifyReport, also absent from 3.2.7).

So: pre-existing mid-cycle condition for any crate consuming an unreleased core API,
resolved at release by the version bump + leaf-before-core publish order.

cargo package is not a valid pre-release check mid-cycle — it fails for a correct
tree, so a release runner reaching for it gets a red that means nothing.

Wiring — all four places

Cargo.toml members and default-members · ci.yml both test and clippy blocks
(check-gate-coverage.py"OK — 13 publishable crates in BOTH gates", negative control
run) · scripts/publish.rs leaf-before-core · required-checks.txt untouched (no new job).

publish = true forced a claim coupling: README "12 pure" → "13 pure" and claims.yaml
CRATES-12 → CRATES-13, updated in lockstep — claim-check was observed red between the
publish.rs and README edits, then 7/7.

tests=0 clippy=0 fmt=0 rivet=0 claim-check=0 gate-coverage=0 drift-gate=0

🤖 Generated with Claude Code

https://claude.ai/code/session_01KkNzkNYzPh7366DkNijeNc

avrabe and others added 2 commits August 28, 2026 01:33
…y structurally absent

New publishable crate crates/scry-mcp (scry-sai-mcp): an MCP server over
newline-delimited JSON-RPC 2.0 on stdio (initialize / tools/list /
tools/call), hand-rolled on serde_json — deliberately no MCP SDK dependency
(nothing new to clear cargo-deny). Modeled on scry-viz: a plain std host
tool whose only analyzer dependency is scry-sai-core.

Tools (the full v3.3.0 surface):
- analyze: module path (.wasm/.wat) -> compact structured summary — advisory
  counts by class and code, trap verdicts (proven-safe / potential-trap),
  gap counts. Never HTML, never a multi-MB dump (AC#1).
- query: AnalysisResult::query (FEAT-067) over MCP — class / code /
  func_index / op / gap_kind filters, ANDed; matches carry the REQ-020
  stable obligation identity + honesty flags; limit-capped with an exact
  total_matches.

AC#2 is structural (DD-022 family): `verify` is ABSENT from the tools/list
payload itself, and the test asserts against that actual payload — not
documentation. The deferral is measured, not cautious: REQ-021 found
FEAT-065's verify_against yields discharged=0 with every verdict degrading
to `uncertain` on real (stripped) inputs; exposing that over MCP would put
an always-uncertain verdict directly into an agent's tool loop. The tool
follows FEAT-065 into v3.4.0.

Verification: 10 tests, each written RED-first against a stub (all 10
observed failing), then GREEN; 5 mutants (verify added to the tool list,
by_class count inverted, query filters ignored, limit cap dropped,
notifications answered) each applied at exactly 1 site, each compiled with
0 errors, each killed by exactly the targeted test. End-to-end stdio smoke
of the binary passed. check-gate-coverage.py negative control observed red
with the crate missing from a gate.

Wiring (all four places): workspace members + default-members; BOTH cargo
test and cargo clippy lists in ci.yml (check-gate-coverage.py: 13 crates in
both gates); scripts/publish.rs after scry-sai-core (leaf-before-core); no
new CI job so required-checks.txt untouched. README + claims.yaml crate
count moved 12 -> 13 in lockstep (CRATES-13).

Known mid-cycle condition (pre-existing pattern, not introduced here):
cargo package verify of scry-sai-mcp fails against crates.io scry-sai-core
3.2.7, which predates the Query API — identical to scry-sai-viz on main
(uses FEAT-065 types absent from 3.2.7). Resolved at release by the
version bump + leaf-first publish order.

Refs: FEAT-066
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KkNzkNYzPh7366DkNijeNc
Reviewed the delegated implementation rather than accepting its report, and
independently re-ran the two load-bearing claims.

AC2's structural guard WORKS -- but my first mutant did not prove it. Appending
a duplicate "name" line inside the query object APPLIED (count asserted) and
COMPILED (0 errors) and changed nothing, because JSON keeps the last key, so no
third tool ever appeared and the test passed. Mutating properly -- a whole third
tool object appended to the json! array -- turns it red with
  left: ["analyze", "query", "verify"]
So the mutation ladder has a third rung: APPLIED, then COMPILED, then ACTUALLY
CREATES THE CONDITION. Only the third proves anything, and all three look alike
in the output. Stopping at rung two would have had me report a correct test as
ineffective.

RELEASE-MACHINERY NOTE, verified and not introduced here: `cargo package -p
scry-sai-mcp` fails verify against crates.io scry-sai-core 3.2.7, which predates
the Query API. Confirmed `scry-sai-viz` ON MAIN fails identically (it uses
FEAT-065's VerifyReport, also absent from 3.2.7). Pre-existing mid-cycle
condition affecting any crate consuming an unreleased core API; it resolves at
release via the version bump plus leaf-before-core publish order.

The consequence worth writing down: `cargo package` is NOT a valid pre-release
check mid-cycle. It fails for a correct tree, so a release runner reaching for it
as a smoke test gets a red that means nothing.

tests=0 clippy=0 fmt=0 rivet=0 claim-check=0 gate-coverage=0 drift-gate=0.

Refs: FEAT-066

Claude-Session: https://claude.ai/code/session_01KkNzkNYzPh7366DkNijeNc

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

📐 rivet artifact delta

PR: #190 Base SHA: c91d62ac

Validation

head — `rivet validate` result
  SR-11 (sw-req, status: accepted) — missing: sw-integration-verification, unit-verification
  SR-12 (sw-req, status: accepted) — missing: sw-integration-verification, unit-verification
  SR-13 (sw-req, status: accepted) — missing: sw-integration-verification, unit-verification
  SR-2 (sw-req, status: accepted) — missing: sw-integration-verification, unit-verification
  SR-3 (sw-req, status: accepted) — missing: sw-integration-verification, unit-verification
  SR-4 (sw-req, status: accepted) — missing: sw-integration-verification, unit-verification
  SR-5 (sw-req, status: accepted) — missing: sw-integration-verification, unit-verification
  SR-6 (sw-req, status: accepted) — missing: sw-integration-verification, unit-verification
  SR-7 (sw-req, status: accepted) — missing: sw-integration-verification, unit-verification
  SR-8 (sw-req, status: accepted) — missing: sw-integration-verification, unit-verification
  SR-9 (sw-req, status: accepted) — missing: sw-integration-verification, unit-verification
  SYS-1 (system-req, status: accepted) — missing: sys-integration-verification
  SYS-2 (system-req, status: accepted) — missing: sys-integration-verification
  SYS-3 (system-req, status: accepted) — missing: sys-integration-verification
  SYS-4 (system-req, status: accepted) — missing: sys-integration-verification
  SYS-5 (system-req, status: accepted) — missing: sys-integration-verification
  → run `rivet validate --explain SR-1` to see which link type and source types satisfy a gap

Result: PASS (171 warnings)
Schemas: common@0.3.0 (embedded), dev@0.3.0 (embedded), research@0.1.0 (embedded), research-ext@0.1.0 (on-disk), safety-case@0.1.0 (embedded), aspice@0.2.0 (embedded)
base — `rivet validate` result (for comparison)
  SR-11 (sw-req, status: accepted) — missing: sw-integration-verification, unit-verification
  SR-12 (sw-req, status: accepted) — missing: sw-integration-verification, unit-verification
  SR-13 (sw-req, status: accepted) — missing: sw-integration-verification, unit-verification
  SR-2 (sw-req, status: accepted) — missing: sw-integration-verification, unit-verification
  SR-3 (sw-req, status: accepted) — missing: sw-integration-verification, unit-verification
  SR-4 (sw-req, status: accepted) — missing: sw-integration-verification, unit-verification
  SR-5 (sw-req, status: accepted) — missing: sw-integration-verification, unit-verification
  SR-6 (sw-req, status: accepted) — missing: sw-integration-verification, unit-verification
  SR-7 (sw-req, status: accepted) — missing: sw-integration-verification, unit-verification
  SR-8 (sw-req, status: accepted) — missing: sw-integration-verification, unit-verification
  SR-9 (sw-req, status: accepted) — missing: sw-integration-verification, unit-verification
  SYS-1 (system-req, status: accepted) — missing: sys-integration-verification
  SYS-2 (system-req, status: accepted) — missing: sys-integration-verification
  SYS-3 (system-req, status: accepted) — missing: sys-integration-verification
  SYS-4 (system-req, status: accepted) — missing: sys-integration-verification
  SYS-5 (system-req, status: accepted) — missing: sys-integration-verification
  → run `rivet validate --explain SR-1` to see which link type and source types satisfy a gap

Result: PASS (171 warnings)
Schemas: common@0.3.0 (embedded), dev@0.3.0 (embedded), research@0.1.0 (embedded), research-ext@0.1.0 (on-disk), safety-case@0.1.0 (embedded), aspice@0.2.0 (embedded)

Artifact stats

base head
Total artifacts 263 263
full stats — head
Artifact summary:
  academic-reference               24
  competitive-analysis             11
  design-decision                  22
  feature                          93
  market-finding                    7
  requirement                      21
  safety-context                    3
  safety-goal                       5
  safety-justification              4
  safety-solution                   6
  safety-strategy                   1
  stakeholder-req                   3
  sw-req                           13
  sw-verification                  13
  sys-verification                  5
  system-req                        5
  technology-evaluation            12
  verification                     15
  TOTAL                           263

Orphan artifacts (no links): 12
  CA-001
  CA-002
  CA-003
  CA-004
  CA-005
  CA-006
  CA-007
  CA-008
  CA-009
  CA-010
  CA-011
  FEAT-078

Diagnostics: 0 error(s), 171 warning(s), 26 info(s)

Diff (base → head)

~ FEAT-066
  description: changed

0 added, 0 removed, 1 modified, 262 unchanged

AADL model — head

spar/scry.aadl: OK

Posted by the rivet-delta workflow. Informational only — does not gate the PR.

@avrabe
avrabe merged commit 8c5ddac into main Aug 28, 2026
13 checks passed
@avrabe
avrabe deleted the feat-066-mcp-server branch August 28, 2026 00:22
avrabe added a commit that referenced this pull request Aug 28, 2026
…191)

FEAT-066 -> accepted. Merged in #190 with 13/13 CI green; its 10 tests re-run on
main, gate-coverage reports 13 publishable crates in BOTH gates. Both ACs met,
and AC2's structural guarantee was mutation-verified independently: appending a
third tool object turns it red with ["analyze", "query", "verify"].

BUT PROMOTING IT ALONE WOULD HAVE DELETED WORK FROM THE PLAN. FEAT-066's title
promises `analyze / query / verify`; its description defers `verify` to v3.4.0;
and NOTHING carried the deferred half -- grepping every artifact for MCP returns
FEAT-066 and nothing else. Mark it accepted and the verify tool stops existing
anywhere in the plan. That is exactly scry#157/#160: an implementation with no
artifact, invisible to the release plan, resurfacing later as a surprise. Here it
would have been the inverse -- a plan item quietly evaporating on promotion.

So FEAT-094 is filed first, in v3.4.0, `depends-on REQ-021`.

Its deferral is measured, not cautious: REQ-021 measured `discharged` = 0 on
every real commit pair, gate failing on all of them, ~47% uncertain, and
`discharged` unreachable by construction on a stripped module. An always-
uncertain verdict in an agent's tool loop is worse than an absent tool -- an
absent tool is a fact an agent plans around; a useless one is noise it must
learn to ignore.

FEAT-094's AC3 is the part worth keeping: when `verify` is eventually added,
FEAT-066's AC2 test must be UPDATED, not deleted. The structural guarantee moves
from "verify is absent" to "verify is present and its verdict set is complete".
A guard removed is a guard that stops being checked.

v3.3.0 accepted 7/proposed 3; v3.4.0 accepted 9/proposed 3.

rivet=0 claim-check=0 fmt=0 drift-gate=0 gate-coverage=0.

Claude-Session: https://claude.ai/code/session_01KkNzkNYzPh7366DkNijeNc

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant