spec / v1 v1.12.2
41 JSON Schemas served from this path. Each $id equals its URL.
Top-level
-
annotations.schema.jsonAnnotationsCatalog of conventional annotation fields skill-map ships out of the box, written into the `annotations:` block of a sidecar (`<basename>.sm`). Every field is OPTIONAL, a sidecar with an empty `annotations: {}` is valid. Schema is `additionalProperties: true` so users / plugins can add custom keys without coordination; the built-in `unknown-field` analyzer emits a warning on unrecognized keys (typo guard). The curated catalog is the load-bearing fields below, versioning (`version`, `stability`), provenance (`authors`, `license`, `source`, `sourceVersion`), taxonomy (`tags`), docs (`docsUrl`). The activity timestamp lives in the reserved `audit:` block (`audit.lastBumpedAt`), not in `annotations:`. Plugins that want first-class custom keys with their own validation declare `annotationContributions` in their manifest (see Step 9.6.6).
-
api/rest-envelope.schema.jsonRestEnvelopeWrapper shape for REST responses under `/api/*` (Step 14.2). Five variants distinguished by the `kind` discriminator and which payload field is present (`items` for list kinds AND `'annotations.registered'`, `item` for single-resource kinds, `value` for `kind: 'config'` and the action-result kinds). The `/api/scan` and `/api/health` responses are exempt, they carry the underlying `ScanResult` / `IHealthResponse` shape directly. The `/api/graph` response is also exempt, it returns the formatter's native textual output (text/plain or text/markdown). Step 14.5.d adds the required `kindRegistry` field on every payload-bearing list / single / config variant so the UI can render Provider-declared kinds (label, color, icon) without hardcoding visuals; the sibling `providerRegistry` field carries the registered Providers' own identity (label, color, chip visibility) so the UI renders the active-lens dropdown and the per-node provider chip from the real Provider set instead of a hardcoded list. Sentinel kinds (`health`, `scan`, `graph`) stay exempt because they don't carry an envelope payload. Step 9.6 closes the action-result (R7) and `'annotations.registered'` (R7) gaps, both are payload-bearing but carry their own variant shapes (action-result: `value` + `elapsedMs`, no `filters`/`counts`/`kindRegistry`; annotations.registered: `items` + `counts.total`, no `filters`/`kindRegistry`) because they project read-only kernel surfaces orthogonal to the kindRegistry. Step 16 piece 1 (the findings workbench, inspector half) adds three kinds: `findings` rides the list shape with a REQUIRED `counts.dismissedExcluded` / `counts.fixedExcluded` honesty pair (stale rows ride `items` inline with their derived `stale` flag) and a locked per-item finding-row shape, `node.prob-extensions` rides the single shape (`item` = the finders / fixers / standalone launcher catalog), and `job.submitted` rides the action-result shape (`value` + `elapsedMs`, no registries). The change keeps `schemaVersion` at `'1'`, the BFF is greenfield (no released consumers depend on the prior shape), so a versioned migration buys nothing.
-
bump-report.schema.jsonBumpReportReport shape produced by the built-in deterministic `node-bump` Action (Step 9.6.3, Decision #125). Extends `report-base-deterministic.schema.json` (the deterministic counterpart to `report-base.schema.json`, which carries the LLM-only `confidence` + `safety` fields). The `node-bump` Action returns one of three concrete shapes, distinguished by `ok` / `noop` / `reason`: success-with-write (`{ ok: true, version }`), silent-no-op under `force` (`{ ok: true, noop: true }`), or refusal (`{ ok: false, reason: 'fresh' }`).
-
conformance-case.schema.jsonConformanceCaseShape of a single declarative test case under `spec/conformance/cases/<id>.json`. Consumed by language-neutral conformance runners. See `spec/conformance/README.md` for the runner contract.
-
conformance-result.schema.jsonConformanceResultMachine-readable output of `sm conformance run --json`. Aggregates pass / fail totals across the selected scope set plus per-scope and per-case breakdowns. The `elapsedMs` top-level field is the command's own wall-clock (see `cli-contract.md` §Elapsed time).
-
enrich-report.schema.jsonRefreshReportMachine-readable output of `sm enrich <node.path> --json` and `sm enrich --stale --json`. Reports the count of enrichment rows persisted across the targeted node set (universal enrichment layer per `architecture.md` §A.8). The `elapsedMs` top-level field is the command's own wall-clock (see `cli-contract.md` §Elapsed time).
-
enrichments/github.schema.jsonEnrichmentGithubReport produced by `github/enrichment` (Model A provenance verification) for a single node carrying `source` + `sourceVersion` annotations. Deterministic: it does NOT extend `report-base.schema.json` (no LLM, no confidence / safety metacognition). A report schema that extends a schema under the canonical `enrichments/` namespace marks its Action as an enricher: `sm enrich` upserts the validated report into `state_enrichments` keyed by `(node_id, <qualified action id>)`, the mirror of the summaries write-through convention. Stability: experimental.
-
execution-record.schema.jsonExecutionRecordA single row in the execution history (`state_executions`). One record per extension execution (Action or finder Analyzer), whether an external agent processed the job (agent) or the extension ran in-process.
-
extensions/action.schema.jsonExtensionActionManifest shape for an `Action` extension. An action operates on one or more nodes in one of two modes: `deterministic` (code runs in-process, returns a report JSON directly) or `probabilistic` (kernel renders a prompt, a runner executes it against an LLM, the callback closes the job). **Structure-as-truth files**: every Action carries `<action-dir>/report.schema.json` (the JSON Schema for the report, MUST extend `report-base.schema.json`); probabilistic Actions additionally carry `<action-dir>/prompt.md` (the prompt template). An Action whose report schema extends the canonical node-summary schema (under `summaries/`, via `$ref`; today the single universal `summaries/markdown.schema.json`) is a summarizer: recording a `completed` job for it also upserts the validated report into `state_summaries` (see `job-lifecycle.md` §Record). The summarizer signal is the report schema itself; there is no manifest flag. The kernel resolves both by convention; missing or mis-placed files surface as `load-error`. A deterministic Action with a `prompt.md` in its folder is also `load-error` (config inconsistent). **`prob*` prefix convention**: manifest fields that only apply when `mode=probabilistic` start with `prob`; if a deterministic-only field ever appears, it starts with `det`.
-
extensions/analyzer.schema.jsonExtensionAnalyzerManifest shape for an `Analyzer` extension. An analyzer consumes the full graph (nodes + links) after all extractors have run, emits `Issue[]`, and MAY emit view contributions to project findings into the UI. Analyzers are dual-mode: `deterministic` analyzers MUST be byte-for-byte reproducible (same graph in → same issues out; time, random, and network are forbidden) and run synchronously inside `sm check` / `sm scan`; `probabilistic` analyzers (finders) execute only as queued jobs, sharing the Action queue verbatim (`sm jobs submit <extension>`, processed by an external agent via `sm jobs claim` + `sm record`). A probabilistic analyzer has NO `evaluate()` (the processing agent does the reasoning) and instead ships files-by-convention: `<analyzer-dir>/prompt.md` plus `<analyzer-dir>/report.schema.json` extending the canonical findings envelope (`findings/report.schema.json`) via `$ref`; missing either is `invalid-manifest`. Its validated report lands in `state_findings` at record time (never as `Issue[]`), output MAY vary across runs, and it NEVER participates in `sm scan`. Each issue a deterministic analyzer emits is tagged with `analyzer_id = <plugin-id>/<extension-id>` by default (the extension's qualified id, derived from structure); analyzers that need to discriminate sub-types append `:<sub-id>` at emit time. Severity is set per-emit (no manifest-level default).
-
extensions/base.schema.jsonExtensionBaseBase shape common to every extension kind, describing what an extension's MODULE may declare. Kind-specific schemas (`provider`, `extractor`, `analyzer`, `action`, `formatter`, `hook`) extend this via `allOf` and add a kind-specific shape. The fields the loader must know BEFORE importing the module (`version`, `description`, `stability`, `defaultEnabled`) are deliberately NOT here: they live on disk at `<plugin>/<kind-plural>/<id>/extension.json` (`extension-manifest.schema.json`). The enabled decision depends on `stability` / `defaultEnabled`, so reading them out of the module would mean executing code to learn whether that code was allowed to execute at all. A module re-declaring any of the four is rejected by the kind schemas' `unevaluatedProperties: false`. Both `id` and `kind` are likewise derived from the filesystem structure (`<plugin>/<kind-plural>/<id>/index.ts`, where the parent folder dictates the kind and the leaf folder dictates the id), so they are NOT manifest fields either; manifests carrying `id` or `kind` are rejected as `invalid-manifest`. Closed-content enforcement (unknown keys = bug) lives on the kind schemas via `unevaluatedProperties: false`; those see base's evaluated keys through the `allOf` composition.
-
extensions/extension-manifest.schema.jsonExtensionManifestThe DECLARATIVE half of an extension, stored on disk at `<plugin>/<kind-plural>/<id>/extension.json`, next to the extension's `index.{js,mjs,ts}`. It exists so the loader can learn whether an extension is allowed to run WITHOUT importing it: the enabled decision depends on `stability` / `defaultEnabled`, and while those lived in the module you had to execute the code to discover you were not allowed to execute it. Reading JSON is not execution, so the gate now closes before the import. Same structure-as-truth family as an Action's `report.schema.json` and a Provider's `kinds/<kindName>/kind.json`: the file's location IS its binding, `id` and `kind` still come from the folder layout and are not fields here. Required for every extension of an on-disk (drop-in) plugin; a missing, unparseable, or invalid file rejects the plugin as `invalid-manifest`. The extension's module must NOT re-declare these fields (the kind schemas' `unevaluatedProperties: false` rejects them), exactly as it must not re-declare `id` / `kind`. Built-in extensions compiled into the reference CLI do not use this file: they never reach the disk loader, so there is no import to gate, and they keep declaring these fields inline (see `version` below).
-
extensions/extractor.schema.jsonExtensionExtractorManifest shape for an `Extractor` extension. An extractor consumes a parsed node (frontmatter + body) and emits output through three context-supplied callbacks rather than returning a value: `ctx.emitLink(link)` writes to the kernel's `links` table (validated against the global closed enum of link kinds before persistence; per-extractor whitelisting was retired with structure-as-truth, the global enum is the contract), `ctx.enrichNode(partial)` merges author-canonical properties into the kernel's enrichment layer (separate from the author-supplied frontmatter), `ctx.emitContribution(id, payload)` emits per-node view contributions validated against the slot payload schema, and `ctx.store` persists into the plugin's own KV namespace. The runtime method is `extract(ctx) → void`. Extractors run in isolation: they MUST NOT read other nodes, the graph, or the DB. Cross-node reasoning lives in Analyzers. Extractors are deterministic-only: pure code, runs synchronously inside `sm scan`, same input → same output every run. LLM-driven enrichment of a node is an Action concern (queued as a job), not an Extractor concern.
-
extensions/formatter.schema.jsonExtensionFormatterManifest shape for a `Formatter` extension. A formatter serializes the graph (or a filtered subgraph) into a string in a declared format. The format id comes from the formatter's folder name (structure-as-truth, `<plugin>/formatters/<formatId>/index.ts`), it is NOT a manifest field. Invoked by `sm graph --format <formatId>` and `sm export`. Formatters are deterministic-only, they sit at the graph-to-string boundary and their output MUST be byte-deterministic for the same input graph (the snapshot-test suite relies on this). The `mode` field MUST NOT appear in formatter manifests. Probabilistic narrators of the graph are a valid product but they live in jobs and emit Findings, not in formatters. All formatters accept the `--filter` expression; opting out is no longer supported.
-
extensions/hook.schema.jsonExtensionHookManifest shape for a `Hook` extension. Subscribes declaratively to a curated set of kernel lifecycle events. **Hooks are deterministic-only** since the structure-as-truth refactor: the `mode` field was removed; LLM-dependent lifecycle behaviour is modeled as a deterministic hook that enqueues a probabilistic Action via `ctx.queue('<plugin>/<action>', payload)`. Hooks react to events; they cannot block or alter the main pipeline. The set of hookable triggers is intentionally small, nine events out of the full job-events catalog. Seven are pipeline-driven (emitted from inside `runScan` or the record path); two (`boot`, `shutdown`) are CLI-process-driven (emitted by the driving binary before / after the verb runs, fire-and-forget so `process.exit` is never blocked). Other events (per-node `scan.progress`, `run.*`, internal job lifecycle) are deliberately not hookable: too verbose for a reactive surface, or covered elsewhere. Declaring a trigger outside the hookable set yields `invalid-manifest` at load time.
-
extensions/provider-kind.schema.jsonProviderKindMetadataPer-kind UI metadata written as `<plugin>/kinds/<kindName>/kind.json`. Lives next to the kind's frontmatter `schema.json` under the kind folder; together they are the structure-as-truth replacement for the old `kinds` map inside the Provider manifest. Reaches the UI via the `kindRegistry` field embedded in REST envelopes (`api/rest-envelope.schema.json`). The kind name is the folder name; it is NOT repeated as a field here.
-
extensions/provider.schema.jsonExtensionProviderManifest shape for a `Provider` extension. A Provider declares its own universe: the platform it recognises (Claude Code, Codex, Antigravity, Obsidian vault, generic MD), the catalog of node `kind`s it emits, and the per-kind frontmatter schema each kind follows. **Structure-as-truth**: exactly one Provider lives in each plugin that carries one, declared as `<plugin>/provider.ts`. The kinds catalog lives as folders under `<plugin>/kinds/<kindName>/` and the loader discovers each entry by walking that directory; the manifest itself NO LONGER carries a `kinds` map. Each kind folder MUST contain `schema.json` (the kind's frontmatter JSON Schema, extending `frontmatter/base.schema.json` via `allOf` + `$ref`) and `kind.json` (UI metadata under `{ ui: {...} }`). The kernel resolves these at boot time and registers each schema with AJV for scan-time validation. Exactly zero or one Provider MUST match any given file; multiple matches → `provider-ambiguous` issue, file unclassified. **`roots` is enforcement-grade**: a Provider declaring `roots` only receives files matching at least one glob; a Provider without `roots` acts as a fallback for files unmatched by any other Provider's roots. Providers are deterministic-only, they sit at the filesystem boundary and run during boot; probabilistic classification would make boot slow, costly, and non-reproducible. The `mode` field MUST NOT appear in Provider manifests. If you need LLM-assisted classification, write a probabilistic Action that runs as a queued job and writes back through the enrichment layer; Extractors are deterministic-only and Providers stay on the deterministic boot path. Distinct from the **hexagonal-architecture** 'adapter' (`StoragePort.adapter`, `FilesystemPort.adapter`, etc.), which is an internal driven-adapter implementing a port, Providers live in the extension surface, hexagonal adapters live in `src/kernel/adapters/`.
-
findings/report.schema.jsonFindingsReportCanonical findings envelope: the report shape every probabilistic Analyzer records. A probabilistic Analyzer's `report.schema.json` MUST extend this schema via `$ref` (typically inside `allOf`); the kernel enforces the reference at load time (`invalid-manifest` otherwise) and `sm record` writes the validated `findings` array through to `state_findings` (see `job-lifecycle.md` §Record). Extends `report-base.schema.json`, so every findings report also carries the model's `confidence` and the `safety` block. The envelope and the per-finding item are deliberately open (no `additionalProperties: false`): a finder's own schema MAY narrow `type` to an enum of its categories and MAY add evidence fields per finding. Stability: experimental.
-
history-stats.schema.jsonHistoryStatsMachine-readable output of `sm history stats --json`. Aggregates over `state_executions` within a time window. camelCase keys throughout. The `elapsedMs` top-level field is the command's own wall-clock (see `cli-contract.md` §Elapsed time), distinct from `totals.durationMsTotal`, which is the sum of every execution record's duration.
-
input-types.schema.jsonInputTypesClosed catalog of input-types for plugin settings. The plugin author declares each user-configurable setting in the manifest's `settings` map by picking an `input-type` from this catalog; the kernel knows the schema for each type, the UI ships a generated form per type, and the CLI's `sm plugins config <id>` command exposes the same surface. Plugin authors NEVER write JSON Schema for settings, they pick a type by name and supply per-type parameters (label, default, min/max, options for enums, etc.). Closed catalog by design: every new input-type requires spec + UI form + CLI prompter + tests. Versioned via the manifest field `catalogCompat` (semver against the catalog as a whole). For the rationale and open issues, see ROADMAP.md §UI contribution system.
-
issue.schema.jsonIssueDeterministic finding emitted by a analyzer when evaluating the graph. Not to be confused with `Finding`, which is probabilistic (LLM-produced).
-
job.schema.jsonJobRow in `state_jobs`. Non-terminal state until it reaches `completed` or `failed`, at which point an `ExecutionRecord` is also written. The queue is kind-agnostic: `extensionId` names a probabilistic Action OR a probabilistic Analyzer (the columns were renamed from `action_id` / `action_version` when Analyzers joined the queue; `state_executions.extension_id` set the naming precedent).
-
link.schema.jsonLinkDirected relation between two nodes, produced by one or more extractors during a scan.
-
map-view.schema.jsonMapViewOne named, shareable map view: a human-curated projection of the workspace map (visibility overrides plus pinned node positions, later visual groups), persisted as `<scopeRoot>/.skill-map/views/<slug>.json` and committed to the shared repository. The slug is NOT stored inside the document; the filename is the identity and MUST match the Slug rule in `$defs.Slug`. Consumers MUST apply a view tolerating references that no longer resolve to scanned nodes (dead override keys, pins, or group members are ignored on apply and surfaced as a count, never rewritten server-side). Full contract: map-views.md.
-
node.schema.jsonNodeA single entity in the graph. Typically a file on disk (a markdown skill, an agent, a TOML sub-agent definition, a plain-markdown note), but MAY also be a **virtual / derived** entity that lives only in memory and is reconstructed from one or more source files on every scan (e.g. an MCP server node derived from `settings.json` / `mcp.json` / `config.toml`). Virtual nodes carry `virtual: true` and use a synthetic `path` scheme (`mcp://<name>`, etc.). The `kind` is whatever the classifying Provider declares, open by design; the **built-in Claude Provider** emits `skill` / `agent` / `command` / `markdown` today, but external Providers (Cursor, Obsidian, …) MAY emit their own. Format-named kinds (`markdown`, future `toml`, future `json`) are reserved for the generic fallback only, when a file matches a specific role (agent / command / skill) that classification prevails over format naming.
-
plugins-doctor.schema.jsonPluginsDoctorReportMachine-readable output of `sm plugins doctor --json`. Aggregates per-status counts across built-in and drop-in plugins plus the structured issue / warning lists the human renderer produces, and the runtime contribution rejections persisted by the last scan. The `elapsedMs` top-level field is the command's own wall-clock (see `cli-contract.md` §Elapsed time).
-
plugins-registry.schema.jsonPluginsRegistryTwo shapes in one file: (1) the per-plugin manifest that authors ship as `plugin.json` (see `$defs/PluginManifest`); (2) the aggregate registry the implementation produces on disk (`<cwd>/.skill-map/plugins.json`), which lists all discovered plugins with their compat status. Both shapes are normative. camelCase keys throughout.
-
project-config.schema.jsonProjectConfigShape of `.skill-map/settings.json` (and its `.skill-map/settings.local.json` partner) inside a scope. Loaded by the layered config hierarchy (library defaults → user → user-local → project → project-local → env/flags) and deep-merged per key. All fields optional; defaults apply when absent. camelCase keys throughout, consistent with the rest of the spec.
-
report-base-deterministic.schema.jsonReportBaseDeterministicUniversal base for deterministic Action reports. Every deterministic Action's report MUST extend this base via `allOf` + `$ref`. Symmetric with `report-base.schema.json` (the probabilistic / LLM base, which carries `confidence` + `safety`); deterministic vs probabilistic is the orthogonal axis declared by the Action manifest's `mode` field. Fields: `ok` (boolean, did the Action complete its logical work?), plus action-specific keys via `additionalProperties: true`. Action-specific shapes (e.g. bump's `version` / `noop` / `reason`) ride on the open extension.
-
report-base.schema.jsonReportBaseBase shape for any probabilistic report, whatever extension kind produced it (summarizer Actions, finder Analyzers, plugin-shipped probabilistic extensions). The canonical envelopes under `summaries/` and `findings/` both extend this. Kernel validates the `confidence` and `safety` fields regardless of extension-specific additions.
-
scan-result.schema.jsonScanResultCanonical output of `sm scan --json` (and the data shape sent over WebSocket scan events). Self-describing and versioned; consumers MUST check `schemaVersion` before parsing.
-
serve-info.schema.jsonServeInfoRuntime discovery file written by `sm serve` at `<scopeRoot>/.skill-map/serve.json` while the server is up, and deleted on shutdown. It publishes the RESOLVED listening address (after config layering and flag overrides) plus a per-session ingest token, so short-lived local processes (the activity bridge, see `spec/provider-activity.md`) can find and authenticate against the project's running server without re-deriving config. It is a runtime artifact (lockfile-like), NOT user config: gitignored, never committed, overwritten on every boot. A hard kill can leave a stale copy behind, so readers MUST fail open (treat a refused connection as 'no server' and stay silent). Written atomically (temp file + rename) so a reader never observes a half-written document.
-
session-recording.schema.jsonSessionRecordingOne recorded runtime session: the resolved, content-free activity frames the BFF journaled to `<scopeRoot>/.skill-map/sessions/<file>.json` while `sm serve` was up (see `provider-activity.md` section Session journal). This is the SINGLE session-recording format: the on-disk journal today, the export/import envelope later. Frames carry the SAME wire shapes as the `node.activity` / `agent.spawn` WS events, minus the boot-scoped derived fields (`stats`, `pairCount`), and are content-free by construction: no prompts, no tool arguments, no file contents, no conversation halves (the frame shapes here close with `additionalProperties: false`, so a content field cannot ride even by accident). Machine output per the Storage rule (`architecture.md` section Storage rule): regenerable by re-running sessions, disposable, gitignored, operator-deletable. Readers AJV-validate every file against this schema and SKIP off-shape files silently.
-
sidecar.schema.jsonSidecarRoot shape of a co-located YAML sidecar (`<basename>.sm` next to `<basename>.md`). The `.sm` file IS the annotations file, every key under it is, conceptually, an annotation on the node. The YAML root organizes those annotations into structural blocks: `identity` (anchor + drift-detection hashes), `annotations` (the curated catalog of conventional fields), `audit` (timestamps), `settings` (reserved), and arbitrary `<plugin-id>:` namespaces for plugin-contributed data. Vendor file (`<basename>.md`) stays untouched. Schema is `additionalProperties: true` so plugins can add namespaces without coordination; the built-in `unknown-field` analyzer warns on truly unrecognized root keys (typo guard). Format is YAML, comments via `#`, multiline strings via `|` / `>`, permissive types per the YAML 1.2 spec. See `architecture.md` §Annotation system and ROADMAP §Step 9.6 for the design rationale.
-
signal.schema.jsonSignalIntermediate Representation (IR) emitted by extractors during a scan. A Signal is a *candidate* detection: zero, one, or many interpretations of the same piece of source text or structured data. The kernel's resolver phase consumes `Signal[]` and produces final `Link[]` by selecting a winning candidate per Signal (or rejecting all and emitting none) using the active Provider's resolution rules. Opt-in for plugin authors: an extractor MAY emit `Signal`s via `ctx.emitSignal()` when the detection carries genuine ambiguity (multiple plausible kinds, multiple plausible targets, byte-range awareness for collision detection), OR continue calling `ctx.emitLink()` directly when its detection is unambiguous. The two paths coexist; resolved Link rows look identical regardless of origin. Stability: experimental.
-
skill-actions/report.schema.jsonSkillActionReportCanonical report contract for every skill action (`skill-actions.md`). Skills carry no `report.schema.json` of their own, so the record path resolves a `skill:`-prefixed job's report against this ONE schema, as a constant, without consulting the catalog (an uninstalled skill must not orphan its running job). Extends `report-base.schema.json` (the kernel-validated `confidence` + `safety` pair) and adds a single required `summary`. Additional properties stay OPEN deliberately: a skill may ask its agent for extra structured fields and they persist with the execution (`state_executions.report_json`) without schema coordination; no namespace envelope applies, so record writes the execution row only (no summaries, findings, or tags write-through).
-
tags/markdown.schema.jsonNodeTagsReportCanonical tagger-report shape (2026-07-21, the taxonomy leg of the summarizer-split direction; reframed 2026-07-25). An Action whose report schema `$ref`s a schema under the `tags/` namespace is a TAGGER: `sm record` detects that reference (mirror of the `summaries/` detection signal) and surfaces the report's `tags[]` as a PROPOSAL on the completion event (`job-events.md`, `tagsProposed`), writing nothing. Tags are human curation (`architecture.md` §Storage rule), so the operator reviews the proposal in the ordinary tags editor and saves it through the usual consent-gated `.sm` write. See `job-lifecycle.md` §Tags proposal.
-
user-settings.schema.jsonUserSettingsPer-user, per-machine settings file persisted at `~/.skill-map/settings.json`. Holds the small set of preferences that genuinely belong to the operator (not to a project) plus the bookkeeping each one needs. The file is NOT part of the project config layer system (no merge, no PROJECT_LOCAL_ONLY_KEYS interaction); it is read directly by the few modules that own a user-scope feature. See `spec/cli-contract.md` §Scope is always project-local for the broader principle: skill-map never reads `$HOME` by default, this file is the narrow, documented exception. There is intentionally no `.local` partner; values here are already per-machine, so the project / project-local split would have no meaning.
-
view-slots.schema.jsonViewSlotsClosed catalog of view slots. A view slot is a kernel-published handle that names a visual surface in the UI, fixes the renderer that draws there, and fixes the payload shape the plugin emits. The plugin author picks ONE slot per view contribution; the kernel validates `ctx.emitContribution(id, payload)` against that slot's payload schema in `$defs.payloads`. There is no separate notion of a 'contract', the slot IS the contract. Closed catalog by design: every new slot requires a spec change + UI renderer mount + scaffolder support + conformance fixtures + tests. Compounds catalog evolution cost; see ROADMAP.md §UI contribution system → 'Known limitations carried forward'. Slots are versioned via the manifest field `catalogCompat` (semver against the catalog as a whole), not per-slot.
Frontmatter
-
frontmatter/base.schema.jsonFrontmatterBaseUniversal frontmatter shape every Provider's per-kind schema extends via `allOf` + `$ref` to this `$id`. It DEFINES the two cross-vendor fields, `name` and `description`, but deliberately does NOT require them: whether either is mandatory is a per-kind decision. The kinds whose vendor mandates them add `required` on their own extension (Claude agent, OpenAI Codex agent, the Agent Skills skill); the generic `markdown` fallback and Claude skill/command leave them optional, because no normative Markdown standard mandates frontmatter fields and Claude's merged skill/command contract treats `name` (defaults to the directory/file name) and `description` (defaults to the first paragraph) as optional. `description` is the field every format carries and `name` is universal among formats with explicit identifiers, so both live here as shared definitions with `minLength: 1` (a present value cannot be empty). Everything else is vendor idiosyncrasy and lives on the per-vendor per-kind schema, NOT here. (Taxonomy `tags`, for example, is a skill-map concept with no vendor frontmatter analog, so it lives in the `.sm` sidecar `annotations.tags`, not here.) `additionalProperties: true` is intentional: skill-map AGGREGATES vendor specs, it does not curate them. Vendor-specific fields (`tools`, `allowedTools`, `model`, etc.) flow through validation silently because the per-kind extension declares them.
Summaries
-
summaries/markdown.schema.jsonSummaryMarkdownReport produced by `ai-summarizer-action` for a single node of ANY kind. `markdown` names the body format the summarizer reads (every node body is markdown prose, including frontmatter-field bodies like codex TOML `developer_instructions`), NOT the node kind: the summarizer is universal and `state_summaries.kind` mirrors the target node's own kind. This is the single canonical node-summary shape; a report schema that extends it via `$ref` (the `summaries/` namespace is the detection signal) marks its Action as a summarizer, see `job-lifecycle.md` §Record. Extends `report-base.schema.json`. Stability: experimental.
Prose contracts
-
README.mdREADMEOverview of the spec and what it defines.
-
versioning.mdVersioningEvolution policy, stability tags, deprecation window.
-
CHANGELOG.mdChangelogNormative history of spec changes.
-
architecture.mdArchitectureHexagonal ports & adapters, 6 extension kinds.
-
cli-contract.mdCLI contractVerbs, flags, exit codes, JSON introspection.
-
job-lifecycle.mdJob lifecycleJob state machine, atomic claim, TTL, reap.
-
job-events.mdJob eventsCanonical event stream emitted during execution.
-
prompt-preamble.mdPrompt preambleVerbatim injection-mitigation text prepended to every job.
-
db-schema.mdDB schemaZoned table catalog, naming conventions, migrations.
-
plugin-kv-api.mdPlugin KV APIctx.store KV persistence contract for plugins.
-
mcp-server.mdMCP serverQueue + findings tools exposed over the Model Context Protocol.
-
provider-activity.mdProvider activityReal-time activity capability: hooks bridge, ingest, WS frames.
-
telemetry.mdTelemetryOpt-in usage/error telemetry: toggles, consent, anonymity.
-
view-slots.mdView slotsClosed slot + renderer catalog for plugin UI contributions.
-
map-views.mdMap viewsNamed, committed, shareable map curation: overrides, pins, reserved groups.
-
input-types.mdInput typesClosed catalog of extension input types.
-
skill-actions.mdSkill actionsOperator-installed SKILL.md skills run as per-node jobs.
-
interfaces/security-scanner.mdSecurity scanner interfaceConvention for third-party security scanners.