๐Ÿšš WIA Delivery Robot Standard Ebook | Chapter 7 of 8


Chapter 7: Testing, Conformance and Certification โ€” Surface, Verified, Anchored

The previous four chapters described what a WIA ROB-010 implementation has to do: emit Phase 1 payloads, serve the Phase 2 REST surface, behave according to the Phase 3 navigation contract and plug into a Phase 4 fleet. This chapter is about proving it. A buyer, a building operator or a city permitting office does not want to read your source code; they want a verifiable claim of the form "this build of this robot stack conforms to WIA ROB-010 v1.0.0 at tier X, and here is the evidence." The chapter explains what the standard's three conformance tiers mean in practice, how to build test vectors from the schemas and error cases you already know, how to assemble and sign an evidence package, what the reference CLI does and does not check, how to measure the Phase 3 behavioural targets honestly, and where WIA conformance ends and product safety certification begins.

One caution runs through the whole chapter. The conformance machinery of v1.0.0 is described in three places โ€” the README (tiers), the non-normative Annexes Eโ€“G repeated at the end of each PHASE document, and the cli/delivery-robot.sh script โ€” and none of them defines tier requirements in detail. Where this chapter goes beyond those texts, it says so and labels the material as the author's recommendation.

7.1 What Conformance Means Here

7.1.1 The three tiers

The README defines conformance in one line each: Surface covers data formats, Verified adds an annual third-party audit, and Anchored adds a continuous evidence package. The tiers are cumulative in spirit: an Anchored implementation that emits non-conformant JSON is not meaningfully Anchored. Read together with Annex G, a practical interpretation is as follows.

7.1.2 What the tiers require in practice

Table 7-1. Conformance tier comparison (column 2 quotes the README; columns 3โ€“5 are the author's recommended interpretation)
TierREADME definitionMinimum evidenceWho checksCadence
SurfaceData formatsVector matrix for Phase 1 (and Phase 2 error cases), OpenAPI document, declared profileVendor; any customer can re-checkEvery release
VerifiedAnnual third-party auditSurface evidence plus Phase 3 behavioural test records, Phase 4 integration logs, signed audit reportIndependent auditorAt least annually, and on each major version
AnchoredContinuous evidence packageVerified evidence plus SBOM, Sigstore-signed manifest with transparency-log entry, quarterly re-publicationAutomated verification by consumersQuarterly and on every release

Two things the tiers do not say should be stated plainly. First, the public documents do not specify who is qualified to act as the Verified-tier auditor, nor a WIA accreditation scheme for auditors. Until one exists, choose an auditor whose independence and competence can be demonstrated โ€” for instance a body accredited to ISO/IEC 17065 (product certification) or a testing laboratory accredited to ISO/IEC 17025 โ€” and name them in the evidence package. Second, no tier is a safety certification. A robot can conform at the Anchored tier and still be unsafe on a crowded pavement; section 7.7 returns to this.

The annexes also refer to a tier-scoring annex and to another annex that do not exist in any of the four PHASE files. Do not cite them in your own documents; cite the README for tier names and Annex G for evidence shape.

7.2 Deployment Profiles

The same software can be deployed in very different ways, and an auditor cannot judge isolation, availability or evidence freshness without knowing which. The CLI's profiles command lists four named profiles:

Table 7-2. Deployment profiles and what each implies for testing
ProfileCLI descriptionTypical delivery-robot caseWhat testing must additionally show
single-tenantOne runtime per organizationA campus operator running its own fleet managerNothing beyond the base vectors; simplest to audit
multi-tenantShared runtime, header-based isolationA fleet platform serving several merchants or building ownersNegative vectors proving tenant A's token cannot read tenant B's robots, packages or telemetry topics
federatedPeer-to-peer mesh of runtimesTwo operators sharing a building's elevators and doorwaysCross-runtime identity, clock agreement, handling of a peer that is offline
air-gappedBatch evidence package, no live connectivityA hospital or factory site with no outbound internetEvidence exported in batches; signatures verifiable offline (see 7.4)

Annex E asks implementations to declare their operational scope in the OpenAPI document, but lists only three scopes (single-tenant, multi-tenant, federated), while the CLI lists four. Declare whichever applies, including air-gapped, and record the discrepancy in your conformance notes. OpenAPI 3.1 has no dedicated field for this, so a specification extension on the info object is the least surprising place:

openapi: 3.1.0
info:
  title: Example Fleet Platform โ€” WIA ROB-010 Phase 2
  version: 2.4.0
  x-wia-conformance:
    standard: WIA ROB-010
    standardVersion: 1.0.0
    tier: surface
    profile: { named: multi-tenant, version: 1 }
    tenantHeader: X-Tenant-Id

The x-wia-conformance key is an author proposal, not text from the standard; the x- prefix is the mechanism OpenAPI provides for extensions. For a multi-tenant profile, naming the isolation header explicitly lets an auditor write the cross-tenant negative vectors without guessing.

7.3 Test Vectors

7.3.1 What Annex G asks for

Annex G sets out the rule simply: every normative requirement in a PHASE has at least one positive vector and one negative vector, stored under tests/phase-vectors/<phase>/ with one directory per PHASE (phase-1-data-format, phase-2-api-interface, phase-3-protocol, phase-4-integration). Implementations claiming conformance must run all vectors in CI and publish the pass/fail matrix. Annex G describes these vectors, but at the time of writing you should not assume a complete official vector set is available in the public repository; check the repository yourself, and be prepared to write your own vectors and publish them alongside your matrix.

A positive vector demonstrates that a correct input is accepted or a correct behaviour occurs. A negative vector demonstrates that an incorrect input is rejected, or that a fault triggers the required response. Negative vectors are the ones that catch real defects: a validator configured so loosely that it accepts everything will pass every positive vector.

7.3.2 A vector file format

Annex G does not define the file format of a vector. The following shape, a proposal of the author, keeps each vector self-describing so the matrix can be traced back to the requirement it tests:

{
  "vector_id": "P1-RS-BATTERY-NEG-01",
  "phase": "phase-1-data-format",
  "requirement": "RobotState.battery_level is a number in [0, 100]",
  "kind": "negative",
  "schema": "https://wiastandards.com/schemas/delivery-robot/v1/robot-state.json",
  "input": {
    "robot_id": "550e8400-e29b-41d4-a716-446655440000",
    "timestamp": "2026-09-01T09:15:00Z",
    "version": "1.0.0",
    "position": { "latitude": 37.5665, "longitude": 126.9780, "source": "fused" },
    "battery_level": 104
  },
  "expect": { "valid": false, "error_path": "/battery_level" }
}

The error_path matters. A negative vector that only asserts "invalid" will pass even if the payload is rejected for an unrelated reason โ€” for example because the validator cannot resolve a $ref. Pinning the expected failure location makes the vector test the rule it claims to test.

7.3.3 Deriving Phase 1 vectors from the schemas

Chapter 3 gives you almost every rule you need. Walk each schema keyword and write one pair per rule:

Two technical traps catch most first attempts. First, in JSON Schema 2020-12, format is by default an annotation, not an assertion; many validators will accept "robot_id": "hello" unless you switch format checking on, and some format checkers silently skip date-time when an optional dependency is missing. Your format negative vectors will expose this immediately โ€” which is why they exist. Second, the published RobotState schema references CargoStatus, ActiveRoute and Error, which are not defined anywhere in Phase 1 (Chapter 3.7). The schema will not compile until you supply those definitions. Because the references are local (#/$defs/CargoStatus), they must resolve inside your local copy of robot-state.json: assemble that copy with all the $defs the spec prints separately (Position, SensorReadings), and for the missing ones add entries that point to your own clearly named definitions file, for example "CargoStatus": { "$ref": "https://schemas.example.com/wia-dr-local-defs.json#/$defs/CargoStatus" }. Include both files in the evidence package so the auditor sees exactly what you filled in.

7.3.4 Phase 2 vectors from the error cases

Phase 2 vectors are HTTP exchanges rather than documents: a request, the expected status code and, for failures, the expected error envelope code. The requirements of Chapter 4 translate directly:

Table 7-3. Example Phase 2 vector pairs
RequirementPositive vectorNegative vector
Bearer token requiredValid token โ†’ 200No Authorization header โ†’ 401 with error envelope
Scope enforcementrobot:control token starts navigation โ†’ 202 with command_idrobot:read-only token calls commands/start โ†’ 403
Unknown resourceExisting robot โ†’ 200Unknown robot_id โ†’ 404, code = ROBOT_NOT_FOUND
Input validationValid package โ†’ 201Package with weight_kg = โˆ’1 โ†’ 400
Rate limitingHeaders X-RateLimit-* present on 200Limit exceeded โ†’ 429, code = RATE_LIMIT_EXCEEDED, retry_after present
Error envelope shapeโ€”Every 4xx/5xx body has code, message, timestamp, request_id

The last row deserves a vector of its own because error paths are where implementations drift: a framework's default 404 page or a reverse proxy's 502 body is rarely in the WIA envelope. For multi-tenant deployments, add the cross-tenant negatives from Table 7-2.

7.3.5 Phase 3 and Phase 4 vectors are scenarios

Behavioural requirements cannot be checked by validating a document. A Phase 3 vector is a scenario: a recorded or synthesized sensor stream fed to the autonomy stack, and an assertion over the resulting commands and telemetry. For example, "LiDAR stream stops for 5 s โ†’ commanded speed limit drops by 50 %", or "GPS fix absent for 30 s โ†’ position.source leaves gps". The negative counterpart checks that the response does not fire early โ€” LiDAR silent for 1 s must not halve the speed โ€” because a robot that degrades at every hiccup is also out of specification. Phase 4 vectors look similar at the message level: a duplicated QoS 1 message on fleet/robots/{robot_id}/state must be de-duplicated by the consumer (Chapter 6), and a robot at 18 % battery must not be offered a task that the assignment rule excludes.

7.3.6 A minimal runner

The following Python runner handles document vectors (Phase 1 and the envelope checks of Phase 2). It uses the jsonschema library with its referencing registry so that $refs resolve to local copies of the schemas, and it turns format assertion on.

# tools/run_vectors.py โ€” install: pip install "jsonschema[format-nongpl]"
import json, pathlib, sys
from jsonschema import Draft202012Validator
from referencing import Registry, Resource

SCHEMAS = pathlib.Path("schemas")          # local copies, incl. your own $defs
VECTORS = pathlib.Path("tests/phase-vectors")

def load_registry():
    resources = []
    for p in SCHEMAS.glob("*.json"):
        doc = json.loads(p.read_text())
        resources.append((doc["$id"], Resource.from_contents(doc)))
    return Registry().with_resources(resources)

def run():
    registry, rows = load_registry(), []
    for vf in sorted(VECTORS.rglob("*.json")):
        v = json.loads(vf.read_text())
        if v.get("skip"):
            rows.append({"id": v["vector_id"], "result": "skipped",
                         "justification": v.get("justification")})
            continue
        schema = registry.contents(v["schema"])
        validator = Draft202012Validator(
            schema, registry=registry,
            format_checker=Draft202012Validator.FORMAT_CHECKER)
        errors = list(validator.iter_errors(v["input"]))
        ok = (not errors) == v["expect"]["valid"]
        if ok and errors and "error_path" in v["expect"]:
            ok = any(e.json_path.replace("$", "", 1).replace(".", "/")
                     == v["expect"]["error_path"] for e in errors)
        rows.append({"id": v["vector_id"], "result": "pass" if ok else "fail"})
    json.dump(rows, sys.stdout, indent=2)
    bad = [r for r in rows if r["result"] == "fail"
           or (r["result"] == "skipped" and not r.get("justification"))]
    sys.exit(1 if bad else 0)

if __name__ == "__main__":
    run()

The path comparison is deliberately simple and works for flat field names such as /battery_level; extend it for array indices if your vectors need them. Note the exit rule: Annex G states that a test skipped without recorded justification is treated as a failure, and the runner enforces exactly that. A skip with a justification is still reported in the matrix so the auditor can judge whether the reason is acceptable.

7.4 The Evidence Package

7.4.1 Contents

Annex G describes the compliance package as a tarball containing four things: a software bill of materials (CycloneDX 1.5 or SPDX 2.3), the OpenAPI document, the test-vector matrix and a signed manifest. In practice it is worth adding the schemas you actually validated against (including your filled-in $defs) and, for Verified and Anchored tiers, the behavioural test records. A layout that keeps these easy to find:

evidence-2026Q3/
โ”œโ”€โ”€ manifest.json               # identity of the build and the claim
โ”œโ”€โ”€ manifest.sigstore.json      # Sigstore bundle: signature, certificate, Rekor entry
โ”œโ”€โ”€ sbom.cdx.json               # CycloneDX 1.5 (or sbom.spdx.json, SPDX 2.3)
โ”œโ”€โ”€ openapi.yaml                # the Phase 2 surface you actually serve
โ”œโ”€โ”€ schemas/                    # Phase 1 schemas + your own $defs
โ”œโ”€โ”€ vector-matrix.json          # output of the runner, incl. skips
โ”œโ”€โ”€ behaviour/                  # Phase 3 scenario results and field logs
โ””โ”€โ”€ attestations/               # in-toto statements (build, test)

7.4.2 The SBOM

The SBOM answers "what is inside this build?" For a delivery robot, that includes the on-robot image (ROS 2 packages, drivers, perception models) and the cloud services, which are usually separate builds with separate SBOMs. Generate the SBOM from the artefact you ship, not from a developer's workstation, so that it matches the build digest in the manifest. With Syft, for example, syft <image> -o cyclonedx-json=sbom.cdx.json produces a CycloneDX JSON document; confirm that the specVersion field reads the version you claim (1.5 in the annex).

7.4.3 Signing: Sigstore, DSSE and Rekor

Annex G specifies Sigstore signatures using a DSSE envelope and a Rekor transparency-log entry, so that consumers can verify provenance "without trusting a private CA". The three pieces play distinct roles. DSSE (Dead Simple Signing Envelope) wraps the signed payload together with its type, so a signature over a manifest cannot be replayed as a signature over something else. The Sigstore keyless flow binds the signature to a short-lived certificate issued to a workload identity โ€” for instance a CI workflow โ€” instead of a long-lived private key. Rekor records the signing event in an append-only public log, which gives the "continuous" in Anchored its meaning: anyone can see that the Q3 package was signed, and when.

With the cosign tool, one workable pattern is to attach the manifest to the evidence tarball as an in-toto attestation (cosign v2-series commands shown; check the flags against the version you install):

# in CI, with an OIDC identity available to cosign
tar -czf evidence-2026Q3.tar.gz evidence-2026Q3/
cosign attest-blob --yes \
  --predicate evidence-2026Q3/manifest.json \
  --type https://wiastandards.com/attestations/delivery-robot/conformance/v1 \
  --bundle evidence-2026Q3.sigstore.json \
  evidence-2026Q3.tar.gz

# consumer side
cosign verify-blob-attestation \
  --bundle evidence-2026Q3.sigstore.json \
  --type https://wiastandards.com/attestations/delivery-robot/conformance/v1 \
  --certificate-identity-regexp 'https://github.com/example-org/.*' \
  --certificate-oidc-issuer https://token.actions.githubusercontent.com \
  evidence-2026Q3.tar.gz

The predicate-type URI above is an author choice; the standard does not define one. What matters is that it is stable and published, so verifiers can require it. For the air-gapped profile, keep the bundle next to the tarball: a Sigstore bundle contains the material needed for offline verification, provided the verifier has a copy of the Sigstore trust root distributed out of band.

7.4.4 Attestations and quarterly re-publication

The CLI lists the in-toto Attestation Framework among its reference frameworks. Beyond the conformance attestation, two further statements are useful: a build provenance statement tying the image digest to the source commit, and a test statement tying the vector matrix to the same digest. Together they let a consumer check that the tested build and the shipped build are one and the same, which is the question auditors most often have to answer by hand.

Annex G asks for the package to be re-published every quarter even if the source did not change, so that consumers can notice environmental drift โ€” a new compiler, an updated base image, a patched dependency. Rebuild, regenerate the SBOM, rerun the vectors, and sign again. If the new SBOM differs from the previous one while the source commit is unchanged, that difference is exactly the drift the quarterly rule is designed to reveal; record it in the manifest notes rather than hide it.

Retention of evidence packages is a separate question from telemetry retention. Phase 4 gives "auto-delete old telemetry (e.g., 90 days)" as a privacy measure, while evidence packages and audit records are kept for as long as your auditor, certification body or regulator requires. Annex E states a seven-year figure and attributes it to ISO/IEC 17065 audit expectations, but gives no source for that link; derive your retention period from the actual requirements that apply to you, and keep the two clocks apart so that deleting telemetry never deletes evidence.

7.5 The Reference CLI

7.5.1 Commands

The repository ships a Bash script, cli/delivery-robot.sh (version 1.0.0), with four commands. A typical session looks like this:

$ ./cli/delivery-robot.sh info
WIA delivery-robot
Version: 1.0.0

Conformance tiers (see README.md): Surface / Verified / Anchored.
Conformance package follows CycloneDX 1.5 + Sigstore (DSSE + Rekor).

$ ./cli/delivery-robot.sh profiles
Supported conformance profiles for delivery-robot:
  - single-tenant      one runtime per organization
  - multi-tenant       shared runtime, header-based isolation
  - federated          peer-to-peer mesh of runtimes
  - air-gapped         batch evidence package, no live connectivity
...

$ ./cli/delivery-robot.sh manifest-template > manifest.json
$ ./cli/delivery-robot.sh validate manifest.json
OK: manifest.json declares standardSlug=delivery-robot

$ ./cli/delivery-robot.sh validate robot-state.json
standardSlug mismatch: got '', expected 'delivery-robot'

The last two lines are the most instructive in the chapter. A perfectly valid RobotState payload fails validate, and a manifest full of placeholder values passes it. That is because validate checks one thing only: that the file's standardSlug equals delivery-robot. It is a manifest sanity check, not a payload validator, and it proves nothing about Phase 1โ€“4 conformance.

7.5.2 The manifest

The template emitted by manifest-template has six top-level fields. Filled in for a hypothetical release, it looks like this (all values are examples):

{
  "documentId": "wia-delivery-robot-0007",
  "standardSlug": "delivery-robot",
  "version": "1.0.0",
  "implementation": {
    "name": "example-sidewalk-stack",
    "build": "sha256:3f5c0dโ€ฆ(image digest)",
    "sbom": "https://evidence.example.com/2026Q3/sbom.cdx.json"
  },
  "profile": { "named": "multi-tenant", "version": 1 },
  "issuedAt": "2026-09-30T00:00:00Z"
}
Table 7-4. Manifest fields
FieldMeaningPractical note
documentIdIdentifier of this manifestTemplate value wia-delivery-robot-XXXX must be replaced; keep IDs unique per publication
standardSlugWhich WIA standardAlways delivery-robot; the only field the CLI checks
versionStandard version claimedThis is the standard's version, not your product's
implementation.nameProduct or service nameTemplate value REPLACE_ME
implementation.buildGit SHA or image digestPrefer the image digest; it is what the SBOM and signature cover
implementation.sbomWhere the SBOM livesShould resolve to the file inside this package or a durable copy
profileNamed deployment profile and its versionOne of the four profiles in 7.2
issuedAtIssue timeThe template hard-codes a fixed date; always overwrite it

Notice what the manifest lacks: a tier, a list of PHASEs covered, and a pointer to the vector matrix. Until the template grows those fields, add them under a clearly namespaced extension object (for example "x-wia": { "tier": "anchored", "phases": [1,2,3,4], "vectorMatrix": "vector-matrix.json" }) rather than inventing new top-level keys that a future template might define differently.

7.5.3 Limitations and how to extend the CLI

Besides the narrow scope of validate, two implementation details are worth fixing in your fork. The Python fallback (used when jq is absent) interpolates the file name into Python source code, so a path containing a quote character breaks it; pass the path as an argument instead. And the script never verifies signatures, so it cannot tell a signed package from an unsigned one. A minimal extension adds a validate-payload command that delegates to the vector runner's validator:

validate_payload() {
    local schema_id="$1" file="$2"
    python3 - "$schema_id" "$file" <<'PY'
import json, sys
sys.path.insert(0, "tools")
from run_vectors import load_registry
from jsonschema import Draft202012Validator
schema_id, path = sys.argv[1], sys.argv[2]
reg = load_registry()
v = Draft202012Validator(reg.contents(schema_id), registry=reg,
                         format_checker=Draft202012Validator.FORMAT_CHECKER)
errs = sorted(v.iter_errors(json.load(open(path))), key=lambda e: e.json_path)
for e in errs:
    print(f"{e.json_path}: {e.message}")
sys.exit(1 if errs else 0)
PY
}
# add to the case statement:
#   validate-payload) validate_payload "$2" "$3" ;;

With that in place, validate-payload https://wiastandards.com/schemas/delivery-robot/v1/robot-state.json robot-state.json performs a genuine Surface-tier check of one document, and the same code path runs in CI.

7.6 Behavioural and Field Testing

7.6.1 Measuring the Phase 3 targets

Phase 3 ยง8 lists performance targets, and ยง6 adds the emergency-stop and failure-mode timings. They are simple to state and easy to measure badly. The core difficulty is that each target is a statement about a time interval or a rate, and the spec does not define exactly where the interval starts or what the denominator is. Your test plan must fix those definitions and write them into the evidence package.

Table 7-5. Phase 3 spec targets and how to measure them
Spec targetStart/end or denominator to declareInstrumentation
Position error < 0.5 m, 95 % of the timeHorizontal error against ground truth, per sample, over declared routesReference trajectory from a surveyed RTK receiver or surveyed markers; report the 95th percentile, not the mean
Obstacle detection rate > 99.9 %Detected obstacles รท obstacles presented, per scripted encounterScripted encounters with labelled targets; see the sample-size note below
False positive rate < 5 %Declare it: per detection, per encounter or per kilometreLabelled logs; review every stop that had no real obstacle
Reaction time < 100 ms, detection to evasive actionFrom sensor capture timestamp (worst case) or perception output (best case) โ€” report bothCommon clock across sensors and motor controller (NTPv4 or PTP per Annex E)
Emergency-stop motor cutoff < 100 msFrom trigger (button edge, command receipt on the robot, perception event) to measured drop in motor currentExternal measurement, e.g. a logic analyser or current probe, not software logs
Route deviation < 10 %Actual path length รท reference optimal length โˆ’ 1Declare the reference planner and map used to compute "optimal"
Failure-mode timers (GPS 30 s, LiDAR 5 s, comms 60 s, stuck 30 s, battery < 15 %)From fault injection to observable response in telemetryFault injection in simulation and on a closed course; negative vectors for early triggering

Why external instrumentation for the emergency stop? A software log proves that the stop command was issued; it does not prove that current stopped flowing to the motors. The whole point of a safety-relevant timing is the physical effect, and a relay or motor driver that lags by tens of milliseconds is invisible in application logs.

7.6.2 Why 100 ms and 0.3 m interact

Simple kinematics shows why these two numbers must be tested together. Take, as an example only, a robot travelling at 1.5 m/s. In 100 ms it covers 0.15 m before the motors are cut. If it then decelerates at an assumed 2 m/sยฒ, the braking distance is vยฒ/(2a) = 1.5ยฒ / 4 โ‰ˆ 0.56 m. The total stopping distance is about 0.71 m โ€” more than twice the 0.3 m obstacle trigger in ยง6.1. The conclusion is not that the spec is wrong, but that the 0.3 m trigger is a last-resort threshold: the local planner and the speed limits of Chapter 5 must keep the robot slow enough, and far enough away, that the emergency stop is rarely the only line of defence. A good Phase 3 scenario vector therefore measures stopping distance at the robot's maximum permitted speed, not just the cutoff latency.

7.6.3 How many trials is enough?

Rate targets need sample sizes that match them. A standard statistical rule of thumb, the "rule of three", states that if an event is observed zero times in n independent trials, the 95 % upper confidence bound on its probability is approximately 3/n. To support "miss rate below 0.1 %" (detection above 99.9 %) with zero misses, you therefore need roughly 3,000 independent encounters; with even one miss you need more. Twenty passes on a test track tell you almost nothing about a 99.9 % target. State the trial count and the confidence bound in the evidence package, and keep the encounters varied (lighting, weather, obstacle classes, approach angles) so that they are genuinely independent.

7.6.4 From simulation to closed course to pilot

A sound progression has three stages. Simulation runs the Phase 3 scenario vectors at scale, including fault injection that would be unsafe or impractical to reproduce physically; it is the only practical way to reach thousands of encounters early. Closed-course testing repeats a representative subset with physical sensors, real braking and external timing instruments, and calibrates how far simulation results can be trusted. Pilot operation in the intended environment, under whatever local authorization applies, then produces the field logs that support the operator KPIs below.

The simulator published with the standard has a "Safety & Testing Suite" panel with scenario and sensor-suite selectors, a "Run Safety Test" button and a compliance checklist. It is a demonstration of what a test report might look like: the result it prints is a fixed object with the same timings and a "passed": true field on every run, and the checklist items (for example a GPS accuracy figure and an ingress-protection rating) are not requirements of the PHASE documents. Use it to explain the idea to stakeholders; do not put its output in an evidence package.

7.6.5 Operator KPIs from Phase 4

Phase 4 ยง6.1 lists fleet KPIs with targets. They describe how well a service runs, not whether an implementation conforms, and they are best treated as operator targets agreed with customers.

Table 7-6. Phase 4 KPI targets (spec values) with measurement notes
KPISpec targetDefinition in the specMeasurement note
Delivery success rate> 99 %Delivered รท total deliveriesDeclare how cancellations by the customer and returns are counted
Average delivery time< 15 minMean from assignment to deliveryReport the distribution too; a mean hides long tails
Fleet utilization> 80 %Time in active delivery รท total timeDeclare whether charging and maintenance time is in the denominator
Energy efficiency> 5 km/kWhDistance รท energy consumedMeasure energy at the charger or from the battery management system consistently
Customer satisfaction> 4.5 / 5Survey ratingsNot a technical measurement; keep it out of conformance claims
Incidents per 1,000 deliveries< 1Safety events รท total deliveriesDefine "safety event" before the pilot starts, including near misses

7.7 Relationship to External Certification

7.7.1 Separate tracks, separate questions

WIA ROB-010 is a data and protocol standard. Conformance, even at the Anchored tier, answers the question "does this implementation speak the common language correctly and prove it continuously?" It does not answer "is this robot safe to operate among people?" or "is this robot permitted on this pavement?" Those are separate tracks with their own bodies and procedures.

Table 7-7. Conformance and external tracks
TrackQuestion answeredTypical assessorOutput
WIA ROB-010 conformanceCorrect data, API, behaviour contract and fleet integrationVendor (Surface), independent auditor (Verified), public verification (Anchored)Vector matrix, signed evidence package, audit report
Product safetyIs the robot acceptably safe? For example, assessment against ISO 13482, where the certification body judges that standard applicableCertification body accredited to ISO/IEC 17065Product certificate
Operating permissionMay it operate here? For example, Korea's operating-safety certification for outdoor mobile robots, or a city's sidewalk permitNational or local authority and its designated bodiesCertificate, permit or licence with conditions
Information securityIs the organization's security management adequate?ISO/IEC 27001 certification bodyManagement-system certificate
PrivacyIs camera and location data processed lawfully?Internal or external assessment under PIPA, GDPR or other applicable lawImpact assessment, records of processing

The tracks reinforce each other. A safety assessor examining an emergency-stop function will be glad to see your Table 7-5 measurements with external instrumentation; an authority reviewing a pilot application can use the Phase 4 incident records. But the evidence flows one way: a WIA badge must never be presented as, or next to, a safety claim in a way that suggests one implies the other.

7.7.2 Monthly error-budget report

Annex E recommends a monthly error-budget summary โ€” p95 latency, error rate and violation hours โ€” "in the format defined by the WIA reporting profile" so that operators can be compared without exposing tenant data. That reporting profile is not published with v1.0.0. Until it is, a small, explicit document is better than none. The shape below is an author proposal and the values are placeholders:

{
  "standardSlug": "delivery-robot",
  "period": "2026-08",
  "profile": "multi-tenant",
  "slo": { "latency_p95_ms": 300, "error_rate": 0.001 },
  "observed": {
    "latency_p95_ms": 0,
    "error_rate": 0.0,
    "violation_hours": 0
  },
  "scope": ["/api/v1/robots", "/api/v1/packages", "/api/v1/routes", "/api/v1/fleet"],
  "method": "server-side histograms, 1-minute buckets, 5xx and timeouts counted as errors"
}

Define "violation hour" before the first report (for example, any clock hour in which an SLO was breached) and keep the definition stable; a changing definition makes the month-to-month comparison meaningless. Report aggregates across tenants, never per-tenant figures, to stay within the privacy intent of the annex.

Summary: A Conformance Checklist

The list below collects the chapter into one pass that a team can walk through before claiming a tier.

  1. Decide the tier you will claim and the deployment profile; declare both in the OpenAPI document and the manifest.
  2. Fill in the undefined Phase 1 definitions (CargoStatus, ActiveRoute, Error, Contact, Location) in a separate schema file and publish it.
  3. Write at least one positive and one negative vector per normative requirement you implement, with expected error locations.
  4. Run the validator with format assertion switched on; confirm that the format negatives fail.
  5. Add Phase 2 exchange vectors, including error-envelope shape, rate limiting and โ€” for multi-tenant โ€” cross-tenant isolation.
  6. Add Phase 3 and Phase 4 scenario vectors, including "must not trigger early" negatives.
  7. Make CI fail on any failed vector and on any skip without recorded justification.
  8. Generate the SBOM from the shipped artefact and put its digest in the manifest.
  9. Replace every placeholder in the CLI manifest template, including issuedAt.
  10. Sign with Sigstore, log in Rekor and publish the bundle alongside the tarball.
  11. For Verified: measure Phase 3 timings with external instruments and trial counts that support the target rates; engage an independent auditor.
  12. For Anchored: schedule quarterly rebuild, re-test and re-signing, and publish the monthly error-budget report.
  13. Keep product safety certification and operating permission as separate, explicitly named tracks.

A GitHub Actions workflow that runs the vectors on every change and once per quarter might look like this:

name: wia-rob010-conformance
on:
  push:
  pull_request:
  schedule:
    - cron: "0 3 1 1,4,7,10 *"     # quarterly recheck (Annex G)
jobs:
  vectors:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with: { python-version: "3.12" }
      - run: pip install "jsonschema[format-nongpl]"
      - run: python tools/run_vectors.py > vector-matrix.json
      - uses: actions/upload-artifact@v4
        if: always()
        with: { name: vector-matrix, path: vector-matrix.json }
  sign:
    needs: vectors
    if: github.event_name == 'schedule' || startsWith(github.ref, 'refs/tags/')
    runs-on: ubuntu-latest
    permissions: { id-token: write, contents: read }
    steps:
      - uses: actions/checkout@v4
      - uses: sigstore/cosign-installer@v3
      - run: ./tools/build_evidence.sh      # SBOM, tarball, cosign attest-blob (7.4)

The if: always() on the upload step ensures that a failing matrix is still published: Annex G asks vendors to report negative results with the same fidelity as positive ones, and a matrix that disappears whenever it is red would defeat that rule. build_evidence.sh stands for your own script combining the steps of section 7.4. The next chapter looks at what the standard does not yet cover โ€” mission manifests, hand-off events and incident reporting โ€” and how to extend it without breaking the conformance story built here.


โ† Chapter 6: Phase 4 Fleet Integration โ€” Telemetry, Dispatch, Charging and OTA | Back to Index | Chapter 8: Extensions and the Road Ahead โ€” Mission Manifests, Hand-offs, Incident Reporting and the Roadmap โ†’