Context
Deferred from the Model Groups review on PR #14 because the concern is valid but not a blocker for that PR:
#14 (review)
Model Group names can currently contain whitespace, #, commas, control characters, and other grammar-significant characters. Their handling is inconsistent across persistence, TUI entry, prompt rendering, and autocomplete.
Current behavior
At PR #14 HEAD 2a3654abac575b71097659c4a87175ce8f61a161:
model-groups/names.ts:1-3 only trims names.
model-groups/store.ts:56-73,124-167 accepts any nonempty trimmed key during load, save, and CRUD operations.
model-groups/tui.ts:174-194 decodes escaped control characters into raw names.
index.ts:210-216 interpolates raw names into a comma-separated prompt section.
model-groups/autocomplete.ts:28-42 emits every name raw even though its token grammar cannot represent names containing whitespace or #.
- Existing prompt and autocomplete tests cover ordinary names only.
Risks
- Control or separator characters can alter the structure of generated prompt content.
- Commas make the prompt's name list ambiguous.
- Whitespace and
# produce names autocomplete cannot represent faithfully.
- Exact-name routing and completion can disagree about which names are addressable.
- Tightening validation without a migration policy could make previously valid persisted configurations unloadable or uneditable.
OpenSpec contract conflict
The review proposes rejecting whitespace, #, commas, and control characters across load, save, and TUI entry. However, PR #14's data-layer OpenSpec deliberately specifies trim-only identity and treats control-bearing names as valid (proposal.md:18,27; story.md:168,346,439,499,501,543). It instead calls for reversible prompt quoting/serialization, omission of grammar-unrepresentable names from autocomplete, and continued access through structured tools.
This contract conflict must be resolved explicitly rather than by silently tightening validation.
Scope and acceptance criteria
Test expectations
Add a shared hostile-name table covering at least:
- line feed (
LF)
- escape (
ESC)
- bell (
BEL)
- Unicode line separator (
U+2028)
- comma
- internal whitespace
#
Exercise it across load, save, CRUD, and TUI paths, with defensive prompt/autocomplete tests.
For a restricted-name policy, verify consistent rejection at every entry point, documented recovery for invalid persisted data, and defensive consumers if malformed data still reaches them.
For an arbitrary-name policy, verify identity-preserving round trips, reversible and structurally safe prompt serialization, omission of unrepresentable autocomplete entries, and unambiguous exact structured-tool routing.
Migration and design questions
- Which characters belong in canonical names versus being unsafe only in textual consumers?
- Should validation use an ASCII allowlist, Unicode-aware rules, or an explicit denylist?
- Are internal spaces intended to remain valid?
- Should invalid persisted names be renamed, quarantined, surfaced for recovery, or reject the whole file?
- What prompt quoting/escaping format is reversible and unambiguous?
- Should autocomplete omit unrepresentable names or gain a quoting grammar?
- Must structured tools continue to support names unavailable through text completion?
- How should normalization collisions be detected and reported?
Context
Deferred from the Model Groups review on PR #14 because the concern is valid but not a blocker for that PR:
#14 (review)
Model Group names can currently contain whitespace,
#, commas, control characters, and other grammar-significant characters. Their handling is inconsistent across persistence, TUI entry, prompt rendering, and autocomplete.Current behavior
At PR #14 HEAD
2a3654abac575b71097659c4a87175ce8f61a161:model-groups/names.ts:1-3only trims names.model-groups/store.ts:56-73,124-167accepts any nonempty trimmed key during load, save, and CRUD operations.model-groups/tui.ts:174-194decodes escaped control characters into raw names.index.ts:210-216interpolates raw names into a comma-separated prompt section.model-groups/autocomplete.ts:28-42emits every name raw even though its token grammar cannot represent names containing whitespace or#.Risks
#produce names autocomplete cannot represent faithfully.OpenSpec contract conflict
The review proposes rejecting whitespace,
#, commas, and control characters across load, save, and TUI entry. However, PR #14's data-layer OpenSpec deliberately specifies trim-only identity and treats control-bearing names as valid (proposal.md:18,27;story.md:168,346,439,499,501,543). It instead calls for reversible prompt quoting/serialization, omission of grammar-unrepresentable names from autocomplete, and continued access through structured tools.This contract conflict must be resolved explicitly rather than by silently tightening validation.
Scope and acceptance criteria
Test expectations
Add a shared hostile-name table covering at least:
LF)ESC)BEL)U+2028)#Exercise it across load, save, CRUD, and TUI paths, with defensive prompt/autocomplete tests.
For a restricted-name policy, verify consistent rejection at every entry point, documented recovery for invalid persisted data, and defensive consumers if malformed data still reaches them.
For an arbitrary-name policy, verify identity-preserving round trips, reversible and structurally safe prompt serialization, omission of unrepresentable autocomplete entries, and unambiguous exact structured-tool routing.
Migration and design questions