Advertise internal worker launches and temporary source writes through the canonical effects owner so MCP hints and session availability agree with existing analysis behavior. Preserve non-destructive, idempotent semantics. Verify public metadata boundaries and a real Obsidian CLI/MCP workflow, retaining compact reproduction and cost records. Fixes #3867 Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
5.6 KiB
JavaScript analysis effect metadata verification
Tracks #3867 under #4027. The canonical effect record omitted existing internal
worker launches and source staging. CapabilityInventory and SDK registration
already consume that owner directly; no adapter override is required. The
worker creates a
private runtime root, stages .source files and launches an owned REA worker.
These are source-derived facts; the analyzed application code is not executed.
Executed results
Public stdio MCP before/after:
| Surface | Before | After |
|---|---|---|
tools/list openWorldHint |
false |
true |
binary_session effects |
mutatesSession, idempotent |
also writesFilesystem, launchesProcess |
| Session annotation flags | idempotent |
also open_world |
| Advertised tools | 207 | 207 |
Regression assertions failed on the original contract: 2 failed, 18 passed. After correction and separating the target-free metadata case, 27 passed across canonical effects, public Electron/JavaScript MCP and SDK-converted catalog validity. The tests retain the non-destructive, idempotent hints and exact session effects; no foreign target execution is declared.
The existing real verifier analyzed official Obsidian 1.14.4 with package.json
and main.js selected, then read the typed module references and verified exact
public CLI/MCP parity. Both revisions returned the same parent/view Evidence
identities and 23-row fact digest. Query replies remained 22,158 bytes and the
selected-schema task remained 16,548 cl100k_base BPE tokens (204,093 with the full
name/description/inputSchema catalog projection). These are static counts, not
model billing; annotations, the separate metadata probe, host rendering/replay,
reasoning and final answers are outside this projection. No hosted agent was run.
| Complete real verifier | Before | After |
|---|---|---|
| Elapsed seconds | 30.470 | 31.059 |
| CPU seconds | 29.939807 | 30.663163 |
| Peak cgroup bytes | 1,551,282,176 | 1,584,574,464 |
The scope includes shared analysis, temporary export, CLI parity and tokenizer. The metadata-only probe separately used 10.711/10.909 s wall, 10.503415/10.664997 s CPU and 842,891,264/853,958,656 B peak cgroup memory. All scopes used one CPU, an 8 GiB hard cap, 6 GiB early stop and no swap; cleanup was verified with no OOM. Memory includes charged file cache, not summed RSS. Counters were observed at payload exit while the observer remained alive. Fresh MCP sessions do not establish an OS-cold cache. Shared-host scheduling and filesystem/build warmness were uncontrolled; one pair establishes neither a performance improvement nor an equivalence bound. Linux x64 static ASAR analysis was executed; live Electron and other platforms were not exercised locally. Compact records and source hashes preserve these distinctions without archiving the downloadable application.
Reproduce
Use pinned Node/npm, npm ci, then npm run build:cached. Run the following as
an ES module from the checkout root (node --input-type=module < probe.mjs):
import assert from "node:assert/strict";
import { Client } from "@modelcontextprotocol/client";
import { StdioClientTransport } from "@modelcontextprotocol/client/stdio";
import { toolAvailabilityInventory } from "./dist/contracts/toolOutputSchemaPrimitives.js";
import { closeVerifierResources } from "./scripts/lib/verifier-cleanup.mjs";
const client = new Client({
name: "javascript-effect-verification",
version: "1",
});
const transport = new StdioClientTransport({
command: process.execPath,
args: ["scripts/rea.mjs", "mcp"],
stderr: "pipe",
});
let primaryError;
try {
await client.connect(transport);
transport.stderr?.resume();
const { tools } = await client.listTools();
const tool = tools.find(
({ name }) => name === "analyze_javascript_application",
);
assert.ok(tool);
const response = await client.callTool({
name: "binary_session",
arguments: {},
});
assert.notEqual(response.isError, true);
const inventory = toolAvailabilityInventory.parse(
response.structuredContent?.result?.tool_availability,
);
const group = inventory.groups.find(({ names }) => names.includes(tool.name));
assert.ok(group);
console.log(
JSON.stringify({
tool: tool.name,
advertised_annotations: tool.annotations,
session_effects: group.effects,
session_annotations: group.annotations,
catalog_tool_count: tools.length,
}),
);
} catch (cause) {
primaryError = cause;
throw cause;
} finally {
await closeVerifierResources(
[() => client.close(), () => transport.close()],
primaryError,
);
}
For the real workflow, follow the artifact acquisition/digest instructions in
module-reference verification.
Run its existing command with --mode references --rounds 1 --cli-parity and the
same selected source paths and tokenizer. That verifier is unchanged by this
fix. The preserved 8 GiB cgroup observer and its source hashes are documented in
the same guide. The machine-readable records separate metadata-probe resources
from the complete real-workflow resources.