Skills and instruction trust

Sophia treats skills as a governed instruction supply chain: owner-gated publication, compiled cross-harness workflows, final-byte installation receipts, and an explicitly unattested runtime signal that never widens agent authority.

Every claim on this page checked against the product source at a pinned revision — 2026-07-21 @ 804cbf79.

What this is

A skill is a stored body of instructions that an agent loads and follows as a playbook. Sophia Superpowers is the governed operating-method layer that teaches agents when and how to use Sophia: one canonical capability graph and set of workflow contracts, compiled into native routers for each supported agent tool. This page covers both boundaries around it: which instruction bodies may ever reach an agent, and what Sophia can honestly verify about the pack installed for a live session.

Why it exists

Most stored content is data — an agent reads it and decides what to do. A skill is not data. It is instructions, and the system delivers it automatically to whichever agent the task matches. So an unapproved skill body arriving in an agent’s context is not a content-quality problem; it is a cross-agent prompt injection, carrying the system’s own authority, delivered on the system’s own initiative. The code states the rule where it enforces it: “an unapproved body reaching an agent’s context IS the cross-agent injection” (proxy/src/mcp/skills.ts:556-565).

That is why publication is the one write no agent can perform. It is not a question of trusting our agents; it is that “agent-authored instructions that every other agent will obey” is a channel that should not exist without a person in it.

How it works

No agent profile can publish, however privileged. sophia.save_skill accepts only draft and refuses anything else before any write — no row, no file, no partial state (tools/skillTools.ts:262-288). This is not a permission check a stronger profile could pass: publishing happens on a different transport altogether. It is an owner HTTP route behind the daemon session key held by the owner’s paired browser, and an agent authenticates to the MCP surface, not to that one (routes/api/skillPublishRoutes.ts). There is no privilege level on the agent plane that reaches it. Publication is journaled, so which human approved which body is a matter of record — and an agent cannot overwrite a skill the owner already approved.

A draft is inert: never served, never ranked. A single predicate, isServableStatus (skills.ts:566), governs every read, and it lives in the data layer rather than in each handler — “so a future read path cannot reintroduce the hole by forgetting.” Drafts are excluded from load_skill, from list_skills (including its body-bearing projection), and from the ranker that feeds orient — the automatic-delivery path, where only published skills are eligible. Even draft titles are withheld, because an attacker-chosen title is itself a small injection surface. A test enumerates every agent-facing read path and asserts a draft body is unreachable through all of them.

The chokepoint is wherever the content can escape. The skills vault is a watched directory: anything written there is ingested into documents and full-text search, and would then be readable through ordinary document retrieval — straight around any check on the skill reader. A gate on one reader is a fence, not a chokepoint. So a draft never reaches disk at all. Publishing writes the file; unpublishing tears the ingested copy back out — the artifact, its full-text row, its index entry — because deleting a file does not un-ingest it (wiki/retractVaultArtifact.ts). If retraction fails, unpublish returns an error saying the body may still be readable, rather than a clean success.

The same principle decides who an agent is: a coordination message’s author is taken from the authenticated connection, never from the message body. A body claiming to be someone is text, not a credential.

The pack that ships with the product is compiled, and the compiler enforces least privilege. The sophia-* skills are authored once as a capability graph plus a workflow description, then rendered into a native pack per agent tool. Two checks run at build time, and both are failures rather than warnings: a workflow may not declare more privilege than the capability it stands on, and the compiler will not emit a composition mode it has not observed that tool actually support (proxy/src/superpowers/compatibility.ts). Where a tool’s behaviour is unknown, the fixture records "unknown" — it is never rounded up to true. And the compiled pack is held to a live cross-harness conformance gate: a fresh agent on each supported tool, run against the real daemon rather than asserted in a document, before a wave counts as done.

The daemon can distinguish a matching installed release from a model merely saying it loaded a skill. The installer hashes the final bytes it actually placed on disk, then injects an activation descriptor naming the installation, harness, adapter version, graph version, build-receipt hash and signature state into the lifecycle hook. At startup, resume, clear and compact, that hook performs a two-step activation with a connection-bound, single-use nonce. The daemon accepts the runtime signal only when the descriptor matches its exact current adapter, graph and build receipt (adapterCompatibility.ts). A release mismatch, replayed or expired nonce, or ordinary load_skill activity does not produce installed-router status. Update, rollback, uninstall and revocation clear prior activation rather than carrying trust forward.

That signal is deliberately authority-inert. A matching pack may organize the tools a connection already has; it cannot add a tool, widen entity scope, remove an approval gate or enable a privileged workflow. Current releases are unsigned, so the surfaced mode is installed_router_unattested, never a stronger claim. This is meaningful prompt-injection resistance because drift and model testimony cannot silently become trusted installation state. It is not a claim that Sophia can see inside the model or prove the model obeyed the method.

What your agent does with it

// An agent may propose a skill. It may not publish one.
await sophia.save_skill({
id: 'sophia:resume-session',
title: 'Resume a multi-turn session without re-deriving context',
body_md: '## Trigger\n...',
status: 'stable',          // ← requesting publication
});
// → { error: 'owner_approval_required' }
// Refused BEFORE any write: no row, no vault file, no partial state.
// Drop the status (or pass 'draft') and the same call succeeds as a draft.

// The owner reviews drafts in the owner UI and publishes there — over the
// session-key HTTP route an agent bearer cannot reach.

// Until then the draft is inert. It is not served:
await sophia.load_skill({ id: 'sophia:resume-session' });
// → { error: 'SKILL_NOT_PUBLISHED' }   (no body)
// ...and it is not ranked into any other agent's orient().

The failure is the interesting part: the refusal happens before anything is written, so a rejected publish leaves nothing behind to clean up.

Boundaries

There is no signature or cryptographic attestation on the pack today. The installer re-hashes final installed bytes and the runtime matches the reported receipt to its current release, but the lifecycle signal remains a self-reported, unsigned local receipt. The build says so itself: the first field of every receipt reads “deterministic development receipt; not an attestation.” Signed releases, per-install device keys and a cryptographic verifier remain in flight.

Sophia also does not claim per-skill behavioral adherence. Manual skill loading cannot activate installed-router state, and the lifecycle signal identifies the pack release rather than every instruction the model subsequently followed. Per-session skill loads are not recorded as proof, and behavior that looks consistent with a skill remains observation—not attestation.

This also inherits The Security Model’s local-trust boundary — whoever controls your OS user controls the daemon, its vault, and its database. None of this defends against a person who already has your account.