ActorSkillListingV2 actor.skill-listing@2
Generated at from
openvibe-contracts v0.419.0 and
openvibe-sdk v0.161.0.
- Version
- 2.1.0
- Owner
- actor
- Visibility
- public
- Status
- active
- Compatibility
- backward
- Decision
- ADR-060
- Schema
https://openvibe.network/contracts/actor/skill-listing.v2.json
actor.skill-listing@2: a skill in the registry, the record every site shows on its card (ADR-058, ADR-060). It keeps every field and meaning of actor.skill-listing@1 and adds what a sandbox skill needs to be shown and run: the document is actor.skill@2, CODE lists, per code resource, its name (the resource path a tool's entry names), the Media object id its bytes are stored under and the sha256 of those bytes, so the bytes that run are the bytes that were scanned; and the SCAN's findings may name the TOOL they are about, so a person reads the scan one tool at a time. A sandbox listing carries code; a prompt listing carries none. It still carries the PUBLISHER from the registry's own record (vouched: unlike the document's author, this is who published it), the SCAN (its state and findings travel with the listing), and the CURATOR's state. Install rules every site keeps: a passed skill installs in one tap; a flagged one only after the person reads the findings and confirms; a failed one never; a stale or archived one is not offered. Installing copies the document into the person's agent; it grants nothing (the skill's requires is a declaration). VISIBILITY is public (the default, and what every listing is today) or private (only its publisher sees or installs it); because a private listing always belongs to a person, VISIBILITY private requires PUBLISHER.KIND person.
Fields
| Field | Type | Required | Description | Constraints |
|---|---|---|---|---|
listing | const | yes |
| |
id | string | yes | The listing's id. |
|
skill | actor.skill | yes | The document as published. | |
code | array of object | Per code resource of a sandbox skill: the resource path a tool entry names, the Media object the registry stored its bytes under, and the sha256 of those bytes. Required when the skill runs in a sandbox; absent for a prompt skill. |
| |
code[].name | string | yes | The resource path, as the document's tools entry names it. |
|
code[].media_id | string | yes | The Media object id the code bytes are stored under. |
|
code[].sha256 | string | yes | Lowercase hex SHA-256 of the code bytes. |
|
publisher | object | yes | Who published it, from the registry's own record. |
|
publisher.kind | enum | yes | OpenVibe's own skills, or a person's. |
|
publisher.subject | string | The person who published it (kind person). |
| |
publisher.name | string |
| ||
visibility | enum | public: anyone may see and install it, as every listing is today (the default, and what an absent field means). private: only its publisher sees or installs it, so its publisher.kind is person. |
| |
scan | object | yes | What the registry's scan found. It travels with the listing, and a finding about a tool names it. |
|
scan.state | enum | yes |
| |
scan.scanner | string | The scanner and its version. |
| |
scan.scanned_at | string |
| ||
scan.findings | array of object |
| ||
scan.findings[].rule | string | yes | Which rule, such as code.network or code.env. |
|
scan.findings[].severity | enum | yes |
| |
scan.findings[].where | string | instructions, description, or a resource path. |
| |
scan.findings[].tool | string | The tool the finding is about, when it is about one tool. |
| |
scan.findings[].note | string | yes | What it found, in plain words. |
|
curator | object | yes | The curator ships with the catalogue: what is used stays, what is not goes stale. |
|
curator.state | enum | yes |
| |
curator.last_used_at | string |
| ||
curator.near_duplicate_of | string | A listing that does the same job. |
| |
installs | integer |
| ||
listed_at | string | yes |
| |
retired_at | string |
|
Examples
From the contract's own test fixtures: valid ones validate, rejected ones must fail.
Valid: flagged
{
"listing": "actor.skill-listing@1",
"id": "skl_01JAB2C3D4E5F6G7H8J9K0MNPR",
"skill": {
"skill": "actor.skill@1",
"slug": "summarize-a-stream",
"name": "Summarize a stream",
"description": "Use when someone asks what happened in a stream recording: a short summary with the moments that mattered.",
"version": "1.0.0",
"instructions": "Read the transcript. Write five bullets, each with its time. Name nobody who did not speak.",
"runs": "prompt",
"integrity": {
"alg": "sha256",
"hash": "dadad5ff3666d12b2270015d96dd5d6f784138a2eeb9010f5a1979ce86f9ff02"
}
},
"publisher": {
"kind": "openvibe"
},
"scan": {
"state": "flagged",
"scanner": "openvibe-skill-scan 1.0.0",
"scanned_at": "2026-11-07T23:02:00.000Z",
"findings": [
{
"rule": "asks.undeclared_kind",
"severity": "warn",
"where": "instructions",
"note": "The instructions ask to post, and the skill does not declare post."
}
]
},
"curator": {
"state": "stale",
"near_duplicate_of": "skl_01JAB2C3D4E5F6G7H8J9K0MNPQ"
},
"installs": 0,
"listed_at": "2026-11-07T23:02:00.000Z"
}Valid: passed
{
"listing": "actor.skill-listing@1",
"id": "skl_01JAB2C3D4E5F6G7H8J9K0MNPQ",
"skill": {
"skill": "actor.skill@1",
"slug": "weekly-changelog-post",
"name": "Weekly changelog post",
"description": "Use when asked to write the week's changelog: what shipped, grouped by product, as a draft post.",
"version": "1.2.0",
"instructions": "1. Read the changelog entries of the last seven days.\n2. Group them by product, newest first.\n3. Write a draft with the template in template.md.\n4. Publish only after the person approves it.",
"resources": [
{
"path": "template.md",
"media_type": "text/markdown",
"text": "# This week on OpenVibe\n\n## <product>\n- <what shipped>"
}
],
"requires": {
"kinds": [
"publish"
],
"apps": [],
"hosts": [
"openvibe.blog"
]
},
"runs": "prompt",
"license": "CC-BY-4.0",
"author": {
"subject": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ",
"name": "Ann"
},
"created_at": "2026-11-07T23:00:00.000Z",
"integrity": {
"alg": "sha256",
"hash": "b3e351154b87238e67434bc973c15c7a831742a43728529b05532dd14e5d9893"
}
},
"publisher": {
"kind": "person",
"subject": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ",
"name": "Ann"
},
"scan": {
"state": "passed",
"scanner": "openvibe-skill-scan 1.0.0",
"scanned_at": "2026-11-07T23:01:00.000Z",
"findings": []
},
"curator": {
"state": "active",
"last_used_at": "2026-11-07T23:30:00.000Z"
},
"installs": 12,
"listed_at": "2026-11-07T23:01:00.000Z"
}Valid: private-person
{
"listing": "actor.skill-listing@2",
"id": "skl_01JAB2C3D4E5F6G7H8J9K0MNPW",
"skill": {
"skill": "actor.skill@2",
"slug": "summarize-a-stream",
"name": "Summarize a stream",
"description": "Use when someone asks what happened in a stream recording: a short summary with the moments that mattered.",
"version": "1.0.0",
"instructions": "Read the transcript. Write five bullets, each with its time. Name nobody who did not speak.",
"resources": [
{
"path": "outline.md",
"media_type": "text/markdown",
"text": "# Summary\n\n- <time> <moment>"
}
],
"requires": {
"kinds": [
"post"
]
},
"runs": "prompt",
"license": "MIT",
"integrity": {
"alg": "sha256",
"hash": "4a3e9e27aff7547cdbd9ad085faa6fa9ea4c523e87df3cdcc491d75e35b590d2"
}
},
"publisher": {
"kind": "person",
"subject": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ",
"name": "Ann"
},
"visibility": "private",
"scan": {
"state": "passed",
"scanner": "openvibe-skill-scan 1.1.0",
"scanned_at": "2026-11-08T03:01:00.000Z",
"findings": []
},
"curator": {
"state": "active"
},
"listed_at": "2026-11-08T03:01:00.000Z"
}Valid: sandbox-public
{
"listing": "actor.skill-listing@2",
"id": "skl_01JAB2C3D4E5F6G7H8J9K0MNPQ",
"skill": {
"skill": "actor.skill@2",
"slug": "fetch-a-page-title",
"name": "Fetch a page title",
"description": "Use when someone gives a web address and asks what the page is called: reads the title over the internet.",
"version": "2.1.0",
"instructions": "Call fetch_title with the https URL. It runs in a sandbox with the internet, and only for this call.",
"resources": [
{
"path": "fetch_title.js",
"media_type": "text/javascript",
"text": "'use strict';\nlet raw = '';\nprocess.stdin.setEncoding('utf8');\nprocess.stdin.on('data', (chunk) => { raw += chunk; });\nprocess.stdin.on('end', async () => {\n const input = JSON.parse(raw);\n const response = await fetch(input.url, { signal: AbortSignal.timeout(8000) });\n const html = await response.text();\n const title = (html.match(/<title[^>]*>([^<]*)<\\/title>/i) || [])[1] || '';\n process.stdout.write(JSON.stringify({ status: response.status, title: title.trim() }));\n});\n"
}
],
"tools": [
{
"name": "fetch_title",
"description": "Read a page over https and answer its status and title.",
"language": "javascript",
"entry": "fetch_title.js",
"args": {
"type": "object",
"properties": {
"url": {
"type": "string",
"pattern": "^https://",
"maxLength": 2048
}
},
"required": [
"url"
],
"additionalProperties": false
},
"net": "public",
"limits": {
"wall_ms": 15000,
"mem_bytes": 134217728
}
}
],
"requires": {
"kinds": [
"skill.run",
"send"
]
},
"runs": "sandbox",
"license": "MIT",
"author": {
"subject": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ",
"name": "Ann"
},
"integrity": {
"alg": "sha256",
"hash": "53d984eb0e639c1b07c99b9a5aec6d64ea9e594cbe404a0a9256debf7cb8b49e"
}
},
"code": [
{
"name": "fetch_title.js",
"media_id": "med_01JAB2C3D4E5F6G7H8J9K0MNPQ",
"sha256": "f75ae8394b4d2e7d7667e0645071534862f321b2015017c26ca49bf1f3a32b80"
}
],
"publisher": {
"kind": "person",
"subject": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ",
"name": "Ann"
},
"scan": {
"state": "flagged",
"scanner": "openvibe-skill-scan 1.1.0",
"scanned_at": "2026-11-08T01:01:00.000Z",
"findings": [
{
"rule": "code.network",
"severity": "warn",
"where": "fetch_title.js",
"tool": "fetch_title",
"note": "The tool asks for the internet; the person must read this before install."
}
]
},
"curator": {
"state": "active",
"last_used_at": "2026-11-08T01:30:00.000Z"
},
"installs": 3,
"listed_at": "2026-11-08T01:01:00.000Z"
}Rejected: bad-state
{
"listing": "actor.skill-listing@1",
"id": "skl_01JAB2C3D4E5F6G7H8J9K0MNPT",
"skill": {
"skill": "actor.skill@1",
"slug": "summarize-a-stream",
"name": "Summarize a stream",
"description": "Use when someone asks what happened in a stream recording: a short summary with the moments that mattered.",
"version": "1.0.0",
"instructions": "Read the transcript. Write five bullets, each with its time. Name nobody who did not speak.",
"runs": "prompt",
"integrity": {
"alg": "sha256",
"hash": "dadad5ff3666d12b2270015d96dd5d6f784138a2eeb9010f5a1979ce86f9ff02"
}
},
"publisher": {
"kind": "openvibe"
},
"scan": {
"state": "trusted"
},
"curator": {
"state": "active"
},
"listed_at": "2026-11-07T23:02:00.000Z"
}Rejected: no-scan
{
"listing": "actor.skill-listing@1",
"id": "skl_01JAB2C3D4E5F6G7H8J9K0MNPS",
"skill": {
"skill": "actor.skill@1",
"slug": "summarize-a-stream",
"name": "Summarize a stream",
"description": "Use when someone asks what happened in a stream recording: a short summary with the moments that mattered.",
"version": "1.0.0",
"instructions": "Read the transcript. Write five bullets, each with its time. Name nobody who did not speak.",
"runs": "prompt",
"integrity": {
"alg": "sha256",
"hash": "dadad5ff3666d12b2270015d96dd5d6f784138a2eeb9010f5a1979ce86f9ff02"
}
},
"publisher": {
"kind": "openvibe"
},
"curator": {
"state": "active"
},
"listed_at": "2026-11-07T23:02:00.000Z"
}Rejected: private-openvibe
{
"listing": "actor.skill-listing@2",
"id": "skl_01JAB2C3D4E5F6G7H8J9K0MNPX",
"skill": {
"skill": "actor.skill@2",
"slug": "summarize-a-stream",
"name": "Summarize a stream",
"description": "Use when someone asks what happened in a stream recording: a short summary with the moments that mattered.",
"version": "1.0.0",
"instructions": "Read the transcript. Write five bullets, each with its time. Name nobody who did not speak.",
"resources": [
{
"path": "outline.md",
"media_type": "text/markdown",
"text": "# Summary\n\n- <time> <moment>"
}
],
"requires": {
"kinds": [
"post"
]
},
"runs": "prompt",
"license": "MIT",
"integrity": {
"alg": "sha256",
"hash": "4a3e9e27aff7547cdbd9ad085faa6fa9ea4c523e87df3cdcc491d75e35b590d2"
}
},
"publisher": {
"kind": "openvibe"
},
"visibility": "private",
"scan": {
"state": "passed",
"scanner": "openvibe-skill-scan 1.1.0",
"scanned_at": "2026-11-08T03:01:00.000Z",
"findings": []
},
"curator": {
"state": "active"
},
"listed_at": "2026-11-08T03:01:00.000Z"
}Rejected: sandbox-without-code
{
"listing": "actor.skill-listing@2",
"id": "skl_01JAB2C3D4E5F6G7H8J9K0MNPQ",
"skill": {
"skill": "actor.skill@2",
"slug": "slugify-a-title",
"name": "Slugify a title",
"description": "Use when a draft needs a URL slug: lowercase words joined by hyphens, no network.",
"version": "1.0.0",
"instructions": "Call slugify with the title. It runs in a sandbox with no network. Use the slug in a URL.",
"resources": [
{
"path": "slugify.js",
"media_type": "text/javascript",
"text": "'use strict';\nlet raw = '';\nprocess.stdin.setEncoding('utf8');\nprocess.stdin.on('data', (chunk) => { raw += chunk; });\nprocess.stdin.on('end', () => {\n const input = JSON.parse(raw);\n const slug = String(input.title).toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '');\n process.stdout.write(JSON.stringify({ slug }));\n});\n"
}
],
"tools": [
{
"name": "slugify",
"description": "Turn a title into a URL slug: lowercase words joined by hyphens.",
"language": "javascript",
"entry": "slugify.js",
"args": {
"type": "object",
"properties": {
"title": {
"type": "string",
"maxLength": 120,
"description": "The title to turn into a slug."
},
"shorten": {
"type": "boolean"
}
},
"required": [
"title"
],
"additionalProperties": false
},
"net": "deny"
}
],
"requires": {
"kinds": [
"skill.run"
]
},
"runs": "sandbox",
"license": "MIT",
"author": {
"subject": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ"
},
"created_at": "2026-11-08T01:00:00.000Z",
"integrity": {
"alg": "sha256",
"hash": "84f9b0bc23d10534158184aa9b1952ef078f7e21d4d4fa6f3aa0f3a1e13ca1db"
}
},
"publisher": {
"kind": "person",
"subject": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ"
},
"scan": {
"state": "passed"
},
"curator": {
"state": "active"
},
"listed_at": "2026-11-08T01:01:00.000Z"
}Rejected: unknown-visibility
{
"listing": "actor.skill-listing@2",
"id": "skl_01JAB2C3D4E5F6G7H8J9K0MNPY",
"skill": {
"skill": "actor.skill@2",
"slug": "summarize-a-stream",
"name": "Summarize a stream",
"description": "Use when someone asks what happened in a stream recording: a short summary with the moments that mattered.",
"version": "1.0.0",
"instructions": "Read the transcript. Write five bullets, each with its time. Name nobody who did not speak.",
"resources": [
{
"path": "outline.md",
"media_type": "text/markdown",
"text": "# Summary\n\n- <time> <moment>"
}
],
"requires": {
"kinds": [
"post"
]
},
"runs": "prompt",
"license": "MIT",
"integrity": {
"alg": "sha256",
"hash": "4a3e9e27aff7547cdbd9ad085faa6fa9ea4c523e87df3cdcc491d75e35b590d2"
}
},
"publisher": {
"kind": "person",
"subject": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ",
"name": "Ann"
},
"visibility": "shared",
"scan": {
"state": "passed",
"scanner": "openvibe-skill-scan 1.1.0",
"scanned_at": "2026-11-08T03:01:00.000Z",
"findings": []
},
"curator": {
"state": "active"
},
"listed_at": "2026-11-08T03:01:00.000Z"
}Validate
const contracts = require('openvibe-contracts');
contracts.validate('actor.skill-listing@2', value); // { valid, errors: [{ path, message }] }