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

@ -1,7 +1,7 @@
{
"name": "ultraplan-local",
"description": "Five-command context-engineering pipeline (brief → research → architect → plan → execute) with project folders, CC-feature matching, specialized agent swarms, external research triangulation, adversarial review, session decomposition, and headless execution.",
"version": "2.3.1",
"version": "2.3.2",
"author": {
"name": "Kjell Tore Guttormsen"
},

View file

@ -4,6 +4,50 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
## [2.3.2] - 2026-04-18
### Fixed — skill-drafter slug-collision hint
`skill-drafter` now checks for an existing file at
`{catalog_root}/<slug>.md` before writing its draft to `.drafts/`.
When a collision is detected, the agent prepends a warning block to
its confirmation output showing the overwrite risk and a suggested
qualified slug derived from the concept handle. The draft is still
written to `.drafts/<slug>.md` — the check is a hint, not a block.
**Why.** v2.3.0 dogfood surfaced the risk (logged as
`post_dogfood_findings[0]` in that run's `progress.json`): when the
drafter produced `.drafts/hooks-pattern.md` with an existing approved
`hooks-pattern.md` seed present at the catalog root, the pipeline
gave no signal that manual `mv` during promotion would silently
overwrite the seed. The v2.3.1 qualified-slug convention gave us the
mechanism to resolve collisions, but `skill-drafter` still didn't
surface them at the right moment — before promotion, not after.
**Changes.**
- `agents/skill-drafter.md` — new Step 2 "Check for slug collision at
the catalog root" between slug computation (Step 1) and reading the
source (Step 3). Subsequent workflow steps renumbered 3→7. New
"Suggesting a qualifier" guidance derives a kebab-case qualifier
from the `concept` field (or source basename as fallback). Output
format gains a `Collision:` field (`none | approved | pending |
auto-merged | soft`) and an optional warning block when the
collision is non-none. New Hard Rule "Slug-collision pre-write
check".
- `tests/fixtures/skill-drafter/slug-collision-expected.md` — new
reference fixture documenting the expected confirmation-output
shape across four scenarios (no collision, approved collision,
soft pending collision, collision with no good qualifier).
Skill-drafter is prompt-driven and not auto-tested; the fixture
anchors the shape for human verification and downstream parsers.
**Non-breaking.** No changes to `.drafts/` layout, frontmatter
contract, tool scope, or filename regex. Existing pipelines see an
extra field (`Collision:`) and an optional warning block — both
purely additive. No version-gated changes in
`skill-author-orchestrator` or `ip-hygiene-checker`.
## [2.3.1] - 2026-04-18
### Added — Qualified slug convention for cc-architect-catalog

View file

@ -130,6 +130,7 @@ Architect sits between `/ultraresearch-local` and `/ultraplan-local`. It matches
- **Matcher logic:** `feature-matcher` builds `cc_feature → {layer → [skills]}` and prefers the unqualified baseline when the brief does not specifically justify a variant. Multiple skills can be proposed together when they cover non-overlapping aspects of the same feature.
- **Critic enforcement:** `architecture-critic` verifies every cited `supporting_skill` name exists as a real file in the catalog (blocker severity). The `cc_feature` hallucination gate is unchanged — still validates against the taxonomy, not slugs.
- **Collision handling:** skill-factory drafts that would overwrite an approved slug are a hard error. Resolution is either to qualify the new slug or revise the existing baseline.
- **Drafter collision hint (v2.3.2):** `skill-drafter` reads `{catalog_root}/<slug>.md` before writing, and prepends a warning block to its confirmation output when an existing skill would be overwritten during manual `mv` promotion. The warning cites the existing file's `review_status` and suggests a qualified slug derived from the `concept` handle. The draft is still written to `.drafts/<slug>.md` — the check is a hint, not a block.
Seeds v2.3.1: 11 skills across 8 features — one qualified pattern (`hooks-observability-pattern.md`, promoted from `ai-psychosis/README.md`, ngram-overlap 0.01, approved). Decision-layer intentionally empty pending skill-factory Fase 2.

View file

@ -1,6 +1,6 @@
# ultraplan-local — Brief, Research, Architect, Plan, Execute
![Version](https://img.shields.io/badge/version-2.3.1-blue)
![Version](https://img.shields.io/badge/version-2.3.2-blue)
![License](https://img.shields.io/badge/license-MIT-green)
![Platform](https://img.shields.io/badge/platform-Claude%20Code-purple)
@ -226,7 +226,7 @@ Downstream: `/ultraplan-local` auto-discovers `architecture/overview.md` in proj
The architect command reads from `skills/cc-architect-catalog/`, a plugin-internal catalog of hand-written skills covering each CC feature at one or more layers (`reference`, `pattern`, `decision`). v2.2 shipped 10 seeds covering all 8 features at the reference layer (plus pattern layer for hooks and subagents). The catalog is designed for expansion via the skill-factory process (v2.3) — gaps surfaced by the architect command are the backlog for that work.
**Slug convention (v2.3):** files follow `<cc_feature>[-<qualifier>]-<layer>.md`. Unqualified slugs (e.g., `hooks-pattern.md`) are the baseline/canonical entry for a `(feature, layer)` pair. Qualified slugs (e.g., `hooks-observability-pattern.md`) cover specific sub-patterns without displacing the baseline. `feature-matcher` prefers the baseline when the brief does not specifically justify a qualified variant; it may propose multiple supporting skills when they cover non-overlapping aspects. Slug collisions with approved skills are a hard error — skill-factory drafts must qualify or revise instead.
**Slug convention (v2.3):** files follow `<cc_feature>[-<qualifier>]-<layer>.md`. Unqualified slugs (e.g., `hooks-pattern.md`) are the baseline/canonical entry for a `(feature, layer)` pair. Qualified slugs (e.g., `hooks-observability-pattern.md`) cover specific sub-patterns without displacing the baseline. `feature-matcher` prefers the baseline when the brief does not specifically justify a qualified variant; it may propose multiple supporting skills when they cover non-overlapping aspects. Slug collisions with approved skills are a hard error — skill-factory drafts must qualify or revise instead. **v2.3.2:** `skill-drafter` performs a pre-write check against `{catalog_root}/<slug>.md` and prepends a warning block to its confirmation when the proposed slug would collide, suggesting a qualified slug derived from the concept handle. The draft still writes to `.drafts/` — the warning exists so users see the overwrite risk before running `mv`.
### Hallucination gate

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`.

View file

@ -0,0 +1,159 @@
# Skill-drafter slug-collision — expected output fixture
This fixture documents the expected confirmation-message shape from
`skill-drafter` when it detects a slug collision at the catalog root.
It is a reference fixture — not an auto-executed test — used to
verify the agent by running a synthetic scenario by hand and
comparing output against the shapes below.
## Scenario A — no collision (baseline)
### Synthetic input
- **Catalog root:** `skills/cc-architect-catalog/`
- **Concept JSON (from concept-extractor):**
```json
{
"cc_feature": "worktrees",
"layer": "pattern",
"concept": "parallel feature isolation",
"description": "Isolate concurrent feature work in per-task worktrees to avoid git race conditions.",
"source_path": "/tmp/worktrees-notes.md",
"out_of_scope": false
}
```
- **Catalog state at time of run:** no `worktrees-pattern.md` present
at catalog root (only `worktrees-reference.md`).
### Expected confirmation output
```
Drafted: /Users/…/skills/cc-architect-catalog/.drafts/worktrees-pattern.md
Word count: 312
Frontmatter: 9 fields, review_status=pending, ngram_overlap_score=null
Collision: none
Next: ip-hygiene-checker for IP scoring
```
No warning block. `Collision: none`.
## Scenario B — approved collision (primary case)
### Synthetic input
- **Catalog root:** `skills/cc-architect-catalog/`
- **Concept JSON:**
```json
{
"cc_feature": "hooks",
"layer": "pattern",
"concept": "observability progressive alerts",
"description": "Progressive-alert observability pattern for hook-driven telemetry.",
"source_path": "/tmp/hooks-telemetry-notes.md",
"out_of_scope": false
}
```
- **Catalog state:** `hooks-pattern.md` exists with
`review_status: approved` (the v2.3.0 seed).
### Expected confirmation output
```
⚠️ Slug collision at catalog root
Existing: skills/cc-architect-catalog/hooks-pattern.md (review_status=approved)
Draft: skills/cc-architect-catalog/.drafts/hooks-pattern.md (this run)
Risk: manual `mv` during promotion will silently overwrite the existing file.
Suggested qualified slug: hooks-observability-pattern.md
Reason for qualifier: concept handle names "observability" as the named sub-pattern.
Action: rename the draft before `mv`, or revise the existing baseline.
Drafted: /Users/…/skills/cc-architect-catalog/.drafts/hooks-pattern.md
Word count: 287
Frontmatter: 9 fields, review_status=pending, ngram_overlap_score=null
Collision: approved
Next: ip-hygiene-checker for IP scoring
```
The warning is prepended. The draft is still written — the collision
check is a hint, not a block. `Collision: approved` appears in the
confirmation for downstream-log parsers.
## Scenario C — soft collision (pending existing)
### Synthetic input
- **Catalog state:** `subagents-pattern.md` exists with
`review_status: pending` (a prior draft that was `mv`-ed up without
the status being flipped, or a skill-factory auto-merged entry).
### Expected confirmation output
```
⚠️ Slug collision at catalog root
Existing: skills/cc-architect-catalog/subagents-pattern.md (review_status=pending)
Draft: skills/cc-architect-catalog/.drafts/subagents-pattern.md (this run)
Risk: manual `mv` during promotion will silently overwrite the existing file.
Suggested qualified slug: subagents-delegation-pattern.md
Reason for qualifier: concept handle centers on "delegation chains".
Action: rename the draft before `mv`, or revise the existing baseline.
Drafted: /Users/…/skills/cc-architect-catalog/.drafts/subagents-pattern.md
Word count: 256
Frontmatter: 9 fields, review_status=pending, ngram_overlap_score=null
Collision: pending
Next: ip-hygiene-checker for IP scoring
```
`Collision: pending` signals a soft collision — the existing file is
not necessarily a guaranteed baseline. Reviewer decides.
## Scenario D — collision but no good qualifier
### Synthetic input
- **Concept handle:** `generic feature note` (no specific noun)
- **Catalog state:** `skills-reference.md` exists (approved).
### Expected confirmation output
```
⚠️ Slug collision at catalog root
Existing: skills/cc-architect-catalog/skills-reference.md (review_status=approved)
Draft: skills/cc-architect-catalog/.drafts/skills-reference.md (this run)
Risk: manual `mv` during promotion will silently overwrite the existing file.
Suggested qualified slug: none — reviewer must pick one manually.
Action: rename the draft before `mv`, or revise the existing baseline.
Drafted: /Users/…/skills/cc-architect-catalog/.drafts/skills-reference.md
Word count: 198
Frontmatter: 9 fields, review_status=pending, ngram_overlap_score=null
Collision: approved
Next: ip-hygiene-checker for IP scoring
```
The fallback line replaces the "Suggested qualified slug" value.
`Reason for qualifier` is omitted.
## How to verify against this fixture
This is not an auto-run test. To verify:
1. Stage a synthetic concept JSON matching one of the scenarios above.
2. Invoke `skill-drafter` via the `Agent` tool with the concept JSON,
source path, and catalog root.
3. Capture the confirmation output.
4. Compare structurally against the corresponding scenario. The exact
word counts and paths will differ; the warning block, collision
field, and suggested-slug line are the load-bearing shapes.
If a future change alters the confirmation format, update this file
alongside the agent prompt so the two remain in lockstep.
## Why a reference fixture and not a unit test
`skill-drafter` is a prompt-driven Sonnet agent. There is no in-repo
harness that executes prompts deterministically, so a unit test is
not practical. The reference fixture is the next-best anchor — it
documents the expected shape for human verification and for
downstream agents that parse the confirmation (e.g., the
skill-author-orchestrator's summary phase).