Lesson 2 of 6 · 0%Make inputs, outputs, and failure modes explicitNext
Course map

Skills, Tools, MCP and Browser Automation

0 of 6 complete0 of 6

Lesson 2.2 · 45 minutes

Make inputs, outputs, and failure modes explicit

Define a capability contract that is bounded, typed, testable, and honest about unavailable evidence.

Skip course map

Verifiedon 2026.7.1

Action boundary

Before you act

Expected result
A reviewer can validate requests without running the capability and can tell every failure from a successful empty result.
Failure mode
Free-form input, ambiguous output, or silent fallback makes a side effect or unsupported claim appear successful.
Rollback
Disable the capability and return a typed validation error until the contract and fixtures are corrected.

A description is not a contract

“Research the web” is a goal, not an interface. It says nothing about source scope, maximum work, evidence quality, or whether the caller can ask the capability to send, publish, or delete. A safe extension starts with a contract that can reject a request before it invokes a tool.

The reviewed OpenClaw MCP documentation is the primary receipt for the integration boundary. Pair it with the skills documentation when the contract is being packaged as reusable instructions. Those sources tell you what OpenClaw exposes at the pinned revision; your contract must still define what this particular capability accepts and refuses.

The contract has four gates: input validation, allowlist and policy, bounded invocation, and evidence output. Every gate must have an observable result. A caller should never need to inspect a log and infer whether a malformed request was rejected or whether a remote source simply had no matching findings.

Contract gates and honest statuses
  1. Input validationReject missing fields, overlong queries, invalid URLs, unknown modes, and credentials.
  2. Allowlist and policyCheck approved sources, side-effect intent, timeout, cost, and human-gate requirements.
  3. InvocationCall only the bounded tool or fixture after the previous gates pass.
  4. Evidence resultReturn status, findings, URLs, timestamps, run ID, and sideEffects explicitly.

Contract flow showing validation, policy, invocation, evidence, and pre-invocation denial paths.

Define the request before the implementation

For the course capstone, use a request shape like this:

{
  "query": "OpenClaw 2026.7.1 release notes",
  "sources": ["https://github.com/openclaw/openclaw/releases"],
  "maxFindings": 3,
  "mode": "dry-run"
}

A useful schema decision table looks like this:

Field Rule Reason for the bound
query required string, 1–160 characters prevents unbounded prompt and retrieval cost
sources one to three exact HTTPS origins or paths prevents arbitrary discovery and exfiltration
maxFindings integer from 1 to 3 keeps review effort predictable
mode dry-run by default; no live-write mode in this course makes safe behavior the default
credentials forbidden the public research job does not need them
local paths forbidden prevents accidental filesystem expansion

Do not allow the model to rewrite these policy fields. If a caller includes mode: "publish", the contract should return side_effect_blocked; it should not ask the model whether publishing “seems safe.” Policy is code or a reviewed configuration boundary, not a suggestion in natural language.

Define results so absence is not success

A good result distinguishes status from findings:

{
  "status": "ok",
  "findings": [
    {
      "claim": "Release note found",
      "url": "https://github.com/openclaw/openclaw/releases",
      "retrievedAt": "2026-07-30T00:00:00Z",
      "sourceId": "openclaw-releases"
    }
  ],
  "sideEffects": [],
  "runId": "fixture-001"
}

An empty findings array can be valid when the source was reached and no matching evidence exists. It is not valid to use an empty result to hide a 403, timeout, parse error, or policy denial. Use a small, stable status vocabulary:

  • ok: the allowlisted source was reached and the result is evidence-backed, even when findings are empty;
  • invalid_request: the request failed validation and invocation did not occur;
  • source_unavailable: the allowlisted source could not be reached or verified; include the source and retry guidance;
  • side_effect_blocked: the request asked for a write or irreversible action and was stopped before invocation;
  • unknown_after_timeout: a timeout occurred after a state-changing boundary may have been crossed; stop and require inspection.

The sideEffects field should be present in every result and equal [] for this capstone. That invariant makes an accidental write visible in a test diff rather than relying on a reviewer to interpret prose.

Contract validator fixture
const input = { query, sources, maxFindings, mode };
if (!Number.isInteger(maxFindings) || maxFindings < 1 || maxFindings > 3)
return { status: 'invalid_request', reason: 'maxFindings must be 1..3', sideEffects: [] };
if (sources.some((source) => !ALLOWED_SOURCES.has(source)))
return { status: 'invalid_request', reason: 'source is not allowlisted', sideEffects: [] };
if (mode !== 'dry-run')
return { status: 'side_effect_blocked', reason: 'dry-run is the only allowed mode', sideEffects: [] };
return invokeFixture(input);

Expected output: invalid_request and side_effect_blocked complete with zero invocation count

Worked example — classify three failures

Imagine three requests arrive. First, maxFindings: 99: return invalid_request, with the field-level reason and no network call. Second, the approved releases URL returns HTTP 403: return source_unavailable and preserve the URL; do not silently switch to a mirror. Third, the caller asks to send the summary to a channel: return side_effect_blocked before invoking retrieval or messaging. The status tells the operator what happened and where to look next.

A common anti-pattern is “helpful fallback”: if the approved source fails, the capability searches the entire web, finds a similar page, and returns ok. That changes both authority and evidence. Another is “best effort” parsing that converts an invalid payload into a default query. Defaults are safe only when they are explicit, documented, and testable. A default must never broaden the source or action boundary.

Hands-on lab — write the negative cases first

Create four fixture files or structured test objects: valid request, overlong query, source outside the allowlist, and a request that asks to send, publish, or delete. For each, record expected status, invocation count, and sideEffects. The last three must finish without network or browser activity. Add a fifth case for an allowlisted source that returns no matching evidence; it should be ok with findings: [], not a failure.

Expected output is a table like this:

Fixture Status Invocation Side effects
valid ok 1 fixture call []
overlong invalid_request 0 []
outside allowlist invalid_request 0 []
publish request side_effect_blocked 0 []
no matching evidence ok 1 fixture call []

Failure cases include a validator that calls the tool before checking sources, a parser that loses the source URL, a retry that repeats a write request, or a test that checks only status while ignoring sideEffects. Roll back by disabling the capability and returning invalid_request for every input until the contract and fixtures agree. Do not “fix” a failing safety test by weakening the expected denial.

Local practice

Contract review

Every step remains visible without JavaScript. When enabled, this browser stores checks on this device only.

0 of 5 checked

Checkpoint

A source returns HTTP 403. Which result is truthful?

A. ok with an empty list
B. source_unavailable plus the source URL and retry guidance
C. Retry against an arbitrary mirror
D. Ask the model to infer the missing content

Answer: B. The learner needs a distinguishable, reproducible failure. A status code does not license a substitute source or invented evidence.

Learner artifact and evidence receipt

Submit the request and result schemas, the five fixture cases, and a contract-flow diagram. Annotate which gate produces each failure and prove that the blocked-side-effect path terminates before invocation. Include the pinned OpenClaw revision 2d2ddc43d0dcf71f31283d780f9fe9ff4cc04fe4 and links to the MCP source and skills source. A reviewer should be able to run the fixtures without secrets, accounts, or live writes. Re-check the pinned references when the implementation surface changes; they bound platform concepts, while the worksheet remains the evidence for this decision.

Source provenanceVerification and sources

Review receipt rr_skills_contracts

Outcome
approved
Method
source-review
Reviewer
academy-editorial
Reviewed

Evidence

Limitations

  • Approval covers the bounded instructional claims, pinned primary sources, and disposable fixture evidence; no personal profile, production account, or external write was used.

Open the public evidence snapshot

Lesson checkpoint

Ready to move on?

Mark this lesson complete when you can apply its outcome without relying on the examples above.