11. The extension contract, as core defines it
Core discovers extension manifests without knowing any extension in advance, and contains no
extension ID. The contract is version 1.1. Each extension has a UTF-8 JSON manifest named
familiar-extension.json at the root of its package. extension-manifest.schema.json is the
authority for machine checks, and extension-contract.md is its prose companion.
11.1 Manifest shape
Section titled “11.1 Manifest shape”Required fields: contract (always 1.1), id, display_name, version (semver), core (a
compatible version range), type (workflow | specialty | enforcement | integration),
namespace, contributions, capabilities, unavailable_behavior, package. Optional fields:
compatible_with, conflicts.
contributions requires four arrays: routes, skills, agents and state_surfaces. It allows two
more arrays for project-lifecycle pieces, checkin_fragments and track_presets. Each is a list of
file paths relative to the package. A route is an object with id, skill and select_when. A state
surface is the four-field object in §6.5.
capabilities requires required and optional arrays. unavailable_behavior is hide, degrade
or report-unavailable. It applies when an optional capability is missing. package requires
update_source.
There is no separate list of extensions. An extension exists exactly when its skill folder is on disk
in an agent’s skill folder, with its own familiar-extension.json manifest. A route’s host_status
is therefore worked out when it is read. The agent compares the manifest’s required and optional
capabilities with the table in .familiar/installed.json. The result is not stored anywhere.
11.2 What validation enforces
Section titled “11.2 What validation enforces”Nothing checks an extension when you add it. There is no installer or validator at run time (§12). Adding an extension is a plain folder merge.
If you build an extension from familiar-template, work through the template’s
CONTRACT-CHECKLIST.md, and run its check_extension.py where the template includes it. Core runs
no check on it.
The agent evaluates capabilities for each agent and records the result under host_status. If a
required capability is missing, the extension is unavailable on that agent. If an optional
capability is missing, hide, degrade or report-unavailable applies. The bootstrap checks this
status before it selects an extension route.
Core route IDs are reserved. The order in the registry decides only the display order. It never lets one route override another.
11.3 Namespacing and materialization
Section titled “11.3 Namespacing and materialization”A plugin-scoped subagent <namespace>:<agent> appears in a project as <namespace>-<agent>. No tool
renames it when you add the extension. Each extension’s folder ships with the names already in this
form:
- Subagent files are named
<namespace>-<agent>in every agent’s subagent folder. - Their frontmatter
name:matches. - The skills call them by that name.
11.4 Layout and removal
Section titled “11.4 Layout and removal”An extension’s folder holds its package at .familiar/extensions/<id>/package/. Its skills and
subagents are already in each agent’s own folders. To add it, merge that folder onto the project
root. No list records this. The files on disk and the package’s own manifest are the record.
.familiar/installed.json still records the capability state for each agent.
To remove an extension, delete the paths that its _about.md lists. Nothing checks whether you
edited one of those files, so look before you delete.