Skip to content
Back to skills

Add Id Convention

ASecurity

Use when allocating feature/hotfix/refactor/chore/docs/backlog IDs or creating branches — canonical `[NNNN][L]` format that the scripts (next-id.cjs, get-branch-metadata.cjs, build-setup.cjs, done.cjs) expect

  • 9 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added September 29, 2026
developmentgobashnodegit

Works with

  • cli

Security analysis

A100/100

Scanned October 7, 2026

npx -y skills add brabos-ai/code-addiction --skill add--id-convention --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Add Id Convention?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Add  Id Convention
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/brabos-ai-add-id-convention/badge)](https://www.skillsdirectory.com/skills/brabos-ai-add-id-convention)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: add--id-convention
description: Use when allocating feature/hotfix/refactor/chore/docs/backlog IDs or creating branches — canonical `[NNNN][L]` format that the scripts (next-id.cjs, get-branch-metadata.cjs, build-setup.cjs, done.cjs) expect
---

# ID & Branch Naming Convention

<!-- uses:
- skill: add--doc-schemas
- command: /add-build
- command: /add-new
- command: /add-plan
- command: /add-review
- script: build-setup.cjs
- script: done.cjs
- script: get-branch-metadata.cjs
- script: next-id.cjs
- script: backlog-cli.cjs
- script: backlog-id.cjs
- mention: init.cjs
- script: qa-evidence.cjs
- script: status.cjs
-->

## Overview

Scripts enforce this format; commands that diverge (e.g., letter-first `H0001` instead of `0001H`) produce branches that `done.cjs` cannot parse.

## When to Use

- Before allocating a new ID via `status.cjs next-id`
- Before `/add-new` records `branch:` in about.md frontmatter, and before `/add-build`'s `build-setup.cjs` runs `git checkout -b` for feature/hotfix/refactor/chore/docs branches
- Before writing `id:` in a doc frontmatter
- When writing `{{doc:ID}}` references

## When NOT to Use

- Scripts that already implement the convention (`next-id.cjs`, `get-branch-metadata.cjs`, `build-setup.cjs`) — they are the authority, not this doc
- Unrelated IDs (e.g., `CHG[NNNN]` for changelogs — different namespace, no letter suffix)
- Provider-specific issue trackers (Jira/Linear) — they own their own ID schemes

## Canonical Format

```
[NNNN][L]
```

- `[NNNN]` — 4-digit zero-padded decimal (`0001`, `0042`, `1337`)
- `[L]` — single uppercase letter suffix identifying the work type

### Letter suffixes

| Letter | Type |
|--------|------|
| `F` | feature |
| `H` | hotfix |
| `R` | refactor |
| `C` | chore |
| `D` | docs |
| `P` | perf |
| `T` | test |
| `B` | backlog ticket — decided, not started |

Must match the regex in `.codeadd/scripts/get-branch-metadata.cjs` (`[0-9]{4}[A-Z]`).

⛔ **`B` IS THE ONE LETTER WITH NO BRANCH.** A ticket records work that has not started, so there is
nothing to check out. When the ticket becomes work, that work allocates its own id of the matching
type — `0007B` becomes feature `0012F`, not `feature/0007B-...`. The ticket keeps a `work_id` field
pointing at the id it became, and the two stay distinct.

```
IF A TICKET IS BEING PICKED UP:
  ⛔ DO NOT USE: Bash for git checkout -b on a B id
  ⛔ DO NOT: Reuse the ticket's number for the work — the counter is global and 0007 is spent
  ✅ DO: Allocate a new id of the work's own type, and record it on the ticket
```

### Branch format

```
[type]/[NNNN][L]-[kebab-slug]
```

Examples:
- `feature/0001F-auth-system`
- `hotfix/0001H-login-timeout`
- `refactor/0007R-extract-parser`
- `chore/0003C-bump-deps`
- `docs/0002D-readme-sync`

### Usage in docs

Frontmatter `id:` and `{{doc:...}}` references both use the canonical format:

```yaml
id: 0001H
```

```
{{doc:0001H}}
```

## Allocation (MANDATORY)

Always via:

```console
node .codeadd/scripts/status.cjs next-id <LETTER>
```

Examples: `status.cjs next-id F` → `0001F`, `status.cjs next-id H` → `0001H`.

**A BACKLOG TICKET is the exception: allocation is native.** `backlog-id.cjs`, invoked through
`node .codeadd/scripts/backlog-cli.cjs add --record-file <ticket.json>`, computes the same global
counter at the operation root — the `status.cjs` call above stays valid for B (the wrapper keeps answering),
but the route the agent runs for a ticket is the CLI add, and `10000B` refuses like the wrapper's
filter always did.

Never hand-roll IDs. Never reuse an ID from another namespace.

### One counter, two sources, one implementation

The number is global across every letter. It is the max over **both** the
`docs/features/[NNNN][L]-*/` directories **and** the ids already on the backlog board,
`docs/backlog.jsonl` — counting only the first would hand out a number a ticket already holds.

**`backlog-id.cjs` is the one allocator, and it is native.** It computes the max+1 from both
sources, anchored on the raw text, and every route delegates to it: `next-id.cjs` (one uppercase
A-Z letter, exit 1 on a bad argument), `status.cjs next-id` (named
prefixes F|H|PRD|CHG|B, exit 2 on a bad prefix), `backlog-cli.cjs add` and the `init.cjs` seed are
thin adapters that retain their public argument validation, exit codes and exhaustion policy. The local CLI
allocates for you
when `add` runs — `node .codeadd/scripts/backlog-cli.cjs add --record-file <ticket.json>` needs no
allocator call. Backlog allocation refuses `10000` (`ERROR=id-allocation-failed`). The public
`next-id`, `status next-id` and `init` adapters preserve the old minimum-width formatting and
can emit a five-digit number at the boundary; this does not expand the four-digit backlog schema.

```
IF CHANGING HOW AN ID IS ALLOCATED:
  ⛔ DO NOT: Copy the scan into an entry — the old `next-id.sh` / `status.sh` pair did, and they
             diverged
  ⛔ DO NOT: Parse JSON to read the backlog — the core reads it by raw-text anchor, so a damaged
             line still yields its id
  ✅ DO: Change the scan only in `backlog-id.cjs`; every entry delegates there
  ✅ DO: Run the native allocator contract tests — `cli/tests/backlog-id.test.js` pins the
         canonical core, and `scripts/tests/status.test.cjs` pins `status next-id` against it
```

**The core reads the backlog by raw-text anchor, never by parse.** A line whose JSON is damaged
still yields its id, so a hand-broken board can never block an allocation. An absent board is a
no-op: a project with no `docs/backlog.jsonl` gets exactly the id it got before.

## Per-Scope Sequence IDs (qa-validation-NNN)

Not every artefact uses the global `[NNNN][L]` convention. QA validation reports (written by the QA judgement `qa-pipeline` adds to `/add-review`) use a **per-scope sequence** so each scope keeps its own local regression history.

- **Format:** `<feature-id>-qa-validation-NNN` (e.g. `0001F-qa-validation-003`), `NNN` zero-padded from `001`. Filename: `_tests/run-NNN/qa-validation-NNN.md` (the `id:` stem matches the filename).
- **Scope = the report's folder:** the subfeature folder when scoped to an SF, the feature folder otherwise. Two SFs of the same feature each have their own `qa-validation-001`.
- **Allocation:** run `node .codeadd/scripts/qa-evidence.cjs next "<scope-dir>"`. It takes the highest ID from working `_tests/run-NNN/` plus immutable `_tests/final/run-NNN/`, then adds 1. NOT via `status.cjs next-id` (that allocator serves `[NNNN][L]` types only). The supported sequence ends at `run-999` and fails loud instead of wrapping.

Distinct from `[NNNN][L]`: `NNN` is a 3-digit per-scope run number, no letter suffix, NOT globally unique. See the `qa-validation` schema in `{{skill:add--doc-schemas/SKILL.md}}`.

## SF-Qualified IDs (subfeature-scoped docs)

An epic feature holds N subfeatures, and some docs are written **per subfeature** — notably `design.md`, which `/add-plan` STEP 7.1 writes into `${FEATURE_DIR}/subfeatures/SFxx-<slug>/`. All N files would otherwise carry the same `id: [NNNN]F`, so single-path ID resolution (`grep -rE "^id: <ID>$" docs/`, the validation gate's `{{doc:}}` reverse lookup) returns N hits and cannot name one document.

- **Format:** `[NNNN]F-SFxx` (e.g. `0042F-SF03`) — the feature ID, a hyphen, then the subfeature key exactly as `epic.md` spells it (`SF` + 2-digit zero-padded).
- **When:** the doc lives under `subfeatures/SFxx-*/`. A feature-level `design.md` (non-epic feature) keeps the plain `[NNNN]F`.
- **`related:`** still points at the plain feature ID: `related: [[NNNN]F]`. The suffix disambiguates the document, not the feature it belongs to.
- **Not a new namespace:** `-SFxx` is a qualifier on an existing ID, never allocated via `status.cjs next-id`.

```yaml
id: 0042F-SF03         # docs/features/0042F-billing/subfeatures/SF03-invoices/design.md
type: feature-design
related: [0042F]
```

## Forbidden Patterns

| Wrong | Right | Why |
|-------|-------|-----|
| `H0001` | `0001H` | Letter-first breaks `get-branch-metadata.cjs` regex |
| `F42` | `0042F` | Must be zero-padded to 4 digits |
| `0001h` | `0001H` | Letter must be uppercase |
| `hotfix/H0001-x` | `hotfix/0001H-x` | Branch format follows ID format |
| `F[NNNN]` (in docs) | `[NNNN]F` | Placeholder follows canonical order |
| `0042F-sf3` | `0042F-SF03` | SF key is uppercase and 2-digit zero-padded |
| Same `0042F` on every subfeature `design.md` | `0042F-SF01`, `0042F-SF02`, … | N docs with one ID break single-path ID resolution |

## Validation Checklist

```
[ ] ID matches /^[0-9]{4}[A-Z]$/ (or /^[0-9]{4}F-SF[0-9]{2}$/ for a subfeature-scoped doc)
[ ] Branch matches /^[a-z]+\/[0-9]{4}[A-Z]-[a-z0-9-]+$/
[ ] ID allocated via `status.cjs next-id <LETTER>` (not hand-rolled)
[ ] Frontmatter `id:` uses the same format (no `L[NNNN]` variant)
[ ] `{{doc:...}}` references use `[NNNN][L]` order
```

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.