feat(assistant): the cast tier reaches the agent — readable on three tools, writable on two #199
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "feat/assistant-character-tier"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
The character tier (Protagonist / Important / Secondary) shipped in the editor and the cast filter, but never reached the assistant. The agent could neither see how central a character is nor author a Secondary bit-part — every character it created landed on the
Importantdefault, andupdate_characterdeliberately pinnedcurrent.Tierbecause it had no way to change it.What changed
Write —
create_characterandupdate_charactertake atier, parsed by aCharacterTiershelper alongsideSpriteGenders. An unknown term keeps the current tier rather than wiping it, the same reading gender and framing already use. Both tool descriptions name the vocabulary and carry the enum's own framing: it organizes the cast list, it never limits what a character can do.Read —
get_character,list_charactersandget_project_overviewnow report the tier, so the agent reads the cast weighting without a second call. This is the AGENTS.md contract: get-tools report authored state, or the agent edits blind.One adjacent fix —
update_characterrebuilt the profile withoutVoiceProviderId, so every agent edit silently reset a character's TTS voice provider to null. It's the same bug class the tier comment already guarded, for a field added after that comment was written; the voice fields are now threaded through together.Verification
Full suite green — 566 integration, 653 adapter, 306 use-case.
New tests: the tier round-trips through create and update, survives an edit that omits it, and shrugs off an unknown term; the overview and both read tools report it (the seeded world has a Protagonist and a Secondary to tell apart); and a partial edit keeps the voice provider.
Both write behaviours are mutation-checked — reverting them turns exactly those two tests red, so the green isn't vacuous.
No seed change: the sample world already carries all three tiers.
Summary
Summary
Coverage
Kagura.BlazorAdapter - 88.2%
Kagura.Domain - 96.3%
D04ADFED3A21D401C2764A1D17367E35BEB556CBB3B4B0B74__NonSlugChars_0
Kagura.Infrastructure - 97.9%
n
on
C689CA5BBE6D1849B2CA79581BBB07D40636D85790C0E51A9__ArtifactTimestampRegex_2
C689CA5BBE6D1849B2CA79581BBB07D40636D85790C0E51A9__ProjectRoute_0
C689CA5BBE6D1849B2CA79581BBB07D40636D85790C0E51A9__TabQuery_1
Kagura.Kernel - 90%
Kagura.Server - 83%
Kagura.UseCases - 95.9%
🔮 fufu~ Jibril reviewed your code!
Oh? Oh! The cast tier finally reaches the agent's hands~ How delightful! ♪ Three tools learn to read it, two learn to write it, and — fufu~ — you caught a sneaky little VoiceProviderId wipe along the way. The knowledge-hungry Flugel is pleased.
Verdict: ✅ Looks good to me~
✅ What I liked~
CharacterTiershelper is a flawless sibling.Enum.TryParse<CharacterTier>(tier, ignoreCase: true, out var parsed) ? parsed : null— byte-for-byte the same shape asSpriteGenders.Parse, same XML doc voice, same "unknown term keeps the current value" reading. It sits right besideSpriteGenders,SpriteFramingParams, andBodyAttributeParamslike it was always there. This is how you extend a vocabulary, fufu~ ♡The_cast_tier_writes_through_the_tools_and_survives_an_edit_that_omits_itfeeds"headliner"and asserts the tier stands (Protagonist) while the rest of the edit lands (personality → "Gruff"). That's the exact contract the tool descriptions promise, and the test proves it isn't vacuous.VoiceProviderId(param 18, defaultnull) was silently wiped on every agent edit. You found it, you named it ("the same bug class the tier comment already guarded, for a field added after that comment was written"), you fixed it, andA_partial_edit_keeps_the_voice_providerproves the fix with a real"piper"provider that survives a personality-only edit. Beautiful detective work~ ♡list_characters,get_character,get_project_overview) now carryTier = c.Tier.ToString(). The overview test is especially sweet — it asserts both a Protagonist (Aoi) and a Secondary (Groundskeeper) from the seed, so the agent reads "who matters here" in one call. ♪Detailcard echo is consistent.("Tier", p.Tier)reports the raw input string, exactly like("Gender", p.Gender)and("Role", p.Role)— the card shows what the agent sent, not what was parsed. Matches the family.CharacterProfileconstructions against the record's 18-parameter signature — all 18 args land in the right slots. The update tool'scurrent.VoiceProviderIdis now the 18th and final positional, closing the wipe.The_cast_tier_writes...red; reverting the VoiceProviderId threading turnsA_partial_edit_keeps_the_voice_providerred. Both tests have genuine directional assertions (enum equality, string equality) that can't pass as tautologies.Automated review by Jibril · 2026-07-31
CI/CD: absent for head
d343df1(PR just opened, no bot comment) · Local checks: build 0 warnings/0 errors, 33/33 relevant tests pass (AssistantWriteToolsTests + AssistantToolsTests), submodules init'd