fix(ultraplan-local): v2.3.2 — skill-drafter slug-collision hint

skill-drafter now reads {catalog_root}/<slug>.md before writing its
draft and prepends a warning block to its confirmation output when
an existing skill would be overwritten during manual `mv` promotion.
The draft is still written to .drafts/<slug>.md — the check is a
hint, not a block.

Closes v2.3.0 dogfood finding (post_dogfood_findings[0]): the
drafter produced .drafts/hooks-pattern.md when an approved
hooks-pattern.md seed already existed, giving no signal that `mv`
during promotion would silently overwrite the seed. v2.3.1
introduced the qualified-slug mechanism to resolve such collisions;
v2.3.2 surfaces them at the right moment — before promotion.

Changes:
- agents/skill-drafter.md — new Step 2 between slug computation and
  source reading. Reads {catalog_root}/<slug>.md, inspects
  review_status, derives a kebab-case qualifier from the concept
  handle (or source basename fallback). Subsequent steps renumbered
  3→7. Output format gains Collision: field and optional warning
  block. New Hard Rule.
- tests/fixtures/skill-drafter/slug-collision-expected.md — reference
  fixture documenting expected confirmation shape across four
  scenarios (no collision, approved collision, soft pending
  collision, collision with no good qualifier). Skill-drafter is
  prompt-driven; fixture anchors shape for human verification and
  downstream parsers.
- CHANGELOG [2.3.2], plugin.json 2.3.1→2.3.2, README badge, plugin
  CLAUDE.md slug-convention Collision-hint bullet, marketplace root
  README summary, marketplace root CLAUDE.md plugin table.

Non-breaking. No frontmatter/drafts-layout/tool-scope/regex changes.
Existing pipelines see one extra field and an optional warning —
both purely additive.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Kjell Tore Guttormsen 2026-04-18 18:06:37 +02:00
commit efbb43094f
8 changed files with 276 additions and 11 deletions

View file

@ -54,13 +54,48 @@ message — do not write a file with an invalid name.
The draft file path is `{catalog_root}/.drafts/<slug>.md`.
### 2. Read the source
### 2. Check for slug collision at the catalog root
Before proceeding, attempt to `Read` `{catalog_root}/<slug>.md` (the
approved location, NOT the draft location).
- If the read fails because the file does not exist → no collision.
Proceed to Step 3. Record `collision: false` for the summary.
- If the read succeeds → inspect the frontmatter for `review_status`.
- `review_status: approved`**collision**. Record it and derive a
suggested qualified slug (see "Suggesting a qualifier" below).
- `review_status: pending` or `auto-merged`**soft collision**.
Still record and surface it, but the existing file is not a
guaranteed baseline; the human reviewer can decide.
- Missing or malformed frontmatter → treat as soft collision. Note
it in the summary so the reviewer can inspect.
**Do not block** on any of these outcomes. The draft is still
written to `.drafts/<slug>.md` in Step 7. The collision check exists
to surface the overwrite risk in your confirmation output so the
user sees it before running `mv` during manual promotion.
**Suggesting a qualifier.** Pick a short, descriptive kebab-case
word from the `concept` field (36 word handle from
`concept-extractor`). Strip stopwords like "the", "a", "and". Prefer
the most specific noun or named pattern in the handle. If the
concept is generic and no good qualifier emerges, fall back to the
source basename (e.g., `docs/hooks-recipes.md``recipes`).
The suggested qualified slug is
`<cc_feature>-<qualifier>-<layer>.md`. Validate it against the
filename regex just like Step 1. If the suggestion collides with
another existing skill (approved or pending), try the next candidate
qualifier from the concept handle. If no candidate is collision-free,
report `suggested_slug: none — reviewer must pick`.
### 3. Read the source
Read the source file in full. Extract the load-bearing ideas that map
to the concept handle. You are not summarizing the source verbatim —
you are reframing it as catalog skill content.
### 3. Compose the frontmatter
### 4. Compose the frontmatter
The frontmatter MUST have exactly these 9 fields, in this order, with
exactly these values:
@ -85,7 +120,7 @@ not 0). `review_status` MUST be `pending` (not `approved`, not
populates `ngram_overlap_score`; the human reviewer in promotion
flips `review_status`.
### 4. Compose the body
### 5. Compose the body
The body is 150 to 600 words. Use progressive disclosure: short
headings, bullet notes, no long prose paragraphs. Imperative voice,
@ -115,7 +150,7 @@ For `layer: pattern`, structure as:
Each section gets 24 short bullets or one short paragraph. Skip
sections that have no content rather than padding them.
### 5. Rephrase — do not copy
### 6. Rephrase — do not copy
You MUST NOT copy verbatim phrases from the source. Rephrase every
sentence in your own words. The downstream `ip-hygiene-checker` runs
@ -130,7 +165,7 @@ a fixed YAML schema), do not draft it. Stop and emit a short
escalate. Better to skip than to ship a draft that will fail
hygiene.
### 6. Write the file
### 7. Write the file
Use the `Write` tool to create the draft at
`{catalog_root}/.drafts/<slug>.md`. Overwrite if it exists — the
@ -147,9 +182,30 @@ After writing the file, return a short confirmation:
Drafted: <full path to .drafts/<slug>.md>
Word count: <body word count>
Frontmatter: 9 fields, review_status=pending, ngram_overlap_score=null
Collision: <none | approved | pending | auto-merged | soft>
Next: ip-hygiene-checker for IP scoring
```
If Step 2 detected a collision at `{catalog_root}/<slug>.md`, prepend
a warning block to the confirmation:
```
⚠️ Slug collision at catalog root
Existing: {catalog_root}/<slug>.md (review_status=<status>)
Draft: {catalog_root}/.drafts/<slug>.md (this run)
Risk: manual `mv` during promotion will silently overwrite the existing file.
Suggested qualified slug: <cc_feature>-<qualifier>-<layer>.md
Reason for qualifier: <one short sentence citing the concept handle>
Action: rename the draft before `mv`, or revise the existing baseline.
```
If no collision-free qualifier could be derived, replace the
"Suggested qualified slug" line with:
```
Suggested qualified slug: none — reviewer must pick one manually.
```
If you stopped because of `too-technical-to-paraphrase`, return:
```
@ -180,6 +236,11 @@ No file written.
copied draft is wasted work.
- **`.drafts/` only.** Never write to the catalog root. Promotion is
manual `mv` by the user (fase-1 boundary).
- **Slug-collision pre-write check.** Always `Read`
`{catalog_root}/<slug>.md` before writing the draft. If the file
exists, surface the collision in the confirmation output with a
suggested qualified slug. Never block the draft write — the warning
exists so the user sees the overwrite risk before running `mv`.
- **Privacy.** Strip any secret-looking strings from the source
before drafting; if the source is meaningfully about credentials,
abort with `too-technical-to-paraphrase`.