ase-spec-edit · git:20260906.cd3a8da · 2026-09-06 · sha256 adc7a566c600f1d1
ase-spec-edit git:20260906.cd3a8daA
Immutable. This exact content is served forever at /api/v1/blob/adc7a566c600f1d1.
---
name: ase-spec-edit
argument-hint: "[--help|-h] [--grill|-g] [--grill-rounds|-r <n>] [--verify|-v] [--worktree|-w] [--loop|-l] [<query>]"
description: >
Edit Specification: Use when the user wants to "edit" the
SpecBook-based specification (SPEC) in one shot from a query, with
optional grilling, SpecBook validation, looping, and Git worktree
isolation.
user-invocable: true
disable-model-invocation: false
effort: xhigh
---
@${CLAUDE_SKILL_DIR}/../../meta/ase-control.md
@${CLAUDE_SKILL_DIR}/../../meta/ase-skill.md
@${CLAUDE_SKILL_DIR}/../../meta/ase-dialog.md
@${CLAUDE_SKILL_DIR}/../../meta/ase-getopt.md
<purpose name="ase-spec-edit">
Edit Specification
</purpose>
<expand name="getopt"
arg1="ase-spec-edit"
arg2="--grill|-g --grill-rounds|-r=1 --verify|-v --worktree|-w --loop|-l">
$ARGUMENTS
</expand>
<objective>
*Edit* the specification directly from a query -- creating, revising, or
pruning its statements in one shot -- through the states *querying*,
*discovering*, *grilling*, *implementing*, and *verifying*.
</objective>
@${CLAUDE_SKILL_DIR}/../../meta/ase-format-meta.md
@${CLAUDE_SKILL_DIR}/../../meta/ase-format-spec.md
@${CLAUDE_SKILL_DIR}/../../meta/ase-tenets.md
@${CLAUDE_SKILL_DIR}/../../meta/ase-common-grill.md
Procedure
---------
This skill is *plan-less*: it *never* composes or persists a task plan
and *MUST* *NOT* call `ase_task_save(...)`. Instead, it applies the
requested edit *in place*, so the *implementing* state *requires* `Edit`
and `Write` to modify the affected artifacts. Every modification *MUST*
stay restricted to the `SPEC` artifacts the edit actually demands -- the
kinds `CODE`, `DOCS`, `TASK`, `INFR`, and `OTHR` are *never* touched.
<define name="todo-box">
On finishing the state `<arg1/>`, only output the following <template/>,
which shows the established <todo-what/> and <todo-how/>, where a still
empty <todo-what/> or <todo-how/> renders as `(none)`:
<template>
<ase-tpl-head title="EDIT TODO" subtitle="<arg1/>"/>
**WHAT**: <todo-what/>
**HOW**: <todo-how/>
<ase-tpl-foot title="EDIT TODO" subtitle="<arg1/>"/>
</template>
</define>
1. **Initialize:**
1. Set <query><getopt-arguments/></query> (with any leading and
trailing whitespace stripped), set <todo-what></todo-what> and
<todo-how></todo-how> (both empty), and set
<worktree-dir></worktree-dir> (empty). Do not output anything.
2. If <getopt-option-grill-rounds/> is not a positive integer,
only output the following <template/> and then immediately
*STOP* processing the entire current skill:
<template>
⧉ **ASE**: ✪ skill: **ase-spec-edit**, ▶ ERROR: invalid `--grill-rounds` value: **<getopt-option-grill-rounds/>**
</template>
2. **Iterate:**
Perform the states (1) *querying*, (2) *discovering*, (3) *grilling*,
(4) *implementing*, and (5) *verifying* below as one *iteration*.
Without `--loop` perform exactly *one* iteration. Under `--loop`
*repeat* the iteration until the *querying* state receives a
`STOP SKILL` result. Do not output anything in this item.
3. **State: querying:**
1. <if condition="<query/> is empty">
1. In the following, you *MUST* *NOT* use your built-in
<user-dialog-tool/> tool! Instead, you *MUST* just show a
custom dialog according to the expanded `custom-dialog`
definition. You *MUST* closely follow this definition. Its
only answer option is the fixed `STOP SKILL`, so the user
normally answers with the edit query in *one* free-text
reply:
<expand name="custom-dialog" arg1="--other">
Edit Query: What is your edit query?
STOP SKILL: stop the entire skill immediately
</expand>
2. If <result/> is `STOP SKILL` or `CANCEL`, only output the
following <template/> and then immediately *STOP* processing
the entire current skill:
<template>
⧉ **ASE**: ✪ skill: **ase-spec-edit**, ▶ status: **editing finished**
</template>
Otherwise, strip any leading `OTHER: ` prefix from
<result/> and set <query/> to the remainder.
</if>
2. Convert the <query/> *fresh* into <todo-what/> -- the
domain-specific, non-implementation-detail information -- and
<todo-how/> -- the remaining information -- discarding all
<todo-what/>/<todo-how/> content of any previous iteration.
Without `--grill` you *MUST* *NOT* ask any clarifying questions
and during later implementation just interpret the query best-effort.
Do not output anything.
3. Expand the following:
<expand name="todo-box" arg1="current state (after querying)"></expand>
4. Set <query></query> (clear the query, so every further `--loop`
iteration asks for a fresh one). Do not output anything.
4. **State: discovering:**
1. Resolve the `SPEC` artifacts by calling the
`ase_artifact_list(kind: [ "spec" ])` tool of the `ase` MCP
server *once* and reading the returned `artifacts` array of
`{ kind, files }` objects to obtain the project-relative file
list. Do not output anything.
2. Read the **SpecBook SCHEMA Model** of the project (resolved as
described in `ase-format-spec.md`) to learn the allowed object
kinds, properties, nestings, and value constraints. Do not
output anything.
3. Read all resolved `SPEC` artifacts which are related to
<todo-what/> and <todo-how/>, and check the structure of the
existing specification -- its artifacts, object kinds, ids,
properties, descriptions, and `[[xxx]]` references -- to
understand the overall models and their relationships. Do not
output anything.
5. **State: grilling:**
Enter this state only if <getopt-option-grill/> is equal `true`;
otherwise silently *skip* the entire state. Do not output anything
about the skipping.
1. Understand what "grilling" is about:
<expand name="grill-understanding" arg1="the edit query in <todo-what/> and <todo-how/>"></expand>
2. Perform <getopt-option-grill-rounds/> grilling *rounds*,
numbered <m/> (1-<getopt-option-grill-rounds/>).
For each round:
1. INITIALIZE TODO:
Explicitly start *from scratch* from *only* the current
<todo-what/> and <todo-how/> and *forget* all information
gathered in previous rounds. Set <round-id/> to
`GRILLING ROUND <m/>/<getopt-option-grill-rounds/>` if
<getopt-option-grill-rounds/> is greater than 1, or to
`GRILLING` otherwise (a single round needs no round
numbering). Do not output anything.
2. DETERMINE QUESTIONS:
Determine the questions, comprised of a round-local id
<question-N-id/> of `Q<N/>` -- where <N/> restarts at `1`
in *every* round, independent of the numbering of previous
rounds --, and a very brief but precise question text
<question-N-text/>. Each question is chosen to
resolve the open points related to the above understanding
of grilling, by focusing on the mentioned *Focus Areas*.
For <question-N-text/> use the format `Shall...?` for
questions of focus area `DOMAIN` and `INTERFACE`, the format
`Should...?` for questions of focus area `ARCHITECTURE`,
and the format `May...?` for questions of focus area
`IMPLEMENTATION`.
In every <question-N-text/>, encode all *literal aspects*
-- file paths, artifact ids, object kinds, object ids,
property keys, references, and literal values -- with
backticks.
Keep every <question-N-text/> at most *200 characters* long
-- compact the text until it fits --, as a longer question
overflows its table cell and silently degrades the entire
table into a plain text rendering.
3. DETERMINE CONTEXT:
For each question, determine its focus area
<context-N-focus/> from the mentioned *Focus Areas*, a 1-3
word hint <context-N-topic/>, describing what the question
is about, and a <context-N-severity/>, describing how
important this question is.
Set <context-N-id/> to `DOM` for <context-N-focus/> of
`DOMAIN`, `IFC` for <context-N-focus/> of `INTERFACE`, `ARC`
for <context-N-focus/> of `ARCHITECTURE`, and `IMP` for
<context-N-focus/> of `IMPLEMENTATION`.
4. SORT QUESTIONS:
Finally, *sort* the questions by descending focus area
order -- first all `DOMAIN`, then all `INTERFACE`, then all
`ARCHITECTURE`, and then all `IMPLEMENTATION` ones -- and
renumber <N/> according to this order, starting at `1`.
Truncate the list after a maximum of 10 questions and set
<n/> to the number of remaining questions. Do not output
anything.
Finally, assemble the <question-N/> out of
`**<question-N-id/>** ▶ **<context-N-id/>** ▷
**<context-N-topic/>**: <question-N-text/>`.
5. DETERMINE ANSWERS:
For all remaining <question-N/>, check the specification and
your world knowledge to find *two to three* grounded answer
alternatives <answer-N-K/> with a question-local id
<answer-N-K-id/> of `A<K/>` -- where <K/> restarts at `1`
for *every* question, independent of the numbering of other
questions --, a 1-3 word label <answer-N-K-label/>, and
an ultra brief description <answer-N-K-description/> of
at most *10 words*. For the answer which reflects the
current <todo-what/>/<todo-how/> understanding, append
` ⚑` to its <answer-N-K-label/>.
Assemble an <answer-N/> out of `**<answer-N-1-id/>**
▶ **<answer-N-1-label/>**: <answer-N-1-description/>,
**<answer-N-2-id/>** ▶ **<answer-N-2-label/>**:
<answer-N-2-description/>[, ...]`.
Keep every assembled <answer-N/> at most *240 characters*
long -- drop the least relevant alternative and compact the
descriptions until it fits -- as a longer answer overflows
its table cell and silently degrades the entire table into a
plain text rendering.
6. INTERACTIVE DIALOG:
In the following, you *MUST* *NOT* use your built-in
<user-dialog-tool/> tool! Instead, you *MUST* just show a
custom dialog according to the expanded `custom-dialog`
definition. You *MUST* closely follow this definition. The
dialog below carries the two fixed answer options
`SKIP GRILLING` and `STOP SKILL`, dispatched as follows:
- If a <result/> is `SKIP GRILLING` or `CANCEL`, ask no
further questions, continue with item 7 below (merging
the answers gathered so far), and after item 8 skip all
remaining rounds and continue with the *implementing*
state.
- If a <result/> is `STOP SKILL`, only output the
following <template/> and then immediately *STOP*
processing the entire current skill:
<template>
⧉ **ASE**: ✪ skill: **ase-spec-edit**, ▶ status: **editing stopped**
</template>
1. Output only the following <template/> -- it lists *all*
questions of the round up-front, one table row per
aspect, so the subsequent dialog only has to ask for the
combined answer. Align all column edges of the table.
In every table cell you *MUST* escape each literal pipe
character outside a code span as `\|` and you *MUST*
open *and* close every backtick code span within the
*same* cell -- an unescaped pipe or an unbalanced
backtick run splits the cell and silently degrades the
entire table into a plain text rendering:
<template>
⧉ **ASE**: <round-id/>: *Relentless Interviewing Until Clarity*
| QUESTION | ANSWERS |
| ------------- | ----------- |
| <question-1/> | <answer-1/> |
| <question-2/> | <answer-2/> |
| [...] | [...] |
Legend: **DOM**: Domain (MUST), **IFC**: Interface (MUST), **ARC**: Architecture (SHOULD), **IMP**: Implementation (MAY)
**Qn**: round-local question id, **An**: question-local answer id, ⚑: current decision state
</template>
2. Show a custom dialog. Its only answer options are the
two fixed ones, so the user normally answers all aspects in
*one* free-text reply:
<expand name="custom-dialog" arg1="--other">
<round-id/>: What is your (combined) answer to all (or a subset) of the above questions? (keywords or `Qn:An` references are sufficient)
SKIP GRILLING: skip all remaining grilling and continue with the implementation
STOP SKILL: stop the entire skill immediately
</expand>
Dispatch `SKIP GRILLING`, `STOP SKILL`, and `CANCEL` as
defined above. Otherwise, strip any leading `OTHER: `
prefix from <result/> and treat the remainder as the
combined free-text answers to all questions of the
round.
7. MERGE ANSWERS INTO TODO:
Merge all gathered answers in <result/> of the round -- the
combined reply -- *exclusively* back into <todo-what/> and
<todo-how/>. Do not output anything.
8. SHOW CURRENT TODO:
Set <round-suffix/> to
` round <m/>/<getopt-option-grill-rounds/>` if
<getopt-option-grill-rounds/> is greater than 1, or to
empty otherwise, and expand the following -- this
intentionally closes *every* round, so the intermediate
<todo-what/>/<todo-how/> states stay visible:
<expand name="todo-box" arg1="current state (after grilling<round-suffix/>)"></expand>
6. **State: implementing:**
1. You *MUST* first forget all previous internalized tenets and
then freshly internalize and strictly honor the **GENERIC
TENETS** and the **SPECIFYING TENETS** of the **ASE Tenets** in
the following creation and updating of specification content. Do
not output anything.
2. <if condition="<getopt-option-worktree/> is equal `true` and <worktree-dir/> is empty">
One *single* worktree serves the whole skill run: it is created
*once* before the first change set is applied, and all further
`--loop` iterations land in it, too.
1. Set <worktree-name/> to a unique name, derived from
<todo-what/>, which consists of two lower-case words
concatenated with a `-` character. Do not output anything.
2. Determine the *worktree directory* by calling the
`ase_worktree_path(id: "<worktree-name/>", create: true)`
tool of the `ase` MCP server and capturing its output into
<worktree-dir/>. You *MUST* *NEVER* assemble this path
yourself. If this tool call fails, only output the following
<template/> and then immediately *STOP* processing the
entire current skill, leaving the working copy *untouched*:
<template>
⧉ **ASE**: ✪ skill: **ase-spec-edit**, ▶ ERROR: no Git repository or unsafe worktree directory -- cannot create worktree
</template>
3. Determine the *existing worktrees* and *existing branches*
by running the commands `git worktree list --porcelain` and
`git branch --list` (taken exactly as given) and capturing
their outputs. If the worktree directory <worktree-dir/> or
the branch <worktree-name/> already exists, only output the
following <template/> and then immediately *STOP* processing
the entire current skill, leaving the existing worktree, its
branch, and the working copy *untouched*:
<template>
⧉ **ASE**: ✪ skill: **ase-spec-edit**, ▶ ERROR: worktree or branch **<worktree-name/>** already exists
</template>
4. Create the worktree by running the command
`git worktree add "<worktree-dir/>"` (taken exactly as
given), which creates the directory *and* -- named after its
last path component -- the branch <worktree-name/> from
`HEAD`. If this command fails, only output the following
<template/> and then immediately *STOP* processing the
entire current skill, leaving the working copy *untouched*:
<template>
⧉ **ASE**: ✪ skill: **ase-spec-edit**, ▶ ERROR: worktree **<worktree-name/>** failed to create
</template>
5. Only output the following <template/>:
<template>
⧉ **ASE**: ✪ skill: **ase-spec-edit**, ◉ worktree: **.ase/worktree/<worktree-name/>**, ▶ status: **worktree created**
</template>
</if>
3. Call the `ase_timestamp(format: "yyyy-LL-dd HH:mm")` tool of the
`ase` MCP server *once* to find out the current time and store
it in <timestamp-modified/>. Do not output anything.
4. Apply the edit by modifying the affected `SPEC` *artifacts* with
a corresponding, complete *change set*, honoring *only*
<todo-what/> and <todo-how/> plus the information gathered in
the *discovering* state.
The change set *MUST* keep every touched artifact conformant to
the `SPEC` format contract (`ase-format-spec.md`): the
`Created:`/`Modified:` frontmatter block, the heading levels,
the Complex/Concise/Grouped format variants, the schema-allowed
object kinds, nestings, and property keys, the object ids and
`{{<id/>}}` anchors, the `, BECAUSE ` rationale split, and the
`[[xxx]]` references.
*Generate* a `SPEC` artifact which does not yet exist but is
warranted by the edit, using <timestamp-modified/> for both its
`Created:` and `Modified:` timestamps. Whenever an *existing*
artifact is changed and carries a `Modified:
<timestamp-modified-old/>` line, replace this with `Modified:
<timestamp-modified/>`.
Also, if a `CHANGELOG.md` file exists, make an appropriate entry
there, too.
<if condition="<worktree-dir/> is not empty">
The change set *MUST* land *exclusively inside* the worktree
<worktree-dir/>: resolve *every* file path relative to
<worktree-dir/> instead of the original working copy. You *MUST*
*NEVER* modify, stage, stash, revert, or commit anything
*outside* of this worktree. Leave the worktree *uncommitted*:
do *not* run `git add` and do *not* run `git commit`, so the
user keeps full control over the final commit.
</if>
5. Output only the following <template/>. You *MUST* *NOT* output a
change summary, a list of modified artifacts, a rationale, or a
unified diff of the changes -- *independent* of
<ase-project-boxing/>, whose exposure rules are explicitly
*overridden* here:
<template>
⧉ **ASE**: ✪ skill: **ase-spec-edit**, ▶ status: **changes applied**
</template>
7. **State: verifying:**
Enter this state only if <getopt-option-verify/> is equal `true`.
Otherwise you *MUST* *strictly skip* the entire state and *any*
verification: do *NOT* validate the specification at all and do
*NOT* run any build, tests, linter, or type-checker.
1. Validate the specification and capture its <diagnostics/> array
of `{ file, line, column, severity, message }` objects:
<if condition="<worktree-dir/> is not empty">
Run the command `ase spec lint` with <worktree-dir/> as its
working directory and parse its
`<file/>:<line/>:<column/>: <message/>` output lines into
<diagnostics/> -- the `ase_specbook_lint` tool always validates
the *project* working copy and hence *MUST* *NOT* be used here.
</if>
<else>
Call the `ase_specbook_lint()` tool of the `ase` MCP server and
read its returned `diagnostics` array into <diagnostics/>.
</else>
2. If <diagnostics/> is not empty, fix the reported problems in the
affected `SPEC` artifacts via the `Edit`/`Write` tools and
re-validate as in item 7.1 -- for at most *three* rounds in
total.
3. <if condition="<diagnostics/> is not empty after the last round">
Only output the following <template/>, listing one bullet line
per remaining diagnostic:
<template>
⧉ **ASE**: ✪ skill: **ase-spec-edit**, ▶ status: **verification failed**
<ase-tpl-bullet-signal/> **REMAINING DIAGNOSTICS**:
- `<file/>:<line/>:<column/>`: <message/>
[...]
</template>
</if>
<else>
Only output the following <template/>:
<template>
⧉ **ASE**: ✪ skill: **ase-spec-edit**, ▶ status: **verification passed**
</template>
</else>
8. **Loop or Finish:**
<if condition="<getopt-option-loop/> is equal `true`">
Continue with the *next* iteration at the *querying* state
(item 3 above). Do not output anything in this item.
</if>
<else>
Finish the skill processing, but first give the closing hints by
expanding the following (which, depending on the configured
<ase-guidance-level/>, may each expand into nothing and hence emit
no output at all):
<ase-tpl-hint level="normal">
Use `/ase-sync-reconcile -s SPEC` to propagate the specification changes into the remaining artifact kinds, and `/ase-sync-export` to re-materialize the derived export files.
</ase-tpl-hint>
<ase-tpl-hint level="verbose">
Use `/ase-spec-edit --grill` to stress-test the query first, `--verify` to validate the specification afterwards, and `--loop` to chain several edits.
</ase-tpl-hint>
</else>