Status: Accepted (2026-10-20).
Date: 2026-10-20
Deciders: Stijn Dejongh (operator).
Technical Story: #6475 (command skills named spec-kitty.<command> are dropped by
strict Agent Skills readers), #6470 (Google Antigravity stopped reading .agent/workflows
on 2026-10-19). Mission command-skill-names-and-antigravity-01M5DBWT. Follow-ups: #6471
(slash-command file names), #3048 (global skills fan-out).
Reader: a maintainer working on the command-skill installer, the upgrade migrations, the home-folder command writer, or adding an agent harness.
Amends: ADR 2026-04-07-1
(global slash command installation). Its "all agents at every CLI startup" decision is replaced
by "configured slash-command agents of the current project", and its antigravity row
(~/.agent/workflows/) is removed. The rest of that ADR stands.
Context and Problem Statement
Spec Kitty delivers its workflow commands to Agent Skills harnesses (Codex, Vibe, Pi, Letta)
as generated skills under the shared project root .agents/skills/. Each skill was a folder
named spec-kitty.<command> with name: spec-kitty.<command> in its SKILL.md.
- The dot is not allowed (#6475). The Agent Skills specification allows only
[a-z0-9-]in a skillname, and the folder name must equal it. VS Code/Copilot, Cursor and OpenCode read.agents/skills/too, and drop the dotted skills without telling the user. The reference validatorskills-refrejects them. The generated frontmatter also carried Claude-only keys (user-invocable,argument-hint) at the top level, which strict readers reject in a shared root. - Antigravity's surface is dead (#6470). Spec Kitty wrote Antigravity's commands only as
workflow files in
.agent/workflows/(project, before 3.1.2) and~/.agent/workflows/(home folder, at every CLI start). Antigravity stopped reading that folder on 2026-10-19 and reads Agent Skills from.agents/skills/, so its users had no working command. - The home folder was sprayed. ADR 2026-04-07-1 writes command files for every slash-command agent at every CLI start, configured or not, including outside any project.
Decision Drivers
- Every command loads in every harness that reads the shared root, with no silent drop.
- No mutating flow deletes or rewrites a file Spec Kitty cannot prove it owns (charter: User Customization Preservation).
- One authority per concern, so the name and the frontmatter cannot drift between producers.
- The 12 slash-command agents' command files and their
/spec-kitty.<command>invocations do not change in this decision (#6471 owns that).
Decision
1. The name is spec-kitty-<command>, produced by one authority
The canonical command-skill name is spec-kitty-<command>, and its path is
.agents/skills/spec-kitty-<command>/SKILL.md. src/specify_cli/skills/command_skill_names.py
is the only module that composes the name, the path and the invocation tokens; it is a
dependency-free leaf, and tests/architectural/test_command_skill_name_authority.py pins that
no other module composes them.
- Explicit command set, never a prefix.
spec-kitty-is shared with shipped charter skills (for examplespec-kitty-implement-review), so a path is recognised only by exact match against the canonical command set and the manifest. A user's ownspec-kitty-mine/skill is never reported or touched. - Uniqueness is a test-time guarantee. A test checks every canonical name against the shipped charter skills, the built-in pack skills and the retired skill names.
- Clean break. No alias folder keeps the dotted names installed. The dotted names are recorded once, as legacy, in the naming authority (not in the existing retired-skill sets).
2. Invocations are rewritten at render time; slash files are unchanged
Pack sources keep /spec-kitty.<command>, which is correct for the slash-command agents. The
command-skill renderer rewrites /spec-kitty.<command>, $spec-kitty.<command> and
/skill:spec-kitty.<command> tokens of canonical commands to the skill name, keeping the sigil.
The rewrite is agent-independent, so one rendered body serves every command-skill agent. File
names (spec-kitty.plan.md), URLs and spec-kitty.ai are left alone.
How a user invokes a command skill:
| Harness | Invocation |
|---|---|
| Codex CLI | $spec-kitty-<command> |
| Pi | /skill:spec-kitty-<command> |
| Vibe, Letta, Antigravity | /spec-kitty-<command> |
| The 12 slash-command agents | /spec-kitty.<command> (unchanged) |
A pack skill expansion target builtin:spec-kitty.<command> stays accepted as input and
resolves to spec-kitty-<command>; error messages list the canonical names.
3. Every repair path applies one legacy rule
The installer owns the rule, and every installing path runs it: init, upgrade,
agent config add, doctor skills --fix and the tool-surface repair.
- Recorded dotted entry. The file is deleted when its bytes equal the recorded hash, or equal today's render under the old dotted name. Its folder is removed only when it is then empty. Edited bytes, a directory, a link or a symlinked parent are kept with a warning, and the manifest entry is dropped.
- Unrecorded dotted skill (operator ruling 2026-10-21). A clone can pull an
already-migrated manifest that no longer records its local dotted folders. An unrecorded
spec-kitty.<command>/SKILL.mdthat is byte-identical to Spec Kitty's dotted render is treated as owned and retired. Anything else is kept with a warning. - User content at a canonical path (operator ruling 2026-10-20). A user file with other
bytes at
spec-kitty-<command>/SKILL.md, or a non-emptyspec-kitty-<command>/folder withoutSKILL.md, is skipped with a warning. The rest of the batch installs. - A remove-only preparation (
agent config remove) only releases the removed agents from a legacy entry; the rule runs when the last owner is removed.
4. The rename migration is per clone
4.0.0rc8_command_skill_hyphen_names calls the installer once per configured command-skill
agent and deletes nothing itself. It is clone-local (clone_local_effect = True): the
.agents/skills/ files are git-ignored, so a record committed by one clone must not settle
another clone's ledger (ADR 2026-10-17-1). Its
apply() is idempotent. An unreadable manifest, an unreadable agent configuration and any
installer refusal are a success with a warning that names spec-kitty doctor skills, because
a failed record would block later migrations and the upgrade's surface repair retries. Every
dotted path still on disk afterwards is named in a warning. upgrade prints the new
invocation names once, when the rename did something.
5. doctor skills reports leftovers and collisions
spec-kitty doctor skills reports read-only and never deletes a path it lists.
- Kept edited dotted skill (no longer in the manifest): a warning with the remedy "remove it when you no longer need your edits". The exit code stays 0 when nothing else is wrong; keeping it is the user's choice.
- Dotted skill the manifest still records, or an unrecorded one that is Spec Kitty's exact
render: a failure (exit 1) with the remedy
spec-kitty doctor skills --fix, which retires it. A recorded one is labelled "still recorded by Spec Kitty", whether or not it was edited:--fixapplies the legacy rule, so an edited copy is kept and released, and is then reported as a kept edited dotted skill. - Collision at a canonical path: a failure (exit 1). The command skill is not installed
until the user renames or removes the folder and runs
spec-kitty doctor skills --fix.
doctor skills --json gains two additive keys, legacy and collisions; no existing key
changes.
6. Antigravity is a command-skill agent
antigravity joins SUPPORTED_AGENTS in skills/_agent_roster.py. Its AGENT_COMMAND_CONFIG
row and its .agent/workflows entries in AGENT_DIRS are removed. init, upgrade and
agent config add antigravity install the 14 command skills into .agents/skills/.
core.config.configured_slash_agents() splits a configured agent list into its slash-command
half; single-key checks test membership in AGENT_COMMAND_CONFIG, the same rule.
Removing the .agent/workflows entries from AGENT_DIRS also changes the historical
migrations that iterate get_agent_dirs_for_project(): they no longer visit
.agent/workflows. That is intended. Those files are dead (Antigravity stopped reading them),
and the one step that still has to handle them is 4.0.0rc8_retire_antigravity_workflows,
which reaches the retired path directly (section 7). The frozen migrations are not edited.
7. Antigravity workflow files are archived, not deleted
Operator ruling 2026-10-20: archive, never delete.
- Project.
4.0.0rc8_retire_antigravity_workflows(clone-local, ordered after the rename) archives a.agent/workflows/spec-kitty.<command>.mdfile only when it is a regular file, names a historical Spec Kitty command and carries an exact<!-- spec-kitty-command-version: X -->line in its first 20 lines. Each proven file is copied into its own fresh folder,.kittify/backups/antigravity-workflows/.backup-<timestamp>[-n]/.agent/workflows/<name>, and only then removed. A backup is never overwritten. Other files, unprovenspec-kitty.*names and the.agent/folders stay; a symlinked or non-directory path component refuses with a warning. When Antigravity is configured, the migration installs its command skills in the same step. This migration gates on the configured agents and the hard-coded retired path, because.agent/workflowsis no longer inAGENT_DIRS(a recorded deviation from the "iterateget_agent_dirs_for_project()" rule). - Home folder. The home-folder writer removes a
~/.agent/workflows/spec-kitty.*.mdfile only when its inventory records it unedited, or, with no record, when it carries the current version marker and equals the frozen render byte for byte. A symlinked~/.agentor~/.agent/workflowsrefuses. The folder itself is never removed.
The two proofs differ on purpose. The project copy can be committed, shared and edited by a team, so its proof is a marker check and the file is archived, never deleted. The home copy belongs to one person and Spec Kitty's own inventory records what it wrote there, so its proof is the inventory record (or an exact byte match with the frozen render) and an unedited file is removed without a backup. Both refuse on a symlinked path component and both keep the folders.
8. The home-folder writer is scoped to configured agents (amends 2026-04-07-1)
At CLI startup, ensure_global_agent_commands() writes command files only for the
slash-command agents configured in the current project's .kittify/config.yaml. Outside a
project, or with no or an unreadable configuration, it writes nothing. agent config add
writes the added slash-command agents' home files at once; a failure there is a warning and
the next CLI run retries.
The freshness stamp is per key (schema 2: cli_version, template_source_signature,
healthy_keys, retired_surfaces_cleaned). A call is fresh when every requested key is
healthy at this version and signature, so the warm startup path checks only the configured
agents. A stamp without schema 2 reads as absent, so the first run on new code builds once and
runs the ~/.agent/workflows cleanup.
9. Frontmatter is projected per root
src/specify_cli/skills/skill_frontmatter.py is the one frontmatter authority
(tests/architectural/test_single_frontmatter_authority.py).
- Shared roots (
.agents/skills/and any root not listed as native) get only the specification fieldsname,description,license,compatibility,metadataandallowed-tools. Other keys move undermetadata. - Native roots (
.claude/skills,.qwen/skills,.kilocode/skills) keepuser-invocable,argument-hintanddisable-model-invocationat the top level. - Pack skill validation and rendering enforce name ≤ 64 and description ≤ 1024 characters at load and again at preparation; a refusal names the limit and the length.
10. The reference validator runs in CI
The always-on agent-skills job in ci-router.yml runs scripts/ci/validate_agent_skills.py.
It validates every generated and shipped skill in a shared root with skills-ref==0.1.1
(through uvx) and with the in-repo mirror; native roots are checked by the mirror only. A
violation from either validator fails the job, and validator output that cannot be read is an
environment error, never a pass.
Consequences
Positive
- Every command skill is spec-valid and loads in every harness that reads
.agents/skills/. - Antigravity users have working commands again.
- Hand-edited dotted skills and user folders survive every path, each with a named remedy.
- A CLI run outside a project no longer writes into the home folder.
Negative
- Older CLIs cannot read the new manifest. The command-skill manifest schema pattern now admits the hyphenated form; a CLI from before this release refuses a manifest written by this one. That covers a downgrade and a teammate on an older CLI who pulls a committed manifest.
- Commands can appear twice. A harness that reads both a slash-command file and
.agents/skills/(for example a project configured for codex and copilot) now shows each command twice, because the skills load at last. #6471 resolves this. - The global skills writer still fans out to skill roots of agents a project does not use; #3048 tracks that.
Neutral
- Leftover dotted folders are visible in
doctor skillsuntil the user removes them. - Pre-4.x migrations that read the dotted layout are not edited (DIRECTIVE_044); ledger selection does not re-run them on a project they settled.
References
- Mission artifacts:
kitty-specs/command-skill-names-and-antigravity-01M5DBWT/(spec, plan,traces/design-decisions.md). - Code:
src/specify_cli/skills/command_skill_names.py,skills/command_installer.py,skills/skill_frontmatter.py,runtime/agent_commands.py,upgrade/migrations/m_4_0_0rc8_command_skill_hyphen_names.py,upgrade/migrations/m_4_0_0rc8_retire_antigravity_workflows.py,scripts/ci/validate_agent_skills.py. - Upgrade note: Command skills renamed for the Agent Skills specification.