Architecture¶
AbstractSkill is a passive contract library: it parses, validates, hashes, discovers, composes, and classifies skills. It executes nothing and imposes no runtime limits — hosts such as AbstractGateway consume its contracts and enforce policy. It uses no network. See the API reference for the public surface and the trust model for verdict semantics.
Components¶
Arrows point from a module to the modules it uses. Hosts such as AbstractGateway call the public API; they never reach into the package directory itself.
graph TD
subgraph pkg[abstractskill package]
parser[parser.py<br/>parse_skill_md]
validation[validation.py<br/>name / description / compatibility]
models[models.py<br/>SkillMetadata / SkillDocument]
hash[hash.py<br/>content_hash]
loader[loader.py<br/>FilesystemSkillLoader]
tree[tree.py<br/>hash_skill_tree / inspect_skill_dir]
trust[trust.py<br/>TrustRegistry / evaluate_trust]
selection[selection.py<br/>select_skills_for_context]
prompt[prompt.py<br/>format_available_skills_xml]
policy[policy.py<br/>effective_tools]
demand[demand.py<br/>derive_demand]
catalog[catalog.py<br/>load_catalog]
bundled[bundled.py<br/>bundled_registry_dir / seed_registry]
registry[(registry/<br/>14 skills, licenses,<br/>4 yaml files)]
end
parser --> validation
parser --> models
parser --> hash
loader --> parser
loader --> models
tree --> validation
trust --> validation
catalog --> validation
selection --> loader
selection --> tree
selection --> trust
demand --> selection
policy --> models
prompt --> models
bundled --> tree
bundled --> registry
host[Host, e.g. AbstractGateway<br/>seeds a shelf at start,<br/>skill picker, activation]
shelf[(host-owned shelf<br/>seeded copy of registry/)]
host --> bundled
bundled -->|seed_registry| shelf
host --> selection
selection -->|reads| shelf
host --> prompt
host --> loader
host --> trust
Data flow: activating skills through the trust gate¶
select_skills_for_context runs the whole pipeline in one call, so the order
cannot be skipped. The host then renders the active skills into the prompt.
flowchart LR
A[skill names<br/>from host config] --> B[load SKILL.md<br/>from shelf roots]
B --> C[inspect_skill_dir<br/>tree_hash + has_scripts]
C --> D[derive sources<br/>from registry]
D --> E[evaluate_trust<br/>per candidate,<br/>worst verdict wins]
E -->|blocked| F[blocked: never active]
E -->|requires_review| G{operator enabled<br/>name or name@hash?}
G -->|no| H[held]
G -->|yes| I[active]
E -->|attachable| I
I --> J[format_available_skills_xml<br/>with activation_descriptions]
I -.optional.-> K[effective_tools<br/>grant ∩ declared]
Trust verdict precedence¶
flowchart TD
S[skill: tree_hash, name, source, has_scripts] --> ADV{active blocking<br/>advisory match?}
ADV -->|critical/high or hash match| BLOCK[BLOCKED<br/>never attach]
ADV -->|no| VAL{validation<br/>for this hash?}
VAL -->|no| UNV[UNVERIFIED<br/>requires_review]
VAL -->|yes| LVL[level = strongest record]
LVL --> REV{low/medium advisory<br/>OR has_scripts?}
REV -->|yes| RR[requires_review]
REV -->|no| ATT[attachable]
Key design decisions¶
- Hash = bytes, parse = meaning.
content_hash(one document) andhash_skill_tree(whole folder, length-prefixed injective manifest) are byte-exact. Trust binds to the tree hash; any byte change voids a validation and a post-approval tamper is detected. See trust model. - Progressive disclosure.
discover()reads only frontmatter;load()/read_skill_resourcefetch bodies and resources on demand with size bounds. - Grant is the only tool authority.
effective_toolsnarrows below the operator grant, never widens; absence ofallowed-toolsimplies nothing. - Fail closed. Unknown, script-bearing, or low/medium-advisory skills
require review; only a validated, clean skill is
attachable. - Passive by design. No execution, no scheduling, no runtime caps — keeping the framework's agency in the hosts, not the contract library.
Registries (data, not code)¶
The registry ships in the wheel as package data (abstractskill/registry/).
abstractskill.bundled locates it and seeds host-owned copies;
catalog.yaml carries the bundle version (dotted numbers, for example
2026.09.25), which increases whenever bundled content changes.
Seeding a host shelf¶
seed_registry(dest) decides per item (each skill folder by tree hash, each
file by sha256), holding an exclusive lock on <dest>/.seed.lock for the
whole call. <dest>/.seeded.json records the hash of every item a seed
wrote and the bundle version.
flowchart TD
S[bundled item] --> SYM{symlink at dest?}
SYM -->|yes| KS[kept_symlink]
SYM -->|no| EX{exists at dest?}
EX -->|no| ADD[added]
EX -->|yes| RD{hashable?}
RD -->|no| KU[kept_unreadable]
RD -->|yes| SAME{identical<br/>to bundle?}
SAME -->|yes| UN[unchanged<br/>recorded as seeded]
SAME -->|no| MAN{manifest<br/>present?}
MAN -->|no| KP[kept_unknown_provenance]
MAN -->|yes| REC{recorded<br/>in manifest?}
REC -->|no| KF[kept_foreign]
REC -->|yes| PRIS{identical to what<br/>the last seed wrote?}
PRIS -->|no| KM[kept_user_modified]
PRIS -->|yes| OLD{bundle older<br/>than last seed?}
OLD -->|yes| KN[kept_newer]
OLD -->|no| UPD[updated]
Items are written through a staging folder and renamed into place; if a
folder swap fails, the previous copy is restored. Items a previous seed
wrote that the bundle no longer contains are reported as not_in_bundle
and never deleted. A second seed with the same bundle writes nothing. The
manifest is rewritten only when it changes.
AbstractGateway seeds <data dir>/skills/registry when it starts and serves
that copy unless the operator sets its skills.shelf setting to another
folder.
Registry files¶
src/abstractskill/registry/skills/<name>/— the vendored curated shelf (byte-verbatim; first-party + catalog-vendored).src/abstractskill/registry/catalog.yaml— the curated vendoring catalog: reviewed entries pinned to upstream commits + whole-tree hashes; the ONLY admissible source list forscripts/vendor_skill.py(validated byabstractskill.catalog).src/abstractskill/registry/validations.yaml— trust attestations bound to tree hashes (regenerated byscripts/refresh_shelf.py; catalog-vendored skills derive their policy from the catalog entry).src/abstractskill/registry/advisories.yaml— specific do-not-use skills (four mandated fields; empty at v1 until an audit or feed names a real one).src/abstractskill/registry/licenses/— upstream licenses of vendored third-party skills, kept outside the skill trees so the tree hash covers only upstream bytes.src/abstractskill/registry/guidance.yaml— category-level risk notices (never block a specific skill).
For the design history and planned trust work, see the backlog overview.