Skip to content

Commit 170a6dc

Browse files
committed
skills(content-filename-from-title): add skill for naming pages
Captures the rule set used to choose single-word filenames for the walkthrough parts (anatomy, building, parsing, validation, conversion, ecosystems, comparison, security) so future additions follow the same reasoning. Structure follows Anthropic's skill authoring best practices: third-person description that names triggers, XML-tagged task / context / constraints / instructions / examples sections, and the 8 live walkthrough filenames as worked reference examples plus 4 counter-examples of choices the procedure rejects. A checklist at the end makes the soft constraints (stability, typeability, set consistency) self-auditable.
1 parent 3c09230 commit 170a6dc

1 file changed

Lines changed: 308 additions & 0 deletions

File tree

  • .claude/skills/content-filename-from-title
Lines changed: 308 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,308 @@
1+
---
2+
name: content-filename-from-title
3+
description: Turns a prose title into a short, single-word, URL-friendly filename for published content (docs, walkthrough parts, blog slugs, guide pages). Use when adding a new entry to a manifest that exposes public filenames (e.g. walkthrough.json parts, docs/*.md for GH Pages), when renaming an existing one, or when a user asks "what should I name this file?"
4+
---
5+
6+
# content-filename-from-title
7+
8+
<task>
9+
Produce a single-word, lowercase, ASCII-only filename (no extension,
10+
no hyphens, no digits) that best represents the content of a titled
11+
page. The filename goes into a config manifest — `walkthrough.json`
12+
part entries, `docs/` frontmatter, or similar — where it becomes the
13+
public URL segment for that page.
14+
</task>
15+
16+
<context>
17+
## Why this skill exists
18+
19+
Public URLs age badly when filenames carry implementation detail
20+
(`walkthrough-part-1.html`), numbering (`part-3-thing.html`), or
21+
cluttering punctuation (`url-%E2%86%94-purl-conversion.html`). A
22+
title-word filename (`anatomy.html`, `parsing.html`, `conversion.html`)
23+
is short, speakable on a call, typeable, and doesn't need to change
24+
when the surrounding ordering does.
25+
26+
Claude must apply the same reasoning every time, or the fleet of
27+
filenames will drift stylistically across contributors and across
28+
sessions. This skill captures the reasoning as a deterministic
29+
procedure so the output is reproducible.
30+
31+
## Where it fits in the repo
32+
33+
- `walkthrough.json` — the `parts[].filename` field is the URL segment
34+
the page is published under at `socketdev.github.io/socket-packageurl-js/<filename>.html`.
35+
- `docs/*.md` — the file stem becomes the URL segment when docs are
36+
stitched into the GH Pages flow (see `docs/pages-design-system.md`
37+
for the surrounding design system).
38+
- Any future blog or guide manifest added to this repo.
39+
40+
A build-time validator in `scripts/walkthrough.mts` enforces the
41+
**shape** (`[a-z]+`) and **uniqueness**; this skill decides the
42+
**choice** (which word).
43+
</context>
44+
45+
<constraints>
46+
## Hard constraints (validator-enforced)
47+
48+
- **ASCII lowercase letters only:** matches `^[a-z]+$`.
49+
- No digits (`part2` — FORBIDDEN)
50+
- No hyphens (`url-conversion` — FORBIDDEN)
51+
- No underscores, dots, slashes, or unicode
52+
- **Unique across all entries in the manifest.** If the word you picked
53+
is already taken, pick another that's still content-bearing.
54+
- **Single word.** Compound phrases (`buildingandstringifying`) are
55+
FORBIDDEN — pick the stronger of the two nouns instead.
56+
57+
## Soft constraints (style)
58+
59+
- **Typeable.** A user on a call should be able to say "go to the
60+
`anatomy` page" and the listener can type it correctly without
61+
spelling.
62+
- **Stable.** The word should still make sense if the surrounding
63+
ordering changes. `part-one` is unstable (it renames when content
64+
is reordered); `anatomy` is stable.
65+
- **Content-bearing, not generic.** `page`, `doc`, `content`, `item`
66+
are FORBIDDEN. Pick a word that would still be meaningful if you
67+
only saw it in a URL with no context.
68+
</constraints>
69+
70+
<instructions>
71+
## Decision procedure
72+
73+
Apply these rules in order. Stop at the first rule that produces a
74+
clean single word.
75+
76+
### Step 1 — Inventory the nouns in the title
77+
78+
Write out every noun and nominalized action (gerund, `-ion`, `-ance`).
79+
Discard every filler word (articles, prepositions, conjunctions, "of",
80+
"and", "&"). Discard every word that appears in 2+ other titles of the
81+
same manifest (those are qualifiers, not distinguishers).
82+
83+
> **Why:** a filename needs to distinguish this page from its
84+
> siblings. A word that isn't unique within the set can never be
85+
> load-bearing.
86+
87+
### Step 2 — Among remaining candidates, pick the distinguishing noun
88+
89+
If one noun is unique to this title and the others are not, that noun
90+
wins.
91+
92+
> **Example:** `"URL ↔ PURL Conversion"` has three nouns (`URL`,
93+
> `PURL`, `Conversion`). `URL` and `PURL` appear in multiple titles;
94+
> `Conversion` is unique to this one. → `conversion`.
95+
96+
### Step 3 — If several nouns are candidates, pick the superset
97+
98+
If the title lists multiple concepts that are facets of one bigger
99+
concept, pick the bigger one.
100+
101+
> **Example:** `"Validation, Errors & Results"` — errors and
102+
> `Result<T,E>` are outputs of validation. → `validation`.
103+
104+
### Step 4 — If the title is "verb on a subject", pick the verb's nominal form
105+
106+
Gerund (`-ing`) if the activity itself is the topic; `-ion` / `-ance`
107+
if the state or result is the topic.
108+
109+
> **Example:** `"Parsing & Normalization"` — normalization is a
110+
> substep of parsing. The activity is the topic. → `parsing`.
111+
112+
### Step 5 — If the title is a plain subject noun, use it directly
113+
114+
If the title is already a single content noun (`Ecosystems`), that's
115+
the filename. Just lowercase it.
116+
117+
### Step 6 — Check hard constraints, then pick an alternative if needed
118+
119+
Now validate the chosen word against the hard constraints:
120+
121+
1. Does it match `^[a-z]+$`? If not, reshape: `URL ↔ PURL` → consider
122+
nominals like `conversion`, not `urltopurl`.
123+
2. Is it unique across the manifest? If not, go back to Step 2 and
124+
pick the next-best candidate.
125+
3. Is it content-bearing? If it's generic (`items`, `details`), go
126+
back to Step 3 — you probably picked too abstract a word.
127+
128+
### Step 7 — Sanity check
129+
130+
Read your picks as a list. Does it feel like a coherent table of
131+
contents? If one word feels off-tempo (too long, too clinical, too
132+
cute), adjust. Internal consistency matters — don't mix
133+
`gerunds` + `nouns` + `adjectives`.
134+
</instructions>
135+
136+
<examples>
137+
## Worked examples — the 8 walkthrough parts
138+
139+
These are the filenames currently in `walkthrough.json` at the time
140+
this skill was written. Each shows the rule that produced the choice.
141+
142+
<example id="1">
143+
<title>Anatomy of a PURL</title>
144+
<filename>anatomy</filename>
145+
<reasoning>
146+
Nouns: `Anatomy`, `PURL`. `PURL` appears in 3 other titles (parts 2, 5),
147+
so it's a qualifier, not a distinguisher. `Anatomy` is unique. → `anatomy`.
148+
Rule applied: Step 2 (distinguishing noun).
149+
</reasoning>
150+
</example>
151+
152+
<example id="2">
153+
<title>Building & Stringifying PURLs</title>
154+
<filename>building</filename>
155+
<reasoning>
156+
Nouns / gerunds: `Building`, `Stringifying`, `PURLs`. `PURLs` is a
157+
qualifier. Stringifying is a substep of building (serialize is the
158+
last step of building). → `building`.
159+
Rule applied: Step 3 (superset) + Step 4 (gerund).
160+
</reasoning>
161+
</example>
162+
163+
<example id="3">
164+
<title>Parsing & Normalization</title>
165+
<filename>parsing</filename>
166+
<reasoning>
167+
Nouns: `Parsing`, `Normalization`. Normalization is a substep of
168+
parsing. The activity is the topic. → `parsing`.
169+
Rule applied: Step 4 (gerund form).
170+
</reasoning>
171+
</example>
172+
173+
<example id="4">
174+
<title>Validation, Errors & Results</title>
175+
<filename>validation</filename>
176+
<reasoning>
177+
Nouns: `Validation`, `Errors`, `Results`. Errors and Result<T,E> are
178+
the outputs/facets of validation. → `validation`.
179+
Rule applied: Step 3 (superset).
180+
</reasoning>
181+
</example>
182+
183+
<example id="5">
184+
<title>URL ↔ PURL Conversion</title>
185+
<filename>conversion</filename>
186+
<reasoning>
187+
Nouns: `URL`, `PURL`, `Conversion`. `URL` and `PURL` are the domain
188+
(appears in multiple titles). `Conversion` is unique.
189+
`conversion`. Rule applied: Step 2 (distinguishing noun).
190+
</reasoning>
191+
</example>
192+
193+
<example id="6">
194+
<title>Ecosystems</title>
195+
<filename>ecosystems</filename>
196+
<reasoning>
197+
Title is already a single content noun. Lowercase it.
198+
Rule applied: Step 5 (plain subject noun).
199+
</reasoning>
200+
</example>
201+
202+
<example id="7">
203+
<title>Comparison, Matching & Existence</title>
204+
<filename>comparison</filename>
205+
<reasoning>
206+
Nouns: `Comparison`, `Matching`, `Existence`. Matching is a flavor of
207+
comparison (wildcard comparison). Existence is adjacent but weaker.
208+
`comparison`. Rule applied: Step 3 (superset).
209+
</reasoning>
210+
</example>
211+
212+
<example id="8">
213+
<title>Security Primitives & VERS</title>
214+
<filename>security</filename>
215+
<reasoning>
216+
Nouns: `Security`, `Primitives`, `VERS`. In this curriculum VERS is
217+
scoped under security (injection + freeze + VERS-as-safety-boundary).
218+
`security`. Rule applied: Step 3 (superset).
219+
</reasoning>
220+
</example>
221+
222+
## Counter-examples — choices the procedure rejects
223+
224+
<example id="bad-1">
225+
<title>Anatomy of a PURL</title>
226+
<rejected>purl</rejected>
227+
<reasoning>
228+
`PURL` appears in multiple titles → fails Step 1 (not
229+
distinguishing). Also fails uniqueness against any other part that
230+
might want `purl`.
231+
</reasoning>
232+
</example>
233+
234+
<example id="bad-2">
235+
<title>Building & Stringifying PURLs</title>
236+
<rejected>buildingandstringifying</rejected>
237+
<reasoning>
238+
Compound phrase — violates the "single word" hard constraint. The
239+
procedure always picks one over merging.
240+
</reasoning>
241+
</example>
242+
243+
<example id="bad-3">
244+
<title>URL ↔ PURL Conversion</title>
245+
<rejected>url-to-purl</rejected>
246+
<reasoning>
247+
Contains a hyphen → fails the `[a-z]+` hard constraint. The validator
248+
would reject this at build time; the skill catches it earlier at
249+
Step 6.
250+
</reasoning>
251+
</example>
252+
253+
<example id="bad-4">
254+
<title>Ecosystems</title>
255+
<rejected>page6</rejected>
256+
<reasoning>
257+
Numeric, generic, unstable to reordering, not content-bearing. Fails
258+
hard constraints (digits) and soft constraints (stability,
259+
content-bearing).
260+
</reasoning>
261+
</example>
262+
</examples>
263+
264+
<checklist>
265+
## Checklist before committing a filename
266+
267+
Copy this into your working notes when adding/renaming a manifest
268+
entry:
269+
270+
```
271+
Filename choice: _______________
272+
273+
- [ ] Matches ^[a-z]+$ (lowercase ASCII letters only)
274+
- [ ] Unique across every other entry in the manifest
275+
- [ ] Content-bearing (not 'page', 'item', 'content', etc.)
276+
- [ ] Stable under reordering (no 'part1', 'first', etc.)
277+
- [ ] Typeable from hearing it spoken
278+
- [ ] Feels consistent with neighbor filenames' style (all gerunds?
279+
all plain nouns? all -ion forms? one style across the set)
280+
```
281+
282+
If any checkbox fails, return to the decision procedure and pick
283+
again.
284+
</checklist>
285+
286+
<when-not-to-use>
287+
## When NOT to use this skill
288+
289+
- The filename is **internal** (e.g. a build artifact under `dist/`,
290+
an intermediate JSON in `.cache/`). Internal paths don't need to be
291+
pretty — use whatever the code naturally emits.
292+
- The filename is **code-shaped**, not content-shaped. TypeScript
293+
source files follow the convention of the ecosystem (kebab-case,
294+
matching export names). This skill is for *content* filenames only.
295+
- The manifest exposes a **hash** or **date-based identifier** (e.g.
296+
a release slug, a git-sha-addressable blob). Use the hash; it's
297+
already optimal.
298+
</when-not-to-use>
299+
300+
<further-reading>
301+
- `CLAUDE.md` § ERROR MESSAGES — the error-shape the filename
302+
validator uses when it rejects a bad filename.
303+
- `docs/pages-design-system.md` — the surrounding design system for
304+
pages that use these filenames.
305+
- `scripts/walkthrough.mts``validatePartFilenames()` — the
306+
validator implementation that enforces the hard constraints.
307+
- `walkthrough.json` — the current live manifest applying this skill.
308+
</further-reading>

0 commit comments

Comments
 (0)