# Tag Template Syntax

This guide documents the reusable template syntax for tag mutation commands such as `metadata -add` and `metadata -delete`.

The current goal is lowercase-first tagging. Examples in this document use lowercase tag names and lowercase text values, and no case-conversion transforms are part of the documented syntax.

## Where It Works

The shared template resolver currently applies to:

- `metadata -add`
- `metadata -delete`

Templates are resolved per item against that item's current tag set and lightweight result fields such as the current title.

## Core Placeholder Syntax

Use `#(namespace)` to insert the value from an existing namespaced tag.

Examples:

```powershell
metadata -add "title:#(track) - #(series)"
metadata -add "album:#(series)"
metadata -delete "title:#(track) - #(series)"
```

If an item has:

```text
track:9
series:ancient greek intensive course
```

then:

```text
title:#(track) - #(series)
```

resolves to:

```text
title:9 - ancient greek intensive course
```

## Namespace Matching

- Namespace matching is case-insensitive.
- Repeated whitespace inside the placeholder is normalized.
- A trailing `#` is ignored for compatibility, so `#(track #)` resolves the same way as `#(track)`.

Examples:

```powershell
metadata -add "title:#(track #) - #(series)"
metadata -add "code:#(disc number)"
```

## Transform Syntax

Use angle brackets for transforms:

```text
<name(arg1,arg2,...)>
```

Transforms run after `#(namespace)` placeholders are expanded.

### Padding

Use `padding`, `pad`, or `zfill` to zero-pad a value.

Examples:

```powershell
metadata -add "code:e<padding(00,#(episode))>"
metadata -add "code:e<pad(2,#(episode))>"
metadata -add "code:e<zfill(2,#(episode))>"
```

If `episode:3` exists, each example resolves to:

```text
code:e03
```

Padding width can be written in either of these forms:

- `00` meaning width 2
- `000` meaning width 3
- `2` meaning width 2
- `3` meaning width 3

### Default

Use `default(value,fallback)` when a namespace may be missing.

Examples:

```powershell
metadata -add "season:<default(#(season),0)>"
metadata -add "disc:<default(#(disc),1)>"
```

If `season:` is missing, the first example resolves to:

```text
season:0
```

### Replace

Use `replace(value,old,new)` for simple substring replacement.

Examples:

```powershell
metadata -add "slug:<replace(#(title),' ',_)>"
metadata -add "slug:<replace(#(series),-,_)>"
```

If `title:ancient greek intensive course` exists, the first example resolves to:

```text
slug:ancient_greek_intensive_course
```

Quote a space when you want to replace literal spaces. Bare spaces are trimmed by argument parsing, so `' '` is the reliable form.

### Increment

Use `increment(value,amount)` to do small integer adjustments.

Examples:

```powershell
metadata -add "episode_next:<increment(#(episode),1)>"
metadata -add "disc_next:<increment(#(disc),1)>"
```

If `episode:3` exists, the first example resolves to:

```text
episode_next:4
```

The second argument is optional; `<increment(#(episode))>` also adds `1`.

## Commas Inside Transforms

Tag arguments still support comma-separated tags, but commas inside transform calls are preserved.

This means the following stays as two tags, not three fragments:

```powershell
metadata -add "code:e<padding(00,#(episode))>,title:#(series)"
```

## Combining With `-extract`

Templates are especially useful after deriving tags from a title.

Example:

```powershell
metadata -add -extract "(series) - part (track)" "title:#(track) - #(series)"
```

For a title like:

```text
ancient greek intensive course - part 9
```

this can derive:

```text
series:ancient greek intensive course
track:9
title:9 - ancient greek intensive course
```

## Missing Values

If a placeholder or transform cannot be resolved, the whole templated tag is skipped instead of being written literally.

Examples of skipped cases:

- `title:#(missing_namespace)` when no such tag exists
- `code:<padding(x,#(episode))>` when the padding width is invalid

The command logs a warning summary for skipped unresolved templates.

## Recommended Patterns

Episode-style numbering:

```powershell
metadata -add "code:e<padding(00,#(episode))>"
```

Title synthesis from extracted tags:

```powershell
metadata -add -extract "(series) - part (track)" "title:#(track) - #(series)"
```

Delete a derived title tag:

```powershell
metadata -delete "title:#(track) - #(series)"
```

Reuse an existing value under a new namespace:

```powershell
metadata -add "album:#(series)"
```

## Mass Tagging Recipes

These are the patterns most likely to be useful when cleaning or normalizing large existing tag sets.

### Build A Stable Episode Code

If items already have `episode:` values and you want a compact sortable code:

```powershell
add-tag "code:e<padding(00,#(episode))>"
```

Examples:

```text
episode:3   -> code:e03
episode:12  -> code:e12
```

### Build Season-Episode Style Labels

If you already carry both season and episode values:

```powershell
add-tag "code:s<padding(00,#(season))>e<padding(00,#(episode))>"
```

Examples:

```text
season:1
episode:3
```

becomes:

```text
code:s01e03
```

### Fill Missing Season Values Before Building A Code

If some items have episodes but no season tag yet:

```powershell
add-tag "season:<default(#(season),0)>" "code:s<padding(00,#(season))>e<padding(00,#(episode))>"
```

That lets a later code template stay predictable even when the source metadata is incomplete.

### Rebuild A Title From Existing Tags

If you have normalized tags but want a cleaner `title:`:

```powershell
add-tag "title:#(series) - #(track)"
```

or for episode-style material:

```powershell
add-tag "title:#(series) e<padding(00,#(episode))>"
```

### Extract Then Reformat In One Pass

If the current title is messy but predictable:

```powershell
add-tag -extract "(series) - part (track)" "title:#(track) - #(series)"
```

This is useful when you want to convert display-oriented titles into searchable structured tags and then immediately synthesize a cleaner title back from them.

### Promote Existing Values Into New Namespaces

If one namespace already has the correct normalized value and you want to reuse it elsewhere:

```powershell
add-tag "album:#(series)"
add-tag "label:#(publisher)"
add-tag "subtitle:#(title)"
```

### Create URL-Safe Or Filename-Safe Slugs

If you want a simple underscore slug from an existing title:

```powershell
add-tag "slug:<replace(#(title),' ',_)>"
```

For more involved slug cleanup, chain multiple commands over time by writing intermediate normalized tags instead of expecting one giant expression.

### Create "Next Episode" Or Offset Tags

If you need a helper value for ordering or automation:

```powershell
add-tag "episode_next:<increment(#(episode),1)>"
```

or:

```powershell
add-tag "episode_prev:<increment(#(episode),-1)>"
```

### Delete A Derived Tag Predictably

Once a tag was created from a template, you can remove it with the same template:

```powershell
delete-tag "title:#(track) - #(series)"
delete-tag "code:s<padding(00,#(season))>e<padding(00,#(episode))>"
```

This is safer than manually typing the fully expanded value when doing bulk cleanup.

### Keep Inputs Lowercase Upstream

Because the documented system is lowercase-first, the cleanest workflows normalize source tags before using them in templates.

Recommended pattern:

- keep namespace names lowercase
- keep values lowercase when you create/import them
- use templates to compose values, not to fix letter casing later

Examples:

```text
series:ancient greek intensive course
episode:3
publisher:oxford
```

compose more predictably than mixed-case sources.

## Current Supported Syntax Summary

- `#(namespace)` inserts an existing tag value
- `#(track #)` compatibility aliasing works for namespaces that include a trailing `#`
- `<padding(width,value)>` zero-pads values
- `<pad(width,value)>` is an alias of `padding`
- `<zfill(width,value)>` is an alias of `padding`
- `<default(value,fallback)>` uses a fallback when the primary value is missing
- `<replace(value,old,new)>` performs plain substring replacement
- `<increment(value,amount)>` adds an integer offset, defaulting to `1`

If more transforms are added later, they should follow the same angle-bracket function style rather than introducing a second expression format.