Install any skill in seconds. Free to start, no credit card required.
Get Started Free →How saved presets differ from templates, and the Load/Save/Delete/Bootstrap contract — order, atomicity, and naming.
.claude/skills/lingtai-ai-preset-skill-op-saved-presets/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-06 | ✗→✓ | ▲ Improved | 58% | 0% |
| case-01 | ✗→✓ | ▲ Improved | 19% | 0% |
| case-03 | ✗→✓ | ▲ Improved | -9% | 0% |
| case-08 | ✗→✓ | ▲ Improved | 41% | 0% |
| case-09 | ✗→✓ | ▲ Improved | -26% | 0% |
Evidence: tui/internal/preset/preset.go:213-610,731-891.
~/.lingtai-tui/presets/templates/ holds the 12 TUI-owned built-ins; ~/.lingtai-tui/presets/saved/ holds user-owned clones. There is no in-band "is_template" field — Source (SourceTemplate / SourceSaved) is set purely from which directory a file was loaded from, and is never serialized. Two presets with byte-identical JSON but different directories are treated differently.
Load(name) (preset.go:270-293) looks in saved/ first, then templates/. A saved preset with the same name as a template wins — the user's variant overrides. List() (preset.go:217-240) returns saved presets first (alphabetical), then the templates. Treat the templates' exact relative order as an implementation detail; current source does not reliably guarantee the full BuiltinPresets() sequence.
A saved preset falls back to the template only when the saved file is genuinely absent; a malformed saved override blocks fallback. Load distinguishes "file doesn't exist" (errors.Is(err, fs.ErrNotExist), falls through to the next directory) from a real failure — invalid JSON, permission error, or a path that is a directory — which is surfaced to the caller with the underlying cause preserved rather than collapsed into a generic not-found. Only when both the saved and template files are absent does Load return the not-found error.
Validate() is not auto-invoked by Save or LoadValidate() (preset.go:358-410) mirrors the kernel's own load-time validation gauntlet (description.summary non-empty; a non-empty tier must be one of 1..5, while an empty tier is allowed; llm.provider/llm.model non-empty; context_limit shape). It exists so callers — notably the preset editor's commit path — can refuse to save something the kernel would refuse to load. Neither Save nor Load calls it automatically; a caller that writes a preset without checking Validate() first can write a structurally invalid file. The editor calls it explicitly (see reference/operations/availability-save-gate/SKILL.md).
saved/, same-name overwrites, no atomicity claimSave(p) (preset.go:415-429) writes indented JSON only to the saved/ directory — never to templates/; that directory is owned exclusively by RefreshTemplates/Bootstrap. A save under a name that already exists in saved/ overwrites it (os.WriteFile, not create-exclusive). There is no atomicity claim for this write — it is a direct, non-atomic os.WriteFile, not a temp-file-plus-rename.
<template>-N naming for edited templatesAutoSavedName(template, existing) (preset.go:864-892) picks <template>-<N> for the lowest unused positive N, so editing and saving a built-in template never overwrites the template itself — it always branches off a fresh saved copy the user owns.
Delete(name) (preset.go:457-464) removes a file from saved/ only — templates are immutable from the user's perspective, and Bootstrap re-extracts them on the next launch regardless. Deleting a name that doesn't exist in saved/ returns nil (success), not an error. This success-on-missing behavior does not lower the bar for when a delete is proposed: deleting a user's saved preset is still a destructive action that requires exact authority for that specific preset before it is attempted.
RefreshTemplates — rewrites and prunes templates onlyRefreshTemplates() (preset.go:482-510) rewrites every file in templates/ from BuiltinPresets() on every TUI launch, and prunes any templates/*.json file no longer present in BuiltinPresets() — so a TUI upgrade that retires a template propagates cleanly. It never touches saved/. Like Save, there is no atomicity claim for these per-file writes.
For why the preset editor's Save is structural-only and never makes a live provider/model call, see reference/operations/availability-save-gate/SKILL.md. For how a saved preset becomes the running default and what /refresh switches, see reference/operations/activation-session-refresh/SKILL.md.
Other measured skills in the registry, with their headline benchmark lift.