Skill model

A skill is a program, not a markdown file: <leaf>.cue is a typed model with its own logic and validation, and SKILL.md is rendered from it.

The model carries what prose cannot: typed routing and pipeline edges that must agree across skills, entry roles with their registers, a permission manifest, interface bindings for every program the skill calls, a smoke declaration, title states, the changelog. Every load validates all of it, so a wrong role, a route to nowhere, an undeclared tool or a version without its changelog row does not render. The file sits in the leaf dir, named after it (skills/quality/railroad-review/railroad-review.cue); payloads/<entry-id>.md holds template bodies. SKILL.md renders from both at deploy, never committed; only repo-scoped values wait for invocation (Interfaces and environment). Layout: Skills directory.

Never

A SKILL.md, title.json, enforce.json, smoke.json, answer.md or env.json in a skill dir fails make skills-check.

Everyday use

TaskDo
Change a skillEdit <leaf>.cue or a payload as text; make skill-render SKILL=<name>.
Bump a versionPrepend a changelog row; set frontmatter version to match.
Inspect the modelmake skill-export SKILL=<name> (JSON).
List the atomsmake skill-atoms SKILL=<name> (skillmodel atoms [--json] <dir>): per atom kind, source, readers by entry and role, query names.
Adopt a third-party skillmake skill-import DIR=<dir> once; Routing and Model prose to typed blocks.
Check everythingmake skills-check, part of make audit.

skill-render loads, validates and checks the model; re-saves <leaf>.cue canonically (CUE formatter, fixed field order); writes payloads, deletes orphans; stores the deployed body's token count on the newest changelog row. A failed check writes nothing. Root passes follow per-dir saves.

skills-check scopeFails on
Each skill dirNon-canonical <leaf>.cue; invalid model; orphaned payload; leftover skill.cue (former model file name; the line names the renaming git mv), atoms.json, changelog.json or generated file; SKILL.md without <leaf>.cue.
Across the rootThe Across skills layer.

File shape

Top-level fields of <leaf>.cue. The schema (internal/skillgraph/schema.cue) refuses a model missing a required one.

FieldRequiredMeaning
changelogyes[{version, date, text | changes, tokens?}], newest first, versions strictly descending (checked); skillmodel renumber repairs, skillcue merge driver merges two branches' bumps.
frontmatteryes[{key, value}], ordered; value: raw text after key:. format ratchet: 2 caps, role order; 3 typed fields, link tokens, model-checked links and titles.
preamblenoH1 and intro, verbatim markdown.
sectionsno[{title, kind?, pred?, replaces? | between?, blocks}]; blocks: prose, heading, entry, args, routing, model.
notenoModel note.
tierno"annotations" | "strict"; strict: typed atoms, z3 battery.
atomsno{<id>: {sort, kind?, members?, source?, aliases?}}, skill-local (Formal clauses).
exclusiveno[[atom ids]], mutually exclusive.
enforceno{anchors, checks, gate, judgment}: runtime enforcement → enforce.json.
requiresno[<component id>] the skill cannot run without.
envno{<KEY>: {interface, required?}}: called capabilities, one catalog interface each.
smokeyes, by skills-check{skip} | {invoke, expect, env?, fixture?, mode?, model?, effort?, limits?} (Smoke block).
titleno{states: {<TOKEN>: {meaning, entry}}}: session-title states.
dockerno"always" | "never": where it runs; absent: agnostic.
enableDelegationno"always": spawned only, never root; "never": root only.
enforcePermissionsno[acceptEdits | planMode] or ["bypass"]: owned modes.
answeredByno"hook": the skillhelp hook answers (Deploy).
Shared fileHolds
queries/*.json{name?, assume, check? | reach?, bound?, expect}: strict tier's test suite; a query read keeps an atom alive.
skills/vocabulary.cueShared atoms, exclusive groups.
skills/interfaces.cueInterface catalog, providers.

Frontmatter and changelog

From skills/smart/qq/qq.cue:

changelog: [{
  date:    "2026-10-02"
  version: "1.0.4"
  text:    "Dropped the help-only Eval section — --help shows the smoke and eval declared beside the skill."
  tokens:  809
}, {
  date:    "2026-09-28"
  version: "1.0.3"
  text:    "Routing and Model are typed blocks in skill.cue; the Pipeline line renders from declared edges"
  tokens:  809
}]
frontmatter: [{
  key:   "name"
  value: " qq"
}, {
  key:   "version"
  value: " 1.0.4"
}, {
  key:   "format"
  value: " 2"
}, {
  key:   "allowed-tools"
  value: " Glob, Grep, Read"
}]

Sections and entries

Section kind: context, phase or route. A route section carries pred (when it applies) and exactly one anchor: replaces: <ids> (entries it substitutes) or between: <after>..<before> (splice point).

Entry fieldRequiredMeaning
idyesSKILL-<NAME>-<TOPIC>-NNN[a-z]?
roleyesdef, rule, quality, step, spawn, substep, verify, stop or payload.
titleyes1–3 words.
statementnoProse, one line.
channel, customnoEnforcement channel (gate, hook, prose, schema, script); custom tags (hook:read-gate).
fieldsnoFormat 3 only: {script?, schema?, ref?, flags?, forwards?, reads?, returns?, mints?, timeout?} (Fields).
itemsnoBullets, fences under the statement.
needs, unlessnoComponent ids: entry drops when the render's set misses one / holds one.
pred, postnoFormal clauses.
parentSpawn, payload, notenoSubstep's spawn; payload entry's fence, body in payloads/<entry-id>.md; model note.

Flow entry with clauses, skills/verdict/investigate/investigate.cue:

sections: [{
  title: "2. Run"
  blocks: [{
    entry: {
      id: "SKILL-INVESTIGATE-RUN-004b"
      post: ["claims_ruled"]
      pred:      "claims_deduped"
      role:      "substep"
      statement: "RULE each claim `holds | refuted | needs-measurement` in Stage B, watching full funnel."
      title:     "Stage B Rulings"
    }
  }]
}]

Route section, skills/feature/fdesign/fdesign.cue, blocks elided:

{
  title: "Nightly route"
  blocks: […]
  kind:     "route"
  pred:     "mode = nightly"
  replaces: "GATE-001 + CODE-005 + CODE-009 + HANDOFF-001"
}

args block {rows: [{name, form?, default?, effect}]} → the ## Args table:

args: {
  rows: [{
    name:    "topic"
    default: "default slice"
    effect:  "context slice(s) injected; `--no-context` skips all — [Topic use](#skill-qq-context-001)"
    form:    "`topic:<name>`, repeatable"
  }]
}

Routing and model blocks

BlockSits inRenders
routingFirst block of Routing section.Purpose, Disambiguation table, Preconditions, Pipeline line. Fields {purpose, routes: [{for, use, needs?}], preconditions: [...], pipeline}, all but needs required.
modelOnly block of Model section.- Suggested: … - Tested unviable: bullets. Fields {tier, effort, reason, unviable?, routes?, notes?}; tier small, mid-tier, frontier; effort low … max.
routing: {
  pipeline: {
    standalone: true
  }
  preconditions: ["one question in the invocation"]
  purpose: "answer a single question fast and cheaply."
  routes: [{
    for: "a subject to talk through"
    use: "/discuss"
  }, {
    for: "an answer that needs sources and citations"
    use: "/research"
  }]
}

model: {
  effort: "low"
  reason: "single lookup, short answer"
  tier:   "mid-tier"
}

Title block

Session-title states of the skill's approval gates. Deploy renders them to title.json beside SKILL.md, not into it.

RuleDetail
Gate entryEach state names the step or stop presenting its gate. Its statement (below format 3 also a param bullet) runs $SKILL_<SKILL>_TITLE_SET title set <TOKEN> via an env variable bound to title-set@1; model-checked, literal program refused.
ResumeA step resuming after the gate runs … title set WIP.
ProofDeployed copy carries the filled program. The title hook accepts any program word before title set <TOKEN>, takes the printed title: set <TOKEN> line as proof, infers nothing from other tool calls.
RenameAt the turn's first confirmed set call; later sets: once at turn end, with the last.
No valueNo title-set@1 value: no title.json, no states.
title: {
  states: {
    "CODE-READY": {
      entry:   "SKILL-FDESIGN-CODE-005"
      meaning: "code gate presented — approve the Changes or request a change"
    }
    "DESIGN-READY": {
      entry:   "SKILL-FDESIGN-GATE-001"
      meaning: "design gate presented — approve or revise the design"
    }
  }
}

Smoke block

The skill's smoke test, never rendered into any file or deployed. The smoke runner loads the model and resolves every default in memory.

FieldRequiredMeaning
skipfor a skip{skip: "<reason>"} replaces the run.
invokeyesText after /<leaf>.
expectyes{anchors: [...], minAnchors?, minChecks?} naming the skill's own enforce anchors, or {check?: [argv]}.
envnoRepo-scoped values the run sets, every required one.
fixtureno{tree?, patch?} inside the skill's smoke/.
modenonightly (default); interactive needs anchors.
model, effortnoDefaults sonnet, low.
limitsno{minutes?, usd?}, defaults 15 and 3.

skills/feature/fimplement/fimplement.cue, check list shortened; fdesign expects {anchors: [...], minChecks: 0.4} instead:

smoke: {
  env: {
    CHECK: "go test ./internal/smoke/..."
  }
  expect: {
    check: ["go", "test", "internal/smoke/greeting.go", "internal/smoke/greeting_test.go", …]
  }
  fixture: {
    tree: "tree"
  }
  invoke: "fimplement-smoke.md"
  limits: {
    minutes: 45
    usd:     8.0
  }
}

Pipeline edges

pipeline: {standalone?, edges?: [{kind, skill, route?, optional?, needs?}], note?} places a skill among the others. standalone: also runs on demand outside the chain. route: a route of the edge's skill (/fdesign master).

KindMeaningOther end declares
from / toruns after / hands off toto / from
spawnedBy / spawnsspawned as a subagent by / spawnsspawns / spawnedBy
escalatesescalates tonothing

Pipeline line: standalone; <from> → **<name>** → <to>; spawned by …; spawns …; escalates to … — <note>. skillmodel render checks every edge across the skills root: skill exists, other end agrees.

pipeline: {
  edges: [{
    kind:  "from"
    skill: "fdesign"
  }, {
    kind:     "from"
    skill:    "overlap"
    optional: true
  }, {
    kind:  "to"
    skill: "package-commit"
  }, {
    kind:  "spawnedBy"
    skill: "fleet"
  }, {
    kind:  "escalates"
    skill: "restack"
    needs: ["agentwt"]
  }]
}

What is enforced where

Five layers: shape in the schema, cross-references in the check, agreement between skills in the root render.

LayerEnforces
Schema, internal/skillgraph/schema.cueShape only: id and atom-id grammar, closed role and channel sets, 1–3 word titles, single-line fields. No pred on def, payload; post only on flow roles (step, substep, spawn, verify, stop); a payload only on payload entries, body in its file; pred/replaces/between only on route sections. Enum atoms need members.
Check, internal/skillgraph/check.goCross-references, next table.
Across skills, skillmodel render (part of make skills-check)Pipeline edges agree; each Skill(<name>) grant names a skill an edge reaches; each atom has its reader (Formal clauses); each cross-skill link token names an entry of that skill; every skill declares a smoke block. The smineless bundle render (skills.CheckBundle) holds no smine program, unfilled $SKILL_ reference or make <target> of this repo's Makefile.
LoadNo comments (notes are data: note). Frontmatter name equals dir name. Payload files, smoke fixtures present; skills/interfaces.cue valid; queries/*.json parse; smine literals (Checks). Format 3: fields files and schema properties exist (Fields); no model text links an entry in markdown.
SaveEncoded bytes decode back to an equal model before any write.
Check areaRule
Frontmattername, version present, non-empty; no key authored twice. Aliases strict kebab-case, not the skill's name, unrepeated. version equals changelog[0].version.
EntriesIds unique, naming this skill. Role order per section: def, rule, quality, flow, payload. Route anchors resolve, run forward. No skill atom shadows a shared one.
Routing and modelAt most one each, in their sections. Pipeline non-empty; no self edge, no edge twice.
Runtime placementbypass alone, only with docker: "always"; no mode twice; Script: pointer only with enableDelegation: "never"; rendered keys docker, enable-delegation, enforce-permissions never authored.
EnforceAt most 16 anchors, each referenced; checks and judgments name known anchors; a provenance from anchor captures the named capture; patterns compile.
Title1–8 states. Tokens match ^[A-Z]+(-[A-Z]+)*$, at most 16 runes, no built-in state. Entries known steps or stops (Title block). Signal regexes compile; tool matches Bash, Edit, Workflow or Write.
RequirementsCatalog component ids on requires, entry needs/unless, route and edge needs; needs disjoint from requires everywhere, from unless on an entry.
EnvironmentReferences, bindings (Checks).
Format-2 capsStatements ≤200 runes, bullets ≤160 (a link token counts as its target's title), no Why: bullets, verbatim prose ≤ line width.
Format 3No labelled item; fields only on roles taking them, in shape (Fields). Link tokens resolve under the ref discipline: any entry may link a def or payload; a def or payload links nothing; flow links stay in their section; a route may link its anchor entries. Titles unique, case-insensitive. Pred and post identifiers are atoms or enum members.
Every skillAtom sources in the closed grammar (Formal clauses). Each anchor capture read by a provenance check, a judgment's evidence or an atom's runtime source.
SmokeSkip or run; anchors, mode, fixture per Smoke block.

Formal clauses

pred: boolean expression over atoms (risk_map and dispatch_prepared, mode = nightly). post: atoms the entry makes true when it fires. internal/skillmodel.Derive turns them into the semantic model without judgment.

Entry roleBecomes
rule with predinvariant
quality with predsoft constraint
flow roletransition, guarded by pred (conjoined with its route's pred), firing post

An entry without clauses is uncovered, not an error. make model-analyze SKILL=<name> runs the solver on the derived model. Clauses must always parse; on the strict tier every atom must also resolve against the skill's atoms or skills/vocabulary.cue (ACDSL-SKILL-013).

An atom's source marks an input the world sets: | -separated parts, each one of these.

PartMeaning
arg: <row>[ => <member>]Args row, leading dashes stripped; => <member> forces an enum member.
sessionFact of the invocation's session.
environmentFact of the machine or repo.
runtime: <anchor>Anchored call succeeded (bool atoms).
runtime: <anchor>.<capture>Bool atom: capture resolved. Int or enum atom: its value.
runtime: <anchor>.<capture> = <literal> / != <literal>Bool atom comparing the capture to a JSON literal.

A runtime part names an anchor and capture of the skill's own enforce block, so heartbeat's check and the model read one value. Shared atoms (skills/vocabulary.cue) take none; anything else fails the load. Format-3 and shared atoms live only while a stop, rule or quality pred, a route section pred or a query reads them; a step's pred keeps nothing alive. Format-2 locals live with any pred.

skills/quality/railroad-review/railroad-review.cue, strict tier; fdesign's enum atom role reads Args via source: "arg: master => master | arg: derived => derived".

atoms: {
  base_build_red: {
    sort:   "state"
    source: "runtime: base_health.base_build != ok"
  }
  plan_mode: {
    sort:   "state"
    source: "session"
  }
  recommendation: {
    kind: "enum"
    members: ["APPROVE", "CONDITIONAL", "REJECT"]
    sort:   "state"
    source: "runtime: review_json.recommendation"
  }
  void_rounds: {
    kind:   "int"
    sort:   "count"
    source: "runtime: ledger.void_rounds"
  }
}

Fields

A format-3 entry carries no labelled bullet: its statement is prose plus at most one inline command; its mechanics are the fields record, rendered after it as * <key>: <value> bullets in this order.

KeyRolesCheck
scriptspawn, substepExisting stage file under workflows/; required on a [script] entry.
schemastep, spawn, substep, verifyExisting JSON schema under schemas/ declaring properties; required on a [schema] entry.
refevery role but def and payloadEntry ids resolving under the ref discipline; render as derived links.
flagsstep, spawn, substep, verifyOne line; each <name…> placeholder leads with an Args row, def title or defined code span, read, returned or minted name, or env key.
forwardsstep, spawn{arg, flag?, stage?}: arg an Args row, stage a property of the entry's schema.
readsstep, spawn, substep, verifyEntry-schema properties, dotted for nested.
returnsspawn, substepEntry-schema properties.
mintsflow rolesbranch starts with frontmatter branch-prefix; file relative; each id template holds a {…} or <…> placeholder.
timeoutstep, substep whose statement holds RUN Bash tool milliseconds.

Links are tokens: [[SECTION-NNN]] within the skill, [[SKILL-X-SECTION-NNN]] across; the render derives [Title · SECTION-NNN](#skill-<leaf>-<section>-<nnn>). Legal in statements, item texts, fields.ref, Args default/effect cells, Routing purpose, preconditions and route use; never in payload bodies (exact bytes). Below format 3 the param label and [Title](#anchor) links stay legal.

From skills/quality/railroad-review/railroad-review.cue: a script spawn; the Build and test step's fields (SKILL-RAILROADREVIEW-ROUND-016).

entry: {
  id:      "SKILL-RAILROADREVIEW-WALK-004"
  channel: "script"
  fields: {
    ref: ["TPL-008"]
    script: "workflows/checklist.js"
  }
  role:      "spawn"
  statement: "INVOKE `workflows/checklist.js` — one `railroad-checklist` agent per direction lacking a checklist: contracts on a moved head, over the diff; correctness in round 1 without checklist, over the spec."
  title:     "Checklist agents"
}

fields: {
  flags: "--repo <dispatcher toplevel> --root <artifactsDir parent>/wt --branch claude-review/<runNs>-buildtest-r{r} --salvage <artifactsDir>/salvage"
  forwards: [{
    arg:  "expect-head-red"
    flag: "--skip"
  }]
  mints: {
    branch: "claude-review/<runNs>-buildtest-r{r}"
  }
  timeout: 600000
}

Interfaces and environment

A skill never names a smine program: every tool, command or path it calls is a variable bound to one catalog interface. One skill thus runs on a full install, a partial one, or a colleague's machine with their own tools.

ScopeValuesFilled
installProvider programs.At deploy.
repoBuild, check, test, acdsl program, plans archive: per repository.At invocation, from the session's environment.

The catalog

skills/interfaces.cue holds interfaces and providers, loaded with every skill like vocabulary.cue. An interface is one capability a skill calls.

FieldRequiredMeaning
id, versionyesCapability, contract version; a binding names <id>@<version> (plan-sections@1).
kindyescommand (whole command lines, run bare), program (binary the skill appends args to), path (location) or skill (a deployed leaf name the skill hands off to, read in a Skill(…) grant and the hand-off statement).
scopeyesinstall or repo; every command record is repo.
summaryyesOne line, what it does; quoted in an unset repo-scoped variable's abort sentence.
exampleon a repo-scoped recordFitting value (go build ./..., bin/acdsl): skill-page placeholder, abort-sentence example.
invocationyes{cwd, args, stdin, timeout}; a program's args open with its subcommand word (plan-sections <plan file>); timeout a Go duration or none.
resultyes{exit, stdout}; exit maps codes to meanings, with "0" and "other".
validateyes{resolves: true} for command and program, {exists: "dir" | "file"} for path, {} for skill (the value is one leaf name, nothing to locate).
Note

A changed exit code, argument shape or output schema is a new version, never an edit in place.

A provider names a program and the versions it implements: {id, program, binary?, component?, implements, probe: ["interfaces"]}. component: install component shipping it, on PATH on every OS it installs on; binary: path the probe runs when it is off PATH (toolpolicy, a hook binary). Version and scope limits: Checks.

Provider (six)Scope
smine-rules, smine-agentwt, smine-heartbeat, smine-routine, toolpolicyinstall
acdslrepo: no component, no program. Every ACDSL repo vendors its own bin/acdsl, so its five capabilities are repo-scoped.
nonerepo: command, path and skill interfaces build@1, test@1, check@1, plans-archive@1, commit-skill@1.
interfaces: [{
  id:      "test"
  version: 1
  kind:    "command"
  scope:   "repo"
  summary: "Runs the repo's full test suite."
  example: "go test ./..."
  invocation: {cwd: "repo-root", args: "none", stdin: "none", timeout: "20m"}
  result: {exit: {"0": "pass", other: "fail"}, stdout: "diagnostic"}
  validate: {resolves: true}
}]
providers: [{
  id:        "toolpolicy"
  program:   "toolpolicy"
  binary:    "~/.claude/hooks/bin/toolpolicy"
  component: "gate"
  implements: ["permission-check@1", "permission-freeze@1"]
  probe: ["interfaces"]
}]

Binding

env: {
  PLAN_SECTIONS: {interface: "plan-sections@1", required: true}
  CHECK: {interface: "check@1"}
}
FieldRequiredMeaning
keyyesInterface id uppercased, hyphens as underscores (plan-sections → PLAN_SECTIONS); renders as SKILL_<SKILL>_<KEY>, <SKILL> the leaf uppercased without hyphens (SKILL_FDESIGN_PLAN_SECTIONS).
interfaceyes<id>@<version> of a catalog record.
requirednoUnset, skips the whole skill at deploy (install-scoped) or aborts the invocation (repo-scoped). Absent: optional.

Text references $SKILL_<SKILL>_<KEY>; scope decides where.

TextMay reference
Entry statements, items, payload bodiesBoth scopes: a placeholder can stand there.
allowed-toolsBoth scopes: the gate fills them. A path rule may root at one (Read($SKILL_PLANARCHIVE_PLANS_ARCHIVE/**)).
Args cells, routing and model text, other frontmatter values, enforce patterns, check reasons and recoveriesInstall-scoped only.
Preamble, prose blocksNo repo-scoped one.
Files under agents/ and workflows/ (scanned recursively)Install-scoped required only: filled at deploy, never trimmed.

A reference fits its interface's kind:

KindWhat may follow the reference
programThe args head word ($SKILL_FDESIGN_PLAN_SECTIONS plan-sections …), then a space, backtick, quote, (, ) or line end, so Bash($SKILL_X_TITLE_SET title *) and freeze( |$) pass. Or, passing the program itself as a value, a closing backtick, quote or ) right after (--acdsl '$SKILL_RAILROADREVIEW_ACDSL_CHECK').
commandNothing: end, backtick, quote, ), >, 2> or |; a command value takes no arguments.
pathAnything (unchecked).

Values

Deploy fills each variable from the first set source:

  1. The deploy process's SKILL_<SKILL>_<KEY> environment variable.
  2. The install record's SKILL_<SKILL>_<KEY> row (skills.variables, config server skill page).
  3. Repo-scoped interfaces: the global default (skills.defaults, keyed by interface id, Environment tab's global defaults card).
  4. The provider of the bound version: its PROVIDER_<ID> environment variable, its PROVIDER_<ID> row, then its catalog program if its component is in the render's set. A nil set, the canonical deploy, counts every component installed.
  5. Unset.

A provider row moves all of a program's capabilities at once; a SKILL_ row pins one.

Repo-scoped placeholders

For the Claude home a repo-scoped variable is never resolved, so never unset, at deploy (source invocation); it skips nothing there. Its reference renders as a harness placeholder reading SKILL_<SKILL>_<KEY> from the session's environment at invocation. The entry whose statement or payload reads it, else each reading item, becomes one fenced block the harness runs before sending the skill content:

```!
awk 'BEGIN { if (ENVIRON["SKILL_FIMPLEMENT_CHECK"] == "") { print "SKILL_FIMPLEMENT_CHECK: not set - <summary>, e.g. go vet ./... - <hint>"; exit 2 }; printf "%s", "\052\052SKILL-FIMPLEMENT-COMMIT-002\052\052 after \140" ENVIRON["SKILL_FIMPLEMENT_CHECK"] "\140 is green \342\200\224 one Bash call per line" }'
```
RuleDetail
No shell expansionThe harness checks the line against permission rules first, refusing shell expansion ($NAME, ${…}, $(…), backtick). So awk reads values via ENVIRON; no $ in the line.
EscapingOne single-quoted program; every text byte outside letters, digits, space and ,.:/_=+@~- is a three-digit octal escape awk decodes (backticks, quotes, braces, dollars, backslashes, brackets, line breaks, multi-byte runes). Abort message cut to the same characters.
Required unsetPrints SKILL_FIMPLEMENT_CHECK: not set - … with the record's summary and example, exits 2: invocation aborted; the harness forgives only exit 1 of search commands. Required guards first.
Optional unsetExits 0 silently; its entry or item vanishes whole.
Hint"set it for this repo on the skill page, or export it" in a deploy; "export it in your shell" in the bundle.
Install-scoped in the blockFilled value, escaped like text; no $SKILL_ reference survives, so the bundle scan needs no allowance.
Bash(awk *)Appended to allowed-tools when the skill declares a deferred variable; also in the gate template. The gate's awk fence admits the program: no redirect, pipe, system, getline.
env.jsonBeside SKILL.md with the Skill environment component: schema 1, skill, version, one entry per repo-scoped variable a rendered block reads (name, interface id, required, scope, summary, example). The skillenv hook exports each value (repo value, else skill default, else global default); refuses a typed /<leaf> or Skill call with an unset required value before the body expands (Install § Per-repo values). The gate fills grants from the same sidecars.
Without the hookShell exports reach the placeholder alike.

Value rules

KindValue rule
every valueOne line (a command may hold several); no carriage return, backtick, $SKILL_ or parenthesis: grants splice values into Bash(…), a command of several lines as one rule per line.
commandOne command per line, run in order, first red one stops. Lines split by internal/cmdline, as in smine-rules: spaces split words, double quotes group, no shell operators (&&, |, ;, $(). Readers say "one Bash call per line".
programOne word; a path with a slash allowed.
pathOpens with ~/ or /.

Deploy probes each provider it fills from, once per executable per run: <program> interfaces (or binary) prints {"interfaces": [...]}, the sorted versions it implements; all six binaries answer. A failed check leaves the variable unset, with a cause in the deploy log and on the skill page.

CheckCause
value rules and kind formvalue: holds a backtick, value: a program is one word, got 2, …
resolves<word> does not resolve to an executable. Each command line's first word looked up on PATH; a relative word with a slash under the repo it serves (checkout at deploy; the repo's path for a skill-page repo value).
exists<path> is not an existing dir
provider claimsmine-rules does not implement plan-sections@1 (answered: …), or the probe's own failure

required keeps its effect; the skip line carries the cause.

Unset variable read byEffect in the render
required declarationSkill skipped: skip <leaf> unconfigured (SKILL_…: <cause>); DeployLeaf refuses it.
Item (param, bullet, fence)Item drops, entry stays.
flags fieldField bullet drops, entry stays.
Statement, payload bodyEntry drops.
allowed-tools ruleRule drops.
Enforce anchor's tool, input or exclude patternAnchor drops with every check and judgment naming it.
Enforce check's reason or recovery, judgment's question or sendThat check or judgment drops.

A Skill(<leaf>) grant or pipeline edge to a skill the render cannot deploy (unmet requires, unset required variable) drops too. Set values fill as text in SKILL.md and copied sibling files, regex-quoted in enforce patterns, JSON-escaped elsewhere in enforce.json (reasons, recovery texts).

An allowed-tools rule reading a repo-scoped variable stays as text only when the render's set holds gate. The gate fills it per session from the session repo's values (skillenv.Resolve, the hook's resolution) and drops what it cannot fill; without the gate (the bundle) the rule drops. Neither deploy nor gate derives a permission rule from a value.

Checks

LayerRefuses
Model checkReferenced undeclared variable; declared one no text reads; optional one read where nothing can drop it; binding outside the catalog; key other than the derived key; reference not fitting its kind; repo-scoped reference outside entries and allowed-tools.
LoadLiterals smine-rules, smine-agentwt, smine-heartbeat, smine-routine (word-bounded: skill name smine-routines passes) and toolpolicy in model text and every file under agents/, workflows/; repo-scoped or optional references in those files.
Catalog loadDuplicate <id>@<version>; validate block not fitting the kind (a skill record takes {}); exit table without "0" and "other"; program interface without args head; command record outside scope repo; repo-scoped record without example; provider claim no record declares; two providers claiming one version; one provider claiming both scopes.

Deploy

The skills sync (internal/skills, configserver -sync-skills) renders for one component set and value map: the install record's components and skill rows (skills.variables, skills.providers, skills.defaults), on every writer path. The config server reads the record per request, so the Overview Sync, an apply and a skill-page save all deploy with the rows on disk.

HomeRepo-scoped variables
~/.claude/skillsDeferred to invocation as placeholders.
~/.codex/skillsStatic (a Codex agent runs no placeholder): deploy environment, record row, then global default (Values); else dropped or skill skipped like any other (skip <leaf> unconfigured (…) in <home>).
Rendered in processWhenHolds
SKILL.mdalwaysFormal clauses stripped, variables filled or deferred.
help-test.mdwith session-helpers, whose skillhelp hook reads itSmoke line; Eval: line when eval/definition.json exists.
title.jsonwith session-helpers and a set title-set@1 variableTitle states.
enforce.jsonwith gate and heartbeatEnforce block.
env.jsonwith skill-environment and a rendered placeholder; ~/.claude/skills onlyRepo-scoped variables the placeholders read.
answer.mda skill with answeredBy: "hook"Payload bodies.
.smine-deployedalwaysMarker holding the source hash the smoke checks.
CaseDeploy does
Never shipped<leaf>.cue, payloads/, eval/, smoke/, agents/ (deploys to ~/.claude/agents/, filled).
Other sibling filescontext-map.json, workflows/, schemas/ ship, text filled. A deployed copy holds no model: the config server's graph view shows a no-model card.
needs missedEntries, routes, edges drop.
unless heldEntry drops.
requires missedSkill skipped: skip <leaf> unavailable (…).

The bundle

make skill-bundle writes the standalone bundle to dist/skills for the empty component set: install-scoped values from the shell's exports, repo-scoped ones as placeholders with the shell hint. A skill ships unless it requires a component, carries a license key or lacks a required install-scoped value.

Index dist/skills/README.md: per shipped skill, what the full install adds (summaries of its unset optional install-scoped variables); per skipped skill, what it needs.

- <leaf> (skipped): needs <ref> from the <component> component

Runtime placement lines

Runtime placement fields render into the deployed frontmatter after the authored fields: answered-by:, docker:, enable-delegation:, enforce-permissions:. With answeredBy: "hook" the skillhelp hook answers every typed invocation with answer.md plus the feature catalog; the model check requires a payload entry and refuses flow entries.

Z3 analysis

Because the model is typed, its rules can be questioned: a format-2 skill is a controlled-English rulebook (MUST, NEVER, If ⇒ STOP, VERIFY), and modeled, z3 answers whether rules contradict, whether an entry is unreachable, and "if a and b hold, can c?"

What the analysis layer is

An optional per-skill analysis tier (one <leaf>.cue field): authoring contract plus compile-time test suite. No runtime effect: deploy strips clause lines; the executing agent never sees them.

TierOpt-inWhat happens
AnnotationsdefaultAny skill's clause lines are parsed, syntax-checked, rendered (graph view, diagrams), never resolved against a vocabulary: intent documented like interface types, cheap, visible, unenforced.
Modeltier: "strict"Checked semantic model: atoms resolve, kinds pair legally, skillmodel check runs the z3 battery, queries/ is the test suite; make audit-full fails the release gate on an error finding or verdict mismatch. solve, query refuse annotation-only skills.
When to opt in

Interacting state: counters, modes, obligatory stops gating real actions, fan-out/join arithmetic, as in the railroad-review dispatcher. Linear prose protocols gain nothing.

The pieces, bottom-up

Atoms build expressions, expressions clauses; clauses derive the model, queries question it.

Atom

One named, typed fact about a run.

KindDeclaredExample
true/falsedefaultasks_user, tools_loaded
whole numberkind: intremaining_budget, lanes_spawned
choicekind: enum, fixed ordered membersmode: interactive, nightly. Order ranks ("at least medium severity"); members exclusive by construction, no exclusivity axioms.

Shared atoms: skills/vocabulary.cue, alias phrases mapping one wording to one atom. A skill's own: the atoms map of its <leaf>.cue, same shape:

atoms: {<id>: {sort, kind?, members?, source?, aliases?}}

source is a closed grammar; free text fails the load. runtime binds an enforce anchor's capture, so heartbeat's check and the model read one value.

arg: <row>[ => <member>]
session
environment
runtime: <anchor>[.<capture>[ = <literal> | != <literal>]]

An unread atom fails make skills-check; delete it and its clauses.

AtomKept alive by
Local atom, format-3 skillStop, rule, quality or route section preds; queries. Not step preds.
Shared atomSuch a reader in some skill.
Local atom, format-2 skillAny pred.

Expression

An expression (expr) is a condition over atoms: and, or, not, => (implies), parentheses, comparisons; +/- and number literals on ints.

remaining_budget < round_cost
lanes_returned = lanes_spawned
mode = nightly
apply_level >= medium
base_provided and base_unresolvable

The last: "a base was given and could not be resolved."

Clauses

An entry's pred/post fields in <leaf>.cue, written once when authoring. SKILL.md renders them as code lines directly above the headline, no blanks between, each ending in the markdown hard break \ so they stay separate lines, not one paragraph:

`{pred: <expr>}` \
`{post: <atoms>}` \
<entry headline>
Entrypred:post:
ruleInvariant. Never-prompt rule: {pred: not asks_user}.
step, spawn, substep, verify, stopFiring guard. Base-unresolvable stop: {pred: base_provided and base_unresolvable}.Conjunction of atoms made true: a verify's guarantee, a step's effect.
qualitySoft: no effect on consistency verdicts; witnesses prefer runs satisfying it.
no clausesUncovered. Coverage reported (check prints formalized/coverable counts), never enforced.
RuleDetail
Grants stayPost-granted atoms start ungranted, then stay granted. Reachability is causal: a pred needing tools_loaded fires only after a post grants it.
Obligatory stopsA stop whose pred holds blocks every non-stop firing there; a fired stop blocks everything after.
DeployStrips entry clause lines from the agent copy (KEEP_FORMAL=1 keeps them); route section lines stay.
Annotation tierWithout tier: "strict": clauses must parse (gate-checked in every skill); atoms unresolved, no model claims, typed comparisons are documentation, not checked arithmetic.

Model

In memory, never committed, derived 100% mechanically from the clauses.

Clause sourceBecomes
A rule with pred:An invariant.
A flow-role entryA transition.
SectionsA chain in document order.

No translation layer, review pass or committed semantic-model artifact: a pure function of <leaf>.cue plus skills/vocabulary.cue, structurally drift-free. Each invariant and transition keeps its entry id, so solver answers point at <leaf>.cue and SKILL.md entries.

Query

A JSON file in the skill's queries/: one question, frozen to re-ask forever.

FieldMeaning
assumeGiven ("suppose the run is nightly").
checkTested ("…can the gate pause?").
expectPassing answer: sat (a scenario where all holds together) or unsat (the rules forbid it).
Tip

"If a and b, c cannot be" is exactly: assume a and b, check c, expect unsat.

Reading the answers

AnswerMeans
satPossible. solve prints a witness a human can check (scenario: asks_user=false, mode=nightly, lanes_returned=4, …). Whole-model consistency wants sat: no contradiction.
unsat + core: …Impossible. The core, a proof sketch, lists clashing invariants and transitions by entry id, transitions first; premises (your assumptions, vocabulary exclusivity axioms) stay out.
unknownz3 hit its 10-second per-check timeout. No verdict; solve and query exit 2.

Can the review ask the user?

make model-query SKILL=railroad-review EXPR='not asks_user'
verdict: unsat
core: SKILL-RAILROADREVIEW-EXEC-002

EXEC-002 is the invariant not asks_user: no state with the review asking is reachable.

Note

Cores are minimal: of several rule chains forbidding one thing, the core shows one.

The battery

skillmodel check <skill dir> (make model-check SKILL=<name>) runs it on the derived model, one line per finding, then a summary:

<skill dir>: <severity> <check>: <message>
Check (run order)SeverityA finding means
validateerrorUnparsable clause, unresolved atom, illegal kind pairing, consumed bool atom without producer or source, or exclusivity member not a declared bool atom.
consistencyerrorInvariants and exclusivity axioms contradict; names the clashing invariants.
guardwarningGuard never holds under the invariants: entry dead.
redundant-guardwarningA pred conjunct follows from invariants and sibling conjuncts ("guard always holds" for a one-conjunct pred).
haltwarningA stop's guard holds in every allowed state: stops every run at its start.
preservationwarningFiring from a state satisfying invariants and guard can break an invariant once post atoms turn true.
reachwarningEntry or stop never fires within the complete bound. Or "unreachable under atom = member": reachable otherwise, guard feasible there.
completionwarningNo transition of the last phase with transitions fires within the complete bound: every run halts or stalls early.
RuleDetail
SeverityOnly errors fail check (exit 1); warnings exit 0. A validate error, or an inconsistent model after consistency, stops the battery. Later checks skip entries guard found dead.
TierAnnotation: validate only, no z3. Model: all eight, z3 on PATH or check exits 2. unknown on consistency exits 2, on a warning check yields an "undecided" warning.
BudgetOne z3 process per check family (consistency; guard, redundant-guard, halt, preservation; reach, completion); each obligation a check-sat-assuming selector, 10-second timeout.

Complete bound

Posts grant only bool atoms; int and enum atoms never change in a run. So a run changes state at most min(P, G) times, plus one step to fire the target.

complete bound = min(P, G) + 1
G = the number of granted bool atoms
P = the number of transitions granting at least one

An unsat reach verdict at that bound is therefore a proof, not a sample. P and G count only the cone of influence: atoms an effective guard, effective invariant or the question (assumes, check) mentions, closed over exclusivity groups. Unread atoms, such as observation atoms, get no step copies and don't inflate it.

smt and solve default to it; an explicit lower --bound or query bound runs, warning its unsat is no proof.

Route anchors

Anchors are solver semantics, not just drawing.

AnchorEffect
replaces: <items>While the route pred holds, replaced entries can't fire (guard pred and not <route pred>) and replaced rules' invariants are suspended (not <route pred> => <invariant>); the route runs instead.
phase windowRoute transitions fire only in the anchor's phases. replaces: lowest to highest replaced-entry phase. between: <after>..<before>: <after>'s phase to <before>'s; start, end and context-section sides unbounded. Entries within a section stay unordered.

Ad hoc queries

skillmodel query --expr '<expr>' <skill dir> (make model-query SKILL=<name> EXPR='<expr>'): does the model imply the expression? The solver asserts effective invariants, exclusivity axioms and not <expr>, no soft clauses.

VerdictMeans
unsatHolds; the core names the proving invariants.
satCounterexample: scenario: atom=value, ….

Validity, unlike a committed static query file's satisfiability (assume + check, sat = possible). Exit 0 on either verdict, 2 on error or unknown.

Where it runs

The make targets in The commands (make audit: validate only) and the config server graph view, on page load. The read-only graph view shows atoms with grant and read counts, the enforce declaration, the tier in its toolbar (tier: strict — <n> atoms, <m> queries or tier: annotations) and an all-warnings button listing every battery finding and structural warning. Edit the model as <leaf>.cue text.

The commands

Start with make model-analyze ("run it, see it"); the rest answer narrower questions.

CommandDoes
make model-analyzeDefault SKILL=railroad-review. Clause checks, battery (consistency included), then every committed query; annotation tier: check only.
make model-queriesEvery opted-in skill's battery and committed queries, red on an error finding or verdict mismatch; the model test-suite step of make audit-full.
make model-check SKILL=<name>One skill: clause validation, model-tier battery, coverage report; prints the tier (tier: model / tier: annotations) and model-tier warning count.
make auditSolver-free: its skill-model gate (ACDSL-SKILL-013) runs validate only.
make model-query SKILL=<name> EXPR='<expr>'Does the model imply <expr>? Ad hoc queries.
go run ./cmd/skillmodel model <skill dir>Dumps the derived model as JSON.
make skill-export SKILL=<name>Prints the loaded skill model (entries, sections, merged atoms).
go run ./cmd/skillmodel diagram <skill dir>Or make model-diagram SKILL=<name>: whole-skill Mermaid, below.
go run ./cmd/skillmodel query --expr '<expr>' <skill dir>Ad hoc "does the model imply this?", uncommitted.
go run ./cmd/skillmodel solve --query my-question.json skills/quality/railroad-reviewAd hoc assume/check satisfiability, uncommitted.
go run ./cmd/skillmodel solve --reach <entry-id> [--bound n] <skill dir>Dead-entry check.

Dead-entry check

solve --reach unsat: no run ever fires the entry, because

At the default complete bound that is a proof (DEAD — unreachable within the complete bound); a lower --bound warns and prints only unreachable within bound <n>. A query file commits such a question with reach (an entry id) and optional bound in place of check. The battery's reach check already covers every entry and stop.

The diagram

diagram draws the whole skill as Mermaid; section kinds come from each heading's {section: …} line, kind-less skills render all-phase as before.

ElementDrawn as
The phase chainIn order.
between routes on phase boundariesForks: a split junction, one branch per route sharing the anchors, chains via the previous route's last entry, an otherwise edge unless the branch preds cover every case, a reentry junction.
Other between routes, all replaces routesReplaces/between edges to their anchors' sections.
Context sectionsDetached states.
EntriesOrdered kind-tagged lines in their state; flow entries numbered, clauses and enforcement channel shown.
StopsEdges into the halt sink; context-section stops are halt lines.
FlagEffect
--phases (PHASES=1)Trims to the execution chain.
--json (JSON=1)Same projection as stable JSON: the config server graph view's data contract. Server-side the blob adds args, env, warnings and notes outside the contract.

Authoring section kinds

FramingRule
Routes are implicitSteps gated on an earlier-established pred already form a route; the route kind names and groups it. Its line declares exactly one anchor, never inferred from section order: replaces: <items> (substitution) or between: <after>..<before> (optional insertion).
Phase interfaces are derivedNever authored: produces = atoms its posts grant, consumes = earlier-granted atoms its preds mention. Shown per phase card with the graph view's formal layer toggle on.

Boundary principle: a section ends where its last granted output lands; prose on the next section's internals goes there.

Workflow stage files

Programmatic stages are workflow files under workflows/: one per spawn, or a composition nesting others (railroad-review's round.js: lanes, dedup, verify, station). The dispatcher invokes the file at its spawn entry; the script channel names it.

RuleDetail
PromptsSpawns agents, builds prompts from the skill's payloads (GEN:payload blocks, never hand-copied), folds returns into its result.
Schemas and shared blocksSchemas in schemas/*.json, shared prompt blocks in workflows/common/, injected between GEN markers by make workflow-gen (rules binary over internal/workflowgen). The runtime allows no imports or file reads.
Computed argsEvery binary-computable dispatcher arg comes from an installed rules binary (smine-rules) subcommand; an enforce anchor captures its printed document, a provenance check pins it.
Known exceptionrailroad-review stage files still carry severity, id, dedup, retry and validation policy, pending their own plan.

Inputs and refs

Closed producer/consumer system: an atom guarding flow is granted by a step's post or declared an input by its source (grammar under Atom). skillmodel check enforces producer-or-input on the model tier; the graph view shows inputs as invocation tiles in a column left of the structure.

Refs point into vocabulary (defs, payloads) or own-section wiring, never cross-section control flow: sequencing belongs to the condition system. A route may ref the entries its anchor clause names.

Graph view

ElementBehaviour
Route cardsSet into the host phase card, cut at the anchor: a substitution before its first replaced entry, an insertion after its split entry; split line in, reentry line back to the chain. Lists the anchor's refs as a replaces or between line.
Invocation scenarioArg and session tiles on: inactive routes and rows an active route replaces gray out.
Ref violations⚠ analyses on their rows, listed in the all-warnings panel, which opens left outside the graph box; entry cards open right.
Links↗n outgoing, ↙n incoming on the row; focusing it draws them (neon out, orange in).

Current state and edges

TopicState
AdoptionEvery format-2 skill carries clause annotations (2026-09 fleet reformat). Only railroad-review is model-tier: its atoms (typed budget/lane arithmetic, shared mode enum), battery and three committed queries run in audit-full.
Opting inSet tier: "strict", declare atoms for every clause atom, pin worthwhile queries. Sourced input atoms need no tier and feed the invocation tiles (fdesign).
ExpressivenessBools, whole-number arithmetic, ordered choices: contradiction, implication, counting, causal-ordering questions. No unbounded backend: the complete bound makes unrolling exhaustive.
Out of scopeReal time, "eventually". Liveness and optimization: staged in the stage-2 doc plans/2026-09-14-skill-structure-wip-ada2d65/design/smt-stage2.md, with query-authoring conventions and the Stage-3 runtime-referee outlook.
SoundnessCausally sound reachability: only posts change state (non-granted bools rigid, granted ones flip only via a granting firing); boundary firings assert posts; negative bool assumptions and negated stop preds block naturally; Route anchors constrain firings.
Anchoringmake audit: every clause parses, every model-tier atom resolves. make audit-full: battery plus re-proven committed queries.
MeaningWhether a clause means the English is the author's judgment, made once, reviewed as the <leaf>.cue diff. make skills-check keeps it canonical, the skill dir free of generated files; nothing model-shaped can drift.