tap-v1 — Web Annotation extension vocabulary

Stable IRI: https://taprun.dev/ns/tap-v1 JSON-LD context: index.jsonld Version: tap-v1 Issued: 2026-04-15 Modified: 2026-04-23 License: CC0 1.0 Universal

What this is

tap-v1 is the namespace for Tap-specific extensions to the W3C Web Annotation Data Model.

Scope rule: the namespace defines only terms that ship across a consumer boundary in a stored .tap.json envelope — internal implementation types (forge-time fingerprints, intermediate selector candidates) are TypeScript-only and intentionally excluded. Two on-disk surfaces carry tap:* terms:

  1. A compiled Tap program — stored as a .tap.json W3C Annotation with motivation tap:executing and a tap:ExecutionPlan body.
  2. A doctor assessment — a W3C Annotation with motivation assessing (W3C-standard) whose body carries tap:verdict and related diagnostic properties.

Why it exists

The W3C Web Annotation context (http://www.w3.org/ns/anno.jsonld) does not define tokens for execution plans or doctor diagnostics. Without a published tap-v1 context, every tap:* CURIE in a Tap annotation would fail strict JSON-LD validation. This document is what lets external tooling (annotation stores, validators, agent platforms) consume Tap annotations without a custom profile.

Tap itself parses tap:* fields by literal key and does not require runtime context resolution, so the runtime cost of this document is zero — it exists purely as a stable identifier and as documentation for external consumers.

What it defines

Live in stored .tap.json

Term Kind Role
tap:executing oa:Motivation Motivation of a compiled Tap program
tap:ExecutionPlan rdfs:Class Body type of a compiled Tap program
tap:site, tap:name, tap:intent properties Tap identity/direction (bare keys inside tap:ExecutionPlan)
tap:health, tap:args properties Tap contract + argument schema (bare keys inside tap:ExecutionPlan)

Live in a doctor assessing annotation

Term Kind Role
tap:verdict property healthy · broken · stale · layer-mismatch · unreachable · unverified
tap:compiledFromLayer property Trust layer 1–4 the tap was compiled from
tap:recommendedLayer property Layer doctor recommends re-forging from
tap:crossValidation property Layer-1 vs. observed-value disagreement record ({layer1Value, observedValue, disagreement})
tap:suggestions property Ordered diagnostic suggestions (free-text)
tap:suggestAuthoritative property Optional ready-to-embed AuthoritativeSpec shown only when verdict is unverified AND a known-shape source exists for the target. Lets agents close the verification gap without hand-writing the V config.

See index.jsonld for the full context document and normative definitions. CI guards (packages.yml :: ns-cross-consumer-only) keep the term set in this README equal to the term set in the JSON-LD context so the two never drift.

Example: a compiled Tap program

{
  "@context": [
    "http://www.w3.org/ns/anno.jsonld",
    "https://taprun.dev/ns/tap-v1"
  ],
  "id": "https://taprun.dev/taps/github/trending",
  "type": "Annotation",
  "motivation": "tap:executing",
  "target": "https://taprun.dev/taps/github/trending",
  "body": {
    "type": "tap:ExecutionPlan",
    "site": "github",
    "name": "trending",
    "intent": "read",
    "description": "Trending GitHub repositories",
    "health": { "min_rows": 5, "non_empty": ["repo"] },
    "args": { "limit": { "type": "int", "default": 20 } },
    "ops": [
      { "op": "nav", "url": "https://github.com/trending" },
      { "op": "wait", "selector": "article.Box-row" },
      {
        "op": "exec",
        "fn": "async (handle) => handle.eval(() => Array.from(document.querySelectorAll('article.Box-row')).map(el => ({ repo: el.querySelector('h2 a')?.textContent?.trim() })))"
      }
    ]
  },
  "generator": {
    "id": "https://taprun.dev/migrate",
    "type": "SoftwareAgent",
    "version": "1"
  },
  "created": "2026-04-23T00:00:00Z"
}

A consumer that only understands W3C Web Annotation sees a valid Annotation with a motivation, a target IRI, and a body. A Tap-aware consumer additionally reads the tap:ExecutionPlan body and can replay the program against the declared target.

Example: a doctor assessment

{
  "@context": [
    "http://www.w3.org/ns/anno.jsonld",
    "https://taprun.dev/ns/tap-v1"
  ],
  "type": "Annotation",
  "motivation": "assessing",
  "target": "tap:github/trending",
  "body": {
    "tap:verdict": "layer-mismatch",
    "tap:compiledFromLayer": 4,
    "tap:recommendedLayer": 1,
    "tap:crossValidation": {
      "layer1Value": 25,
      "observedValue": 18,
      "disagreement": "ItemList.numberOfItems differs from extracted row count"
    },
    "tap:suggestions": [
      "Re-forge from Layer 1 (JSON-LD ItemList present on this page)",
      "Current selector targets Layer 4 (CSS classes); higher-trust source available"
    ]
  },
  "generator": { "id": "https://taprun.dev/doctor", "type": "SoftwareAgent" },
  "created": "2026-04-23T00:00:00Z"
}

Naming convention

Terms follow JSON-LD / RDF convention:

Stability

License

This namespace document is released under CC0 1.0 Universal — vocabulary infrastructure should never carry usage restrictions. You can freely reference, mirror, or redistribute this document.

Serving note (for operators)

The file at ./index.jsonld should be served with Content-Type: application/ld+json when dereferenced. GitHub Pages’ default MIME for .jsonld is application/octet-stream, which most strict JSON-LD processors accept as long as they can parse the body — Tap’s own consumers never fetch this context — but external validators may warn. A future move to a host with configurable MIME types (or a Cloudflare Worker in front of Pages) can fix this without changing the URI. See ./SERVING.md if present.

References