snapshot capture your domain REQUIREDtransform move and reorient an existing objectmaterialize rebuild one of your records, and undo thatproperties conform an existing object to a record you capturedremove destroy an existing object requires materialize
There is no create, update, delete, migrate or restore. Migration and restore are
Blueprints workflows composed from the five above; they are not things an adapter
declares.An unrecognised capability name is ignored with a warning, so an adapter written for a newer
Blueprints still loads here: it loses that ability rather than its whole domain.
Declaring capability C asserts that for every record this adapter emits from Collect,
the operation named by C either
completes, such that its postcondition is observable by a later Collect; or
is refused in phase 1, with a code and a reason.
There is no third outcome.
properties = true when 70% of your records work and 30% throw at random is not a partial
capability. It is a false one, and the operations built on it (all-or-nothing group
materialization, restore with rollback) are built on the promise, not on the average.Two corollaries:
Phase 1 never writes, and never throws. A thrown error is a contract violation, not a
refusal. Blueprints contains it, names you and fails the operation, but you are at fault.
Phase 2 is per-record all-or-nothing. On failure return false, reason having left the
object as you found it.
When an operation is impossible for one particular record, that is what phase 1 is for:
return { ok = false, code = "unsupported", reason = "this turret is welded to a door" }
code is "unsupported" (this can never work here) or "invalid" (this record or target is
wrong), and reason must be a non-empty string. Both are checked: a refusal without them is
itself a contract failure. Blueprints shows the reason to the admin and plans around it.
All of them or none. If you cannot answer, error(): the capture fails, names you, and is
recoverable.
A Version silently missing a category of objects is not degraded, it is wrong. Diffed
against a healthy one it reports every one of them as removed, and a restore acts on that.
Collect may have side effects (assigning identity on first sight is one) but two consecutive
calls on an unchanged world must name the same objects.
Position and orientation only. Deliberately not update: an adapter that can move a turret
must not thereby be believed able to rewrite a job definition.See Transform.
PrepareMaterialize(self, source, transform, ctx) -> { ok = true, orientation, preview, warnings } or { ok = false, code, reason }Materialize(self, source, transform, ctx) -> { ok = true, localId, identity } or { ok = false, reason }ReleaseMaterialized(self, localId) -> ok, reason
remove requires it. An adapter that can destroy but cannot rebuild turns every removal into
a one-way door, and the durable rollback path runs through materialize. Registration refuses
the combination.See Materialize.
PrepareProperties(self, localId, source, ctx) -> { ok = true, orientation, warnings } or { ok = false, code, reason }ApplyProperties(self, localId, source, ctx) -> ok, reason
Two functions, not three: the inverse of ApplyPropertiesisApplyProperties, called
with the earlier record.The bound is strict: the only values you may write are ones you can also read back.See Properties.
Both PrepareMaterialize and PrepareProperties may declare how much of a recorded
orientation your adapter will actually honour:
orientation = "full" -- pitch, yaw and roll are all reproduced (the DEFAULT)orientation = "yaw" -- only yaw; pitch and roll are droppedorientation = "none" -- position only; there is no facing at all
Omitting it means "full". An unrecognised value is a hard refusal of that operation, not
a downgrade.
Which function is asked depends on what you declare. Post-restore verification asks
PrepareProperties for any adapter that declares properties, and PrepareMaterialize
otherwise.So if you declare properties, return your orientation from both. An adapter that says
"yaw" in PrepareMaterialize and nothing in PrepareProperties is verified as "full" and
reported as mis-rotated on two axes nothing ever wrote.
"none" exists for domains like a DarkRP job spawn, which stores three coordinates and
nothing else.
An adapter that bridges another addon declares it, so Blueprints can start without that addon
present:
dependency = { name = "DarkRP", -- required, shown to operators Available = function() -- required if not DarkRP then return false, "DarkRP is not running on this server" end return true end, Version = function() return "2.7.0" end, -- optional, diagnostics only},
Available runs before every capture, so keep it to a table lookup or one indexed
query. Its reason is the sentence an operator reads on the Adapters screen and in a blocked
plan, so write it.
An Available that throws is reported as an adapter fault, distinct from a missing
dependency.
Version is reported and never compared. It gates nothing.
The consequence that matters: while your dependency is missing, your adapter is left out
of the Version manifest entirely.A Version that listed it with zero objects would assert your domain is empty, and restoring
that assertion would delete it. Absent means unknown, not none.
{"docsConfig":{"theme":"aspen","$schema":"https://mintlify.com/docs.json","name":"Vetra","description":"Documentation for the Vetra suite of Garry's Mod addons. Vetra Blueprints versions, migrates and deploys a server's managed setup, and the Adapter SDK lets any addon take part.","colors":{"primary":"#8fa343","light":"#c5e258","dark":"#8fa343"},"logo":{"light":"https://mintcdn.com/vetra/aE1YWFG_rv2nLo9f/logo/light.svg?fit=max\u0026auto=format\u0026n=aE1YWFG_rv2nLo9f\u0026q=85\u0026s=8badc512ca33228edb6d702138d0bb4a","dark":"https://mintcdn.com/vetra/aE1YWFG_rv2nLo9f/logo/dark.svg?fit=max\u0026auto=format\u0026n=aE1YWFG_rv2nLo9f\u0026q=85\u0026s=72230eb900ca97d5dfdf75675bb036d8","href":"/"},"favicon":"https://mintcdn.com/vetra/aE1YWFG_rv2nLo9f/favicon.svg?fit=max\u0026auto=format\u0026n=aE1YWFG_rv2nLo9f\u0026q=85\u0026s=747c5fa33d0dc6bf8a4ec7755955a8ee","appearance":{"default":"dark"},"navbar":{"links":[{"href":"/sdk/overview","label":"Adapter SDK"}],"primary":{"type":"button","label":"Install Blueprints","href":"/blueprints/installation"}},"footer":{"links":[{"header":"Blueprints","items":[{"label":"Overview","href":"/blueprints/overview"},{"label":"Installation","href":"/blueprints/installation"},{"label":"Supported objects","href":"/blueprints/supported-objects"}]},{"header":"Developers","items":[{"label":"Adapter SDK","href":"/sdk/overview"},{"label":"Quickstart","href":"/sdk/quickstart"},{"label":"API reference","href":"/sdk/api-reference"}]},{"header":"Support","items":[{"label":"Troubleshooting","href":"/support/troubleshooting"},{"label":"Licensing","href":"/support/licensing"},{"label":"Get help","href":"/support/getting-support"}]}]},"search":{"prompt":"Search the Vetra documentation"},"seo":{"metatags":{"og:site_name":"Vetra Documentation","og:type":"website","twitter:card":"summary_large_image"},"indexing":"all"},"fonts":{"heading":{"family":"IBM Plex Sans Condensed","weight":600},"body":{"family":"IBM Plex Sans"}},"icons":{"library":"lucide"},"styling":{"eyebrows":"breadcrumbs","codeblocks":{"theme":"min-dark"},"latex":false},"navigation":{"pages":[]}},"deployment":{"deploymentMetadataValue":{"subdomain":"vetra","hasAudioNarration":false,"actualSubdomain":"vetra","customDomains":["docs.vetrasuite.com"],"gitSource":{"type":"github","owner":"lhnfe","repo":"vetra-docs","deployBranch":"main","contentDirectory":"","isPrivate":false,"mountPath":"","label":""},"chroma":{"latestBuiltDeploymentHistoryId":"6aa2cdb530737d4552c0ae53","collectionId":"501b6eb9-b36b-420c-9434-ea0a0a1e08ea","liveDeploymentHistoryId":"6aa2cdb530737d4552c0ae53","schemaVersion":"v3"},"feedback":{"thumbs":true,"edits":true,"issues":true,"agentFeedback":true},"entitlements":{"AI_CHAT":{"status":"DISABLED"},"WEB_MCP":{"status":"DISABLED"}},"buildId":"6aa2cdb530737d4552c0ae53","clientVersion":"0.0.3966","searchFilterCounts":[{"language":"default","tag":"Adapter SDK","count":20},{"language":"default","tag":"Blueprints","count":18},{"language":"default","tag":"Start here","count":8},{"language":"default","tag":"Support","count":8},{"language":"default","tag":"World operations","count":6},{"language":"default","tag":"Core workflow","count":5},{"language":"default","tag":"The contracts","count":5},{"language":"default","tag":"Shipping an adapter","count":5},{"language":"default","tag":"Help","count":4},{"language":"default","tag":"What it manages","count":3}]},"assistantConfig":{"enableAskAiButton":false,"enableStarterQuestions":false,"enableContextualStarterQuestions":false,"enableAskAiSearchBar":true},"hasChatPermissions":false,"hasWebMcpPermissions":false,"toggles":[{"name":"kill-isr","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"agent-mintlify-slack","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"editor-v7-file-tree","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"agent-integrations-settings","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"automations-pr-grouping","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"custom-prefix","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"analytics-v7","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"user-flows-analytics","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"no-publish-flow","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workos-onboarding","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"llms-txt-v2","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"pause-universal-search-reconcile","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"snapshot-serving-live","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"stripTemperatureMiddleware","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"per-site-search-delta","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"support-platform-pylon-zendesk","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"snapshot-serving","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"instant-rollback","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"granular-notifications-server","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"granular-notifications","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"editor-private-page-search","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"automations-fable","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"fixed-ai-action-pricing","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"automation-resolve-merge-conflict","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"scoped-publishing","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"mintql","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"page-level-analytics","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"editor-version-history-v2","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"editor-nav-generations","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"billing-page-v2","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workspace-projects","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"assistant-v5","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"byok-sep-25","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"fork-pr-previews","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"revalidation-jwt","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"vite-origin","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"editor-agent-model-picker","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"billing-updated-9-15","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"hobby-abuse-scan","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"dashboard-inbox","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false}],"isAtlas":false,"googleFontsInlined":true},"customCss":[],"page":{"pagePath":"sdk/capabilities","slug":"/sdk/capabilities","pageMetadata":{"title":"Capabilities","description":"A capability is not a function that looks roughly right. It is a promise about a result, for every object your adapter produces.","href":"/sdk/capabilities"},"description":{"compiledSource":"\"use strict\";\nconst {jsx: _jsx} = arguments[0];\nconst {useMDXComponents: _provideComponents} = arguments[0];\nfunction _createMdxContent(props) {\n const _components = {\n p: \"p\",\n ..._provideComponents(),\n ...props.components\n };\n return _jsx(_components.p, {\n children: \"A capability is not a function that looks roughly right. It is a promise about a result, for every object your adapter produces.\"\n });\n}\nfunction MDXContent(props = {}) {\n const {wrapper: MDXLayout} = {\n ..._provideComponents(),\n ...props.components\n };\n return MDXLayout ? _jsx(MDXLayout, {\n ...props,\n children: _jsx(_createMdxContent, {\n ...props\n })\n }) : _createMdxContent(props);\n}\nreturn {\n default: MDXContent\n};\n","frontmatter":{},"scope":{}},"pageType":"default","mdx":{"mdxSource":{"compiledSource":"\"use strict\";const{Fragment:_Fragment,jsx:_jsx,jsxs:_jsxs}=arguments[0];const{useMDXComponents:_provideComponents}=arguments[0];function _createMdxContent(props){const _components={a:`a`,code:`code`,em:`em`,li:`li`,ol:`ol`,p:`p`,pre:`pre`,span:`span`,strong:`strong`,ul:`ul`,..._provideComponents(),...props.components},{CodeBlock,Heading,Warning}=_components;if(!CodeBlock)_missingMdxReference(`CodeBlock`,true);if(!Heading)_missingMdxReference(`Heading`,true);if(!Warning)_missingMdxReference(`Warning`,true);return _jsxs(_Fragment,{children:[_jsx(Heading,{level:`2`,id:`the-five`,children:`The five`}),`\n`,_jsx(CodeBlock,{filename:``,numberOfLines:`6`,language:`text`,children:_jsx(_components.pre,{className:`shiki shiki-themes min-dark min-dark`,style:{backgroundColor:`#1f1f1f`,\"--shiki-dark-bg\":`#1f1f1f`,color:`#b392f0`,\"--shiki-dark\":`#b392f0`},language:`text`,children:_jsxs(_components.code,{language:`text`,numberOfLines:`6`,children:[_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{children:`snapshot capture your domain REQUIRED`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{children:`transform move and reorient an existing object`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{children:`materialize rebuild one of your records, and undo that`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{children:`properties conform an existing object to a record you captured`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{children:`remove destroy an existing object requires materialize`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{})})]})})}),`\n`,_jsxs(_components.p,{children:[`There is no `,_jsx(_components.code,{children:`create`}),`, `,_jsx(_components.code,{children:`update`}),`, `,_jsx(_components.code,{children:`delete`}),`, `,_jsx(_components.code,{children:`migrate`}),` or `,_jsx(_components.code,{children:`restore`}),`. Migration and restore are\nBlueprints `,_jsx(_components.strong,{children:`workflows`}),` composed from the five above; they are not things an adapter\ndeclares.`]}),`\n`,_jsx(_components.p,{children:`An unrecognised capability name is ignored with a warning, so an adapter written for a newer\nBlueprints still loads here: it loses that ability rather than its whole domain.`}),`\n`,_jsx(Heading,{level:`2`,id:`totality-what-declaring-one-actually-promises`,children:`Totality: what declaring one actually promises`}),`\n`,_jsxs(Warning,{children:[_jsxs(_components.p,{children:[`Declaring capability `,_jsx(_components.code,{children:`C`}),` asserts that for `,_jsx(_components.strong,{children:`every`}),` record this adapter emits from `,_jsx(_components.code,{children:`Collect`}),`,\nthe operation named by `,_jsx(_components.code,{children:`C`}),` either`]}),_jsxs(_components.ol,{children:[`\n`,_jsxs(_components.li,{children:[_jsx(_components.strong,{children:`completes`}),`, such that its postcondition is observable by a later `,_jsx(_components.code,{children:`Collect`}),`; or`]}),`\n`,_jsxs(_components.li,{children:[`is `,_jsx(_components.strong,{children:`refused in phase 1`}),`, with a code and a reason.`]}),`\n`]}),_jsx(_components.p,{children:`There is no third outcome.`})]}),`\n`,_jsxs(_components.p,{children:[_jsx(_components.code,{children:`properties = true`}),` when 70% of your records work and 30% throw at random is not a partial\ncapability. It is a false one, and the operations built on it (all-or-nothing group\nmaterialization, restore with rollback) are built on the promise, not on the average.`]}),`\n`,_jsx(_components.p,{children:`Two corollaries:`}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[_jsx(_components.strong,{children:`Phase 1 never writes, and never throws.`}),` A thrown error is a contract violation, not a\nrefusal. Blueprints contains it, names you and fails the operation, but you are at fault.`]}),`\n`,_jsxs(_components.li,{children:[_jsx(_components.strong,{children:`Phase 2 is per-record all-or-nothing.`}),` On failure return `,_jsx(_components.code,{children:`false, reason`}),` having left the\nobject as you found it.`]}),`\n`]}),`\n`,_jsx(_components.p,{children:`When an operation is impossible for one particular record, that is what phase 1 is for:`}),`\n`,_jsx(CodeBlock,{filename:``,numberOfLines:`1`,language:`lua`,children:_jsx(_components.pre,{className:`shiki shiki-themes min-dark min-dark`,style:{backgroundColor:`#1f1f1f`,\"--shiki-dark-bg\":`#1f1f1f`,color:`#b392f0`,\"--shiki-dark\":`#b392f0`},language:`lua`,children:_jsxs(_components.code,{language:`lua`,numberOfLines:`1`,children:[_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#F97583`,\"--shiki-dark\":`#F97583`},children:`return`}),_jsx(_components.span,{style:{color:`#B392F0`,\"--shiki-dark\":`#B392F0`},children:` { ok `}),_jsx(_components.span,{style:{color:`#F97583`,\"--shiki-dark\":`#F97583`},children:`=`}),_jsx(_components.span,{style:{color:`#79B8FF`,\"--shiki-dark\":`#79B8FF`},children:` false`}),_jsx(_components.span,{style:{color:`#B392F0`,\"--shiki-dark\":`#B392F0`},children:`, code `}),_jsx(_components.span,{style:{color:`#F97583`,\"--shiki-dark\":`#F97583`},children:`=`}),_jsx(_components.span,{style:{color:`#FFAB70`,\"--shiki-dark\":`#FFAB70`},children:` \"unsupported\"`}),_jsx(_components.span,{style:{color:`#B392F0`,\"--shiki-dark\":`#B392F0`},children:`, reason `}),_jsx(_components.span,{style:{color:`#F97583`,\"--shiki-dark\":`#F97583`},children:`=`}),_jsx(_components.span,{style:{color:`#FFAB70`,\"--shiki-dark\":`#FFAB70`},children:` \"this turret is welded to a door\" `}),_jsx(_components.span,{style:{color:`#B392F0`,\"--shiki-dark\":`#B392F0`},children:`}`})]}),`\n`]})})}),`\n`,_jsxs(_components.p,{children:[_jsx(_components.code,{children:`code`}),` is `,_jsx(_components.code,{children:`\"unsupported\"`}),` (this can never work here) or `,_jsx(_components.code,{children:`\"invalid\"`}),` (this record or target is\nwrong), and `,_jsx(_components.code,{children:`reason`}),` must be a non-empty string. Both are checked: a refusal without them is\nitself a contract failure. Blueprints shows the reason to the admin and plans around it.`]}),`\n`,_jsxs(Heading,{level:`2`,id:`snapshot-required`,children:[_jsx(_components.code,{children:`snapshot`}),`: required`]}),`\n`,_jsx(CodeBlock,{filename:``,numberOfLines:`2`,language:`text`,children:_jsx(_components.pre,{className:`shiki shiki-themes min-dark min-dark`,style:{backgroundColor:`#1f1f1f`,\"--shiki-dark-bg\":`#1f1f1f`,color:`#b392f0`,\"--shiki-dark\":`#b392f0`},language:`text`,children:_jsxs(_components.code,{language:`text`,numberOfLines:`2`,children:[_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{children:`Collect(self) -> array of normalized objects`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{})})]})})}),`\n`,_jsxs(_components.p,{children:[`All of them or none. If you cannot answer, `,_jsx(_components.code,{children:`error()`}),`: the capture fails, names you, and is\nrecoverable.`]}),`\n`,_jsx(Warning,{children:_jsxs(_components.p,{children:[`A Version silently missing a category of objects is not degraded, it is `,_jsx(_components.strong,{children:`wrong`}),`. Diffed\nagainst a healthy one it reports every one of them as removed, and a restore acts on that.`]})}),`\n`,_jsxs(_components.p,{children:[_jsx(_components.code,{children:`Collect`}),` may have side effects (assigning identity on first sight is one) but two consecutive\ncalls on an unchanged world must name the same objects.`]}),`\n`,_jsx(Heading,{level:`2`,id:`transform`,children:_jsx(_components.code,{children:`transform`})}),`\n`,_jsx(CodeBlock,{filename:``,numberOfLines:`2`,language:`text`,children:_jsx(_components.pre,{className:`shiki shiki-themes min-dark min-dark`,style:{backgroundColor:`#1f1f1f`,\"--shiki-dark-bg\":`#1f1f1f`,color:`#b392f0`,\"--shiki-dark\":`#b392f0`},language:`text`,children:_jsxs(_components.code,{language:`text`,numberOfLines:`2`,children:[_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{children:`ApplyTransform(self, localId, transform) -> ok, reason`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{})})]})})}),`\n`,_jsxs(_components.p,{children:[`Position and orientation only. Deliberately not `,_jsx(_components.code,{children:`update`}),`: an adapter that can move a turret\nmust not thereby be believed able to rewrite a job definition.`]}),`\n`,_jsxs(_components.p,{children:[`See `,_jsx(_components.a,{href:`/sdk/transform`,children:`Transform`}),`.`]}),`\n`,_jsx(Heading,{level:`2`,id:`materialize`,children:_jsx(_components.code,{children:`materialize`})}),`\n`,_jsx(_components.p,{children:`Three functions, and the third is the point.`}),`\n`,_jsx(CodeBlock,{filename:``,numberOfLines:`6`,language:`text`,children:_jsx(_components.pre,{className:`shiki shiki-themes min-dark min-dark`,style:{backgroundColor:`#1f1f1f`,\"--shiki-dark-bg\":`#1f1f1f`,color:`#b392f0`,\"--shiki-dark\":`#b392f0`},language:`text`,children:_jsxs(_components.code,{language:`text`,numberOfLines:`6`,children:[_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{children:`PrepareMaterialize(self, source, transform, ctx) -> { ok = true, orientation, preview, warnings }`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{children:` or { ok = false, code, reason }`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{children:`Materialize(self, source, transform, ctx) -> { ok = true, localId, identity }`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{children:` or { ok = false, reason }`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{children:`ReleaseMaterialized(self, localId) -> ok, reason`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{})})]})})}),`\n`,_jsxs(_components.p,{children:[_jsx(_components.code,{children:`remove`}),` requires it. An adapter that can destroy but cannot rebuild turns every removal into\na one-way door, and the durable rollback path runs through materialize. Registration refuses\nthe combination.`]}),`\n`,_jsxs(_components.p,{children:[`See `,_jsx(_components.a,{href:`/sdk/materialize`,children:`Materialize`}),`.`]}),`\n`,_jsx(Heading,{level:`2`,id:`properties`,children:_jsx(_components.code,{children:`properties`})}),`\n`,_jsx(CodeBlock,{filename:``,numberOfLines:`4`,language:`text`,children:_jsx(_components.pre,{className:`shiki shiki-themes min-dark min-dark`,style:{backgroundColor:`#1f1f1f`,\"--shiki-dark-bg\":`#1f1f1f`,color:`#b392f0`,\"--shiki-dark\":`#b392f0`},language:`text`,children:_jsxs(_components.code,{language:`text`,numberOfLines:`4`,children:[_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{children:`PrepareProperties(self, localId, source, ctx) -> { ok = true, orientation, warnings }`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{children:` or { ok = false, code, reason }`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{children:`ApplyProperties(self, localId, source, ctx) -> ok, reason`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{})})]})})}),`\n`,_jsxs(_components.p,{children:[`Two functions, not three: the inverse of `,_jsx(_components.code,{children:`ApplyProperties`}),` `,_jsx(_components.strong,{children:`is`}),` `,_jsx(_components.code,{children:`ApplyProperties`}),`, called\nwith the earlier record.`]}),`\n`,_jsxs(_components.p,{children:[`The bound is strict: `,_jsx(_components.strong,{children:`the only values you may write are ones you can also read back.`})]}),`\n`,_jsxs(_components.p,{children:[`See `,_jsx(_components.a,{href:`/sdk/properties`,children:`Properties`}),`.`]}),`\n`,_jsx(Heading,{level:`2`,id:`remove`,children:_jsx(_components.code,{children:`remove`})}),`\n`,_jsx(CodeBlock,{filename:``,numberOfLines:`3`,language:`text`,children:_jsx(_components.pre,{className:`shiki shiki-themes min-dark min-dark`,style:{backgroundColor:`#1f1f1f`,\"--shiki-dark-bg\":`#1f1f1f`,color:`#b392f0`,\"--shiki-dark\":`#b392f0`},language:`text`,children:_jsxs(_components.code,{language:`text`,numberOfLines:`3`,children:[_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{children:`PrepareRemove(self, localId, ctx) -> { ok = true } or { ok = false, code, reason }`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{children:`Remove(self, localId, ctx) -> ok, reason`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{})})]})})}),`\n`,_jsx(_components.p,{children:`Genuinely destructive, and the contract says so rather than dressing it as narrow.`}),`\n`,_jsxs(_components.p,{children:[`See `,_jsx(_components.a,{href:`/sdk/removal`,children:`Removal`}),`.`]}),`\n`,_jsx(Heading,{level:`2`,id:`orientation`,children:`Orientation`}),`\n`,_jsxs(_components.p,{children:[`Both `,_jsx(_components.code,{children:`PrepareMaterialize`}),` and `,_jsx(_components.code,{children:`PrepareProperties`}),` may declare how much of a recorded\norientation your adapter will actually honour:`]}),`\n`,_jsx(CodeBlock,{filename:``,numberOfLines:`3`,language:`lua`,children:_jsx(_components.pre,{className:`shiki shiki-themes min-dark min-dark`,style:{backgroundColor:`#1f1f1f`,\"--shiki-dark-bg\":`#1f1f1f`,color:`#b392f0`,\"--shiki-dark\":`#b392f0`},language:`lua`,children:_jsxs(_components.code,{language:`lua`,numberOfLines:`3`,children:[_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#B392F0`,\"--shiki-dark\":`#B392F0`},children:`orientation `}),_jsx(_components.span,{style:{color:`#F97583`,\"--shiki-dark\":`#F97583`},children:`=`}),_jsx(_components.span,{style:{color:`#FFAB70`,\"--shiki-dark\":`#FFAB70`},children:` \"full\" `}),_jsx(_components.span,{style:{color:`#6B737C`,\"--shiki-dark\":`#6B737C`},children:`-- pitch, yaw and roll are all reproduced (the DEFAULT)`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#B392F0`,\"--shiki-dark\":`#B392F0`},children:`orientation `}),_jsx(_components.span,{style:{color:`#F97583`,\"--shiki-dark\":`#F97583`},children:`=`}),_jsx(_components.span,{style:{color:`#FFAB70`,\"--shiki-dark\":`#FFAB70`},children:` \"yaw\" `}),_jsx(_components.span,{style:{color:`#6B737C`,\"--shiki-dark\":`#6B737C`},children:`-- only yaw; pitch and roll are dropped`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#B392F0`,\"--shiki-dark\":`#B392F0`},children:`orientation `}),_jsx(_components.span,{style:{color:`#F97583`,\"--shiki-dark\":`#F97583`},children:`=`}),_jsx(_components.span,{style:{color:`#FFAB70`,\"--shiki-dark\":`#FFAB70`},children:` \"none\" `}),_jsx(_components.span,{style:{color:`#6B737C`,\"--shiki-dark\":`#6B737C`},children:`-- position only; there is no facing at all`})]}),`\n`]})})}),`\n`,_jsxs(_components.p,{children:[`Omitting it means `,_jsx(_components.code,{children:`\"full\"`}),`. An `,_jsx(_components.strong,{children:`unrecognised`}),` value is a hard refusal of that operation, not\na downgrade.`]}),`\n`,_jsxs(Warning,{children:[_jsxs(_components.p,{children:[_jsx(_components.strong,{children:`Which function is asked depends on what you declare.`}),` Post-restore verification asks\n`,_jsx(_components.code,{children:`PrepareProperties`}),` for any adapter that declares `,_jsx(_components.code,{children:`properties`}),`, and `,_jsx(_components.code,{children:`PrepareMaterialize`}),`\notherwise.`]}),_jsxs(_components.p,{children:[`So if you declare `,_jsx(_components.code,{children:`properties`}),`, return your orientation from `,_jsx(_components.strong,{children:`both`}),`. An adapter that says\n`,_jsx(_components.code,{children:`\"yaw\"`}),` in `,_jsx(_components.code,{children:`PrepareMaterialize`}),` and nothing in `,_jsx(_components.code,{children:`PrepareProperties`}),` is verified as `,_jsx(_components.code,{children:`\"full\"`}),` and\nreported as mis-rotated on two axes nothing ever wrote.`]})]}),`\n`,_jsxs(_components.p,{children:[_jsx(_components.code,{children:`\"none\"`}),` exists for domains like a DarkRP job spawn, which stores three coordinates and\nnothing else.`]}),`\n`,_jsx(Heading,{level:`2`,id:`dependencies`,children:`Dependencies`}),`\n`,_jsx(_components.p,{children:`An adapter that bridges another addon declares it, so Blueprints can start without that addon\npresent:`}),`\n`,_jsx(CodeBlock,{filename:``,numberOfLines:`8`,language:`lua`,children:_jsx(_components.pre,{className:`shiki shiki-themes min-dark min-dark`,style:{backgroundColor:`#1f1f1f`,\"--shiki-dark-bg\":`#1f1f1f`,color:`#b392f0`,\"--shiki-dark\":`#b392f0`},language:`lua`,children:_jsxs(_components.code,{language:`lua`,numberOfLines:`8`,children:[_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#B392F0`,\"--shiki-dark\":`#B392F0`},children:`dependency `}),_jsx(_components.span,{style:{color:`#F97583`,\"--shiki-dark\":`#F97583`},children:`=`}),_jsx(_components.span,{style:{color:`#B392F0`,\"--shiki-dark\":`#B392F0`},children:` {`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#B392F0`,\"--shiki-dark\":`#B392F0`},children:` name `}),_jsx(_components.span,{style:{color:`#F97583`,\"--shiki-dark\":`#F97583`},children:`=`}),_jsx(_components.span,{style:{color:`#FFAB70`,\"--shiki-dark\":`#FFAB70`},children:` \"DarkRP\"`}),_jsx(_components.span,{style:{color:`#B392F0`,\"--shiki-dark\":`#B392F0`},children:`, `}),_jsx(_components.span,{style:{color:`#6B737C`,\"--shiki-dark\":`#6B737C`},children:`-- required, shown to operators`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#B392F0`,\"--shiki-dark\":`#B392F0`},children:` Available `}),_jsx(_components.span,{style:{color:`#F97583`,\"--shiki-dark\":`#F97583`},children:`=`}),_jsx(_components.span,{style:{color:`#F97583`,\"--shiki-dark\":`#F97583`},children:` function`}),_jsx(_components.span,{style:{color:`#B392F0`,\"--shiki-dark\":`#B392F0`},children:`() `}),_jsx(_components.span,{style:{color:`#6B737C`,\"--shiki-dark\":`#6B737C`},children:`-- required`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#F97583`,\"--shiki-dark\":`#F97583`},children:` if`}),_jsx(_components.span,{style:{color:`#F97583`,\"--shiki-dark\":`#F97583`},children:` not`}),_jsx(_components.span,{style:{color:`#B392F0`,\"--shiki-dark\":`#B392F0`},children:` DarkRP `}),_jsx(_components.span,{style:{color:`#F97583`,\"--shiki-dark\":`#F97583`},children:`then`}),_jsx(_components.span,{style:{color:`#F97583`,\"--shiki-dark\":`#F97583`},children:` return`}),_jsx(_components.span,{style:{color:`#79B8FF`,\"--shiki-dark\":`#79B8FF`},children:` false`}),_jsx(_components.span,{style:{color:`#B392F0`,\"--shiki-dark\":`#B392F0`},children:`, `}),_jsx(_components.span,{style:{color:`#FFAB70`,\"--shiki-dark\":`#FFAB70`},children:`\"DarkRP is not running on this server\" `}),_jsx(_components.span,{style:{color:`#F97583`,\"--shiki-dark\":`#F97583`},children:`end`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#F97583`,\"--shiki-dark\":`#F97583`},children:` return`}),_jsx(_components.span,{style:{color:`#79B8FF`,\"--shiki-dark\":`#79B8FF`},children:` true`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#F97583`,\"--shiki-dark\":`#F97583`},children:` end`}),_jsx(_components.span,{style:{color:`#B392F0`,\"--shiki-dark\":`#B392F0`},children:`,`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#B392F0`,\"--shiki-dark\":`#B392F0`},children:` Version `}),_jsx(_components.span,{style:{color:`#F97583`,\"--shiki-dark\":`#F97583`},children:`=`}),_jsx(_components.span,{style:{color:`#F97583`,\"--shiki-dark\":`#F97583`},children:` function`}),_jsx(_components.span,{style:{color:`#B392F0`,\"--shiki-dark\":`#B392F0`},children:`() `}),_jsx(_components.span,{style:{color:`#F97583`,\"--shiki-dark\":`#F97583`},children:`return`}),_jsx(_components.span,{style:{color:`#FFAB70`,\"--shiki-dark\":`#FFAB70`},children:` \"2.7.0\" `}),_jsx(_components.span,{style:{color:`#F97583`,\"--shiki-dark\":`#F97583`},children:`end`}),_jsx(_components.span,{style:{color:`#B392F0`,\"--shiki-dark\":`#B392F0`},children:`, `}),_jsx(_components.span,{style:{color:`#6B737C`,\"--shiki-dark\":`#6B737C`},children:`-- optional, diagnostics only`})]}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#B392F0`,\"--shiki-dark\":`#B392F0`},children:`},`})}),`\n`]})})}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[_jsx(_components.code,{children:`Available`}),` runs `,_jsx(_components.strong,{children:`before every capture`}),`, so keep it to a table lookup or one indexed\nquery. Its reason is the sentence an operator reads on the Adapters screen and in a blocked\nplan, so write it.`]}),`\n`,_jsxs(_components.li,{children:[`An `,_jsx(_components.code,{children:`Available`}),` that throws is reported as an adapter fault, distinct from a missing\ndependency.`]}),`\n`,_jsxs(_components.li,{children:[_jsx(_components.code,{children:`Version`}),` is reported and `,_jsx(_components.strong,{children:`never compared`}),`. It gates nothing.`]}),`\n`]}),`\n`,_jsxs(Warning,{children:[_jsxs(_components.p,{children:[_jsx(_components.strong,{children:`The consequence that matters:`}),` while your dependency is missing, your adapter is left out\nof the Version manifest entirely.`]}),_jsxs(_components.p,{children:[`A Version that listed it with zero objects would assert your domain is empty, and restoring\nthat assertion would delete it. Absent means `,_jsx(_components.em,{children:`unknown`}),`, not `,_jsx(_components.em,{children:`none`}),`.`]})]})]})}function MDXContent(props={}){const{wrapper:MDXLayout}={..._provideComponents(),...props.components};return MDXLayout?_jsx(MDXLayout,{...props,children:_jsx(_createMdxContent,{...props})}):_createMdxContent(props)}return{default:MDXContent};function _missingMdxReference(id,component){throw new Error(`Expected `+(component?`component`:`object`)+\" `\"+id+\"` to be defined: you likely forgot to import, pass, or provide it.\")}","frontmatter":{},"scope":{"codeStyling":{"theme":"min-dark"},"config":{},"pageMetadata":{"title":"Capabilities","description":"A capability is not a function that looks roughly right. It is a promise about a result, for every object your adapter produces.","href":"/sdk/capabilities"}}},"mdxSourceWithNoJs":null},"mdxExtracts":{"tableOfContents":[{"title":"The five","slug":"the-five","depth":2,"children":[]},{"title":"Totality: what declaring one actually promises","slug":"totality-what-declaring-one-actually-promises","depth":2,"children":[]},{"title":"snapshot: required","slug":"snapshot-required","depth":2,"children":[]},{"title":"transform","slug":"transform","depth":2,"children":[]},{"title":"materialize","slug":"materialize","depth":2,"children":[]},{"title":"properties","slug":"properties","depth":2,"children":[]},{"title":"remove","slug":"remove","depth":2,"children":[]},{"title":"Orientation","slug":"orientation","depth":2,"children":[]},{"title":"Dependencies","slug":"dependencies","depth":2,"children":[]}],"codeExamples":{}},"jsLinks":[],"subdomain":"vetra","siteName":"Vetra","title":"Capabilities - Vetra","navMaps":{"firstHrefInVersion":{},"firstHrefInLanguage":{}},"lastModified":"2026-09-10T13:23:49.913Z","originalFileLocation":"sdk/capabilities.mdx"},"headHtml":"\u003cscript data-mint-head>(function(){var d=document.documentElement,p=null;if(!p){try{p=localStorage.getItem(\"isDarkMode\")}catch(e){}if(p==='true')p='dark';else if(p==='false')p='light';if(p!=='light'\u0026\u0026p!=='dark'\u0026\u0026p!=='system')p=\"dark\"}var r=p==='system'?(matchMedia(\"(prefers-color-scheme: dark)\").matches?'dark':'light'):p;d.classList.remove(r==='dark'?'light':'dark');d.classList.add(r);d.setAttribute(\"data-theme-preference\",p)})();\u003c/script>\u003cmeta data-mint-head name=\"description\" content=\"A capability is not a function that looks roughly right. It is a promise about a result, for every object your adapter produces.\">\u003cmeta data-mint-head name=\"application-name\" content=\"Vetra\">\u003cmeta data-mint-head name=\"generator\" content=\"Mintlify\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"192x192\" href=\"/mintlify-assets/_mintlify/favicons/vetra/eqM5jWinqfsvrg8a/_generated/favicon/android-chrome-192x192.png\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"apple-touch-icon\" type=\"image/png\" sizes=\"180x180\" href=\"/mintlify-assets/_mintlify/favicons/vetra/eqM5jWinqfsvrg8a/_generated/favicon/apple-touch-icon.png\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"16x16\" href=\"/mintlify-assets/_mintlify/favicons/vetra/eqM5jWinqfsvrg8a/_generated/favicon/favicon-16x16.png\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"32x32\" href=\"/mintlify-assets/_mintlify/favicons/vetra/eqM5jWinqfsvrg8a/_generated/favicon/favicon-32x32.png\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"shortcut icon\" type=\"image/x-icon\" href=\"/mintlify-assets/_mintlify/favicons/vetra/eqM5jWinqfsvrg8a/_generated/favicon/favicon.ico\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"192x192\" href=\"/mintlify-assets/_mintlify/favicons/vetra/eqM5jWinqfsvrg8a/_generated/favicon-dark/android-chrome-192x192.png\" media=\"(prefers-color-scheme: dark)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"16x16\" href=\"/mintlify-assets/_mintlify/favicons/vetra/eqM5jWinqfsvrg8a/_generated/favicon-dark/favicon-16x16.png\" media=\"(prefers-color-scheme: dark)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"32x32\" href=\"/mintlify-assets/_mintlify/favicons/vetra/eqM5jWinqfsvrg8a/_generated/favicon-dark/favicon-32x32.png\" media=\"(prefers-color-scheme: dark)\">\u003clink data-mint-head rel=\"shortcut icon\" type=\"image/x-icon\" href=\"/mintlify-assets/_mintlify/favicons/vetra/eqM5jWinqfsvrg8a/_generated/favicon-dark/favicon.ico\" media=\"(prefers-color-scheme: dark)\">\u003cmeta data-mint-head property=\"og:title\" content=\"Capabilities - Vetra\">\u003cmeta data-mint-head property=\"og:description\" content=\"A capability is not a function that looks roughly right. It is a promise about a result, for every object your adapter produces.\">\u003cmeta data-mint-head property=\"og:site_name\" content=\"Vetra Documentation\">\u003cmeta data-mint-head property=\"og:url\" content=\"https://docs.vetrasuite.com/sdk/capabilities\">\u003cmeta data-mint-head property=\"og:type\" content=\"website\">\u003cmeta data-mint-head property=\"og:image\" content=\"https://vetra.mintlify.app/_mintlify/api/og/v2?division=The+contracts\u0026amp;title=Capabilities\u0026amp;description=A+capability+is+not+a+function+that+looks+roughly+right.+It+is+a+promise+about+a+result%2C+for+every+object+your+adapter+produces.\u0026amp;theme=cca15c7ac5e8640f3bfab9da\u0026amp;signature=cd3a807ca5bdc74ab42af04001f3fc7965a20fcf9c499b78dc36f26d689f2e37\">\u003cmeta data-mint-head property=\"og:image:width\" content=\"1200\">\u003cmeta data-mint-head property=\"og:image:height\" content=\"630\">\u003cmeta data-mint-head name=\"twitter:card\" content=\"summary_large_image\">\u003cmeta data-mint-head name=\"twitter:title\" content=\"Capabilities - Vetra\">\u003cmeta data-mint-head name=\"twitter:description\" content=\"A capability is not a function that looks roughly right. It is a promise about a result, for every object your adapter produces.\">\u003cmeta data-mint-head name=\"twitter:image\" content=\"https://vetra.mintlify.app/_mintlify/api/og/v2?division=The+contracts\u0026amp;title=Capabilities\u0026amp;description=A+capability+is+not+a+function+that+looks+roughly+right.+It+is+a+promise+about+a+result%2C+for+every+object+your+adapter+produces.\u0026amp;theme=cca15c7ac5e8640f3bfab9da\u0026amp;signature=cd3a807ca5bdc74ab42af04001f3fc7965a20fcf9c499b78dc36f26d689f2e37\">\u003cmeta data-mint-head name=\"twitter:image:width\" content=\"1200\">\u003cmeta data-mint-head name=\"twitter:image:height\" content=\"630\">\u003cmeta data-mint-head name=\"msapplication-config\" content=\"/mintlify-assets/_mintlify/favicons/vetra/eqM5jWinqfsvrg8a/_generated/favicon/browserconfig.xml\">\u003cmeta data-mint-head name=\"apple-mobile-web-app-title\" content=\"Vetra\">\u003cmeta data-mint-head name=\"msapplication-TileColor\" content=\"#8fa343\">\u003clink data-mint-head rel=\"canonical\" href=\"https://docs.vetrasuite.com/sdk/capabilities\">\u003clink data-mint-head rel=\"alternate\" type=\"application/xml\" href=\"/sitemap.xml\">\u003clink data-mint-head rel=\"alternate\" type=\"text/markdown\" href=\"/sdk/capabilities.md\">\u003cscript data-mint-head type=\"application/ld+json\">{\"@context\":\"https://schema.org\",\"@graph\":[{\"@type\":\"Organization\",\"@id\":\"https://docs.vetrasuite.com/#organization\",\"name\":\"Vetra\",\"url\":\"https://docs.vetrasuite.com\",\"logo\":{\"@type\":\"ImageObject\",\"url\":\"https://mintcdn.com/vetra/aE1YWFG_rv2nLo9f/logo/light.svg?fit=max\u0026auto=format\u0026n=aE1YWFG_rv2nLo9f\u0026q=85\u0026s=8badc512ca33228edb6d702138d0bb4a\"}},{\"@type\":\"WebSite\",\"@id\":\"https://docs.vetrasuite.com#website\",\"name\":\"Vetra\",\"url\":\"https://docs.vetrasuite.com\",\"publisher\":{\"@id\":\"https://docs.vetrasuite.com/#organization\"}},{\"@type\":\"WebPage\",\"@id\":\"https://docs.vetrasuite.com/sdk/capabilities#webpage\",\"url\":\"https://docs.vetrasuite.com/sdk/capabilities\",\"name\":\"Capabilities\",\"description\":\"A capability is not a function that looks roughly right. It is a promise about a result, for every object your adapter produces.\",\"dateModified\":\"2026-09-10T13:23:49.913Z\",\"isPartOf\":{\"@id\":\"https://docs.vetrasuite.com#website\"},\"breadcrumb\":{\"@id\":\"https://docs.vetrasuite.com/sdk/capabilities#breadcrumb\"}},{\"@type\":\"BreadcrumbList\",\"@id\":\"https://docs.vetrasuite.com/sdk/capabilities#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"The contracts\",\"item\":\"https://docs.vetrasuite.com/sdk/registry\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Capabilities\",\"item\":\"https://docs.vetrasuite.com/sdk/capabilities\"}]},{\"@type\":[\"Article\",\"TechArticle\"],\"@id\":\"https://docs.vetrasuite.com/sdk/capabilities#article\",\"headline\":\"Capabilities\",\"name\":\"Capabilities\",\"description\":\"A capability is not a function that looks roughly right. It is a promise about a result, for every object your adapter produces.\",\"url\":\"https://docs.vetrasuite.com/sdk/capabilities\",\"mainEntityOfPage\":{\"@id\":\"https://docs.vetrasuite.com/sdk/capabilities#webpage\"},\"dateModified\":\"2026-09-10T13:23:49.913Z\",\"publisher\":{\"@id\":\"https://docs.vetrasuite.com/#organization\"},\"isPartOf\":{\"@id\":\"https://docs.vetrasuite.com#website\"}}]}\u003c/script>\u003cscript data-mint-head type=\"application/ld+json\">{\"@context\":\"https://schema.org\",\"@type\":\"WebSite\",\"name\":\"Vetra\",\"creator\":{\"@type\":\"Organization\",\"name\":\"Mintlify\",\"url\":\"https://mintlify.com\"}}\u003c/script>","siteName":"Vetra","nav":{"global":null,"tabs":[{"tab":"Blueprints","icon":"layers","pages":[{"title":"Blueprints","href":""}]},{"tab":"Adapter SDK","icon":"plug","groups":[{"group":"Start here","pages":[{"title":"Adapter SDK","sidebarTitle":"Overview","href":"/sdk/overview"},{"title":"Quickstart","href":"/sdk/quickstart"},{"title":"Adapter anatomy","href":"/sdk/anatomy"}]},{"group":"The contracts","pages":[{"title":"Registry and lifecycle","href":"/sdk/registry"},{"title":"Identity","description":"The one field most likely to be wrong, and the one whose consequences are worst.","href":"/sdk/identity"},{"title":"Capabilities","href":"/sdk/capabilities"},{"title":"Capture and records","description":"What Collect must return, field by field, and why what you get back is a different shape.","href":"/sdk/records"},{"title":"Diff semantics","href":"/sdk/diff"}]},{"group":"World operations","pages":[{"title":"Transform","href":"/sdk/transform"},{"title":"Materialize","href":"/sdk/materialize"},{"title":"Properties","href":"/sdk/properties"},{"title":"Removal and ownership","href":"/sdk/removal"},{"title":"Migration and deployment","href":"/sdk/migration"},{"title":"Persistence integrations","href":"/sdk/persistence"}]},{"group":"Shipping an adapter","pages":[{"title":"Data versions and missing adapters","href":"/sdk/compatibility"},{"title":"Error handling","href":"/sdk/errors"},{"title":"Testing your adapter","href":"/sdk/testing"},{"title":"The example adapter","href":"/sdk/example-adapter"},{"title":"Compatibility checklist","href":"/sdk/checklist"}]},{"group":"Reference","pages":[{"title":"API reference","href":"/sdk/api-reference"}]}]},{"tab":"Support","icon":"life-buoy","pages":[{"title":"Support","href":"/support/licensing"}]}]},"title":"Capabilities - Vetra","buildId":"dpl_cadrRc5H9XQiJQbDFN5mcYRs2PAj","deploymentId":"dpl_cadrRc5H9XQiJQbDFN5mcYRs2PAj","dynamics":[],"chunks":[]}