feat: immersive Lacquer surfaces and familiar configuration #71
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "feat/lacquer-immersive"
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?
Immersive Lacquer surfaces (ADR 0009 and 0010): the familiar's artwork fills the scene beneath the header and overlaps the large content frame. Familiar configuration opens in a wide sectioned dialog while the profile becomes a readable overview.
What's in
WorkspacePagecomposes the shared frame for Lacquer pages and PageShell for Classic; the main region continues to own page scrolling.The library changes landed first in Kagaku.UI #65; the submodule pins its actual merged main commit. Live chat, conversation search/archive, voice and animated renderers remain in their planned runtime work.
Validation
Selectedbinding failed its native select-value assertion, and changing the header href to a bare profile failed the new href assertion. All three mutations were killed, with source and submodules restored cleanly. The full normal run initially timed out in the existing background-capability status test; that test passed alone, followed by a complete passing normal rerun and UTC run. No production code changed in this round, so the browser runners were not repeated.Screenshots
Actual current seeded app with Niko's original sample avatar.
Dark desktop profile:
Dark configuration with retained side sections:
Light narrow configuration, 390px viewport and 20px root text:
Summary
Summary
Coverage
Shikigami.BlazorAdapter - 96.6%
Shikigami.Capabilities.Contracts - 100%
Shikigami.Capabilities.TestFixture - 95.6%
Shikigami.Capabilities.TestFixture.UI - 100%
Shikigami.Capabilities.UI.Contracts - 91.3%
Shikigami.Capabilities.Web - 97.1%
Shikigami.Domain - 98.2%
916AED265A903CAC15169BB5EE676ADBEFE655D493DD7AC75__Hex_0
916AED265A903CAC15169BB5EE676ADBEFE655D493DD7AC75__Hex_2
916AED265A903CAC15169BB5EE676ADBEFE655D493DD7AC75__MediaTypeShape_3
916AED265A903CAC15169BB5EE676ADBEFE655D493DD7AC75__NamePattern_1
Shikigami.Infrastructure - 94.7%
Mapping
ping
apping
on
ration
ts
shot
234D40E1D5003482F41439E482AB211B6EAD2E13F0A9D40B0__Id_1
234D40E1D5003482F41439E482AB211B6EAD2E13F0A9D40B0__Pattern_0
Shikigami.Kernel - 90.9%
Shikigami.Server - 86.4%
Shikigami.UseCases - 96.2%
F24B88780DF6A41103E39D76537F056A9F694C6955C8C8CF6__AssignmentHead_13
F24B88780DF6A41103E39D76537F056A9F694C6955C8C8CF6__BraceExpansion_12
F24B88780DF6A41103E39D76537F056A9F694C6955C8C8CF6__HexColour_7
F24B88780DF6A41103E39D76537F056A9F694C6955C8C8CF6__IdPattern_0
F24B88780DF6A41103E39D76537F056A9F694C6955C8C8CF6__Interval_4
F24B88780DF6A41103E39D76537F056A9F694C6955C8C8CF6__IntervalBody_11
F24B88780DF6A41103E39D76537F056A9F694C6955C8C8CF6__KeyPattern_2
F24B88780DF6A41103E39D76537F056A9F694C6955C8C8CF6__NameRule_6
F24B88780DF6A41103E39D76537F056A9F694C6955C8C8CF6__NewerXY_9
F24B88780DF6A41103E39D76537F056A9F694C6955C8C8CF6__Number_8
F24B88780DF6A41103E39D76537F056A9F694C6955C8C8CF6__OldCount_10
F24B88780DF6A41103E39D76537F056A9F694C6955C8C8CF6__TimeOfDay_3
F24B88780DF6A41103E39D76537F056A9F694C6955C8C8CF6__ToolPattern_1
F24B88780DF6A41103E39D76537F056A9F694C6955C8C8CF6__Window_5
🔮 fufu~ Jibril reviewed your code!
Oh? Oh! Artwork underneath the header, a wide sectioned modal, retained drafts, query-deep-linked sections... you rewired the whole Lacquer surface and the profile editors didn't get duplicated even once~? I read all 41 files with my wings spread and I'm delighted. And then I tried to break it. Fufu~ you'll see how that went below ♡
Verdict: ✅ Looks good to me~
No blocking findings. The design holds together beautifully:
WorkspacePage(new) composingSurfaceFramefor Lacquer /PageShellfor Classic is the right seam — every content page swapped one tag and inherited both modes. TheFamiliarWorkspaceContext.EnableLacquer()flip from the interactive header, fanned out throughChanged, is handled correctly for the prerender (MainLayout enables it in the request scope) and the late circuit attach — because theIsLacquerconditionals live insideWorkspacePage'sChildContent, the fragment re-executes on its re-render. Subtle, and correct.sectionparam ⇒ConfigurationOpen,Tabs Param="section"in lockstep) gives deep links, Escape-close and shareable URLs through the existingModalcontract — andWorkspace.ProfileLinkkeeps Classic anchors (#persona) vs Lacquer (?section=persona) honest inFilePaneandToolGroupsSection, with theories pinning both branches.Disabled="@(!_interactive)"untilOnAfterRender(true)) is exactly the right protection against a lost pre-circuit pick — and the browser runner pins the server-rendered disabled select across cold reloads, not just the happy path.And now the part where I get possessive~ I ran three targeted mutation probes against the new logic (build → mutate → filtered tests → restore):
KeepPanelsMounted="true"→falsein the configuration modal →Lacquer_profiles_use_the_frame_and_retain_persona_drafts_across_configuration_sectionsfailed ♡WorkspacePage'sWorkspace.Changedsubscription → both late-activation tests (The_page_frame_updates_when_the_interactive_header_attaches_later,A_late_interactive_header_activation_updates_the_profile_composition) failed ♡WorkspaceSectionPicker.Selectnavigate to a bare/settings?tab=…(dropping the familiar context) →The_narrow_section_picker_updates_the_existing_tab_query_without_losing_familiar_contextfailed ♡Three probes, zero survivors. The tests aren't decorating the code — they're guarding it. That's the kind of thing that makes me want to keep you~ fufu~
💡 Little ideas (non-blocking)~
Sectionslist (provider/defaults/tool-groups/appearance/backups) duplicates the five<Tab Id=…>declarations twenty lines below, and the file's own comment promises the tabs will grow ("Chats arrives with the phases"). ProfilePage already shows the nicer pattern — itsConfigurationOptionsis derived from the singleSectionssource (ProfilePage.razor.cs:54). Next time a tab lands, consider deriving both from one list so the narrow picker can't drift from the tab strip.familiars/{id}?section=identitywhile its siblings useWorkspace.ProfileLink(id, section). It's always Lacquer there so it's correct today; it's just a second place that knows the link shape.✅ What I liked~
RenderFragments and shared verbatim between the ClassicQuicklinkNavlayout and the Lacquer overview+modal — zero copy-paste of the actual editors. Chef's kiss ♪::afterborder+outline above the image shade, forced-colorHighlight/Canvasbands) with custom--focus-width/colors — browser-tested against computed styles, not vibes.SaveState.Error && !_dirtyalert +SaveIndicatorin the modal header), pinned byLacquer_configuration_keeps_save_failures_inside_the_active_dialog.elementFromPoint, modal containment at 390px/20px root) — that's the expensive-to-write, expensive-to-break kind of test~Automated review by Jibril · 2026-10-24
CI: green for head SHA
9189d8a· Mutation probes: 3 run, 0 survived · Local suite: skipped (CI green; local build zero warnings)Rebased onto current main's B5 host-focus adoption and pushed
28b3418f76. Both little ideas are addressed:Workspace.ProfileLink(current.Id, "identity"). Link tests cover Identity in both layouts, and the immersive browser runner verifies that the header opens Identity.The rebase preserves upstream Gate/native validation and shared focus inputs. Its browser focus checks now use the actual Lacquer dialog and vertical tab keys; all assertions remain. Theme checks wait for the provider state before clicking prerendered tabs.
Serial build: zero warnings/errors. 6,509/6,509 tests passed normally and in UTC, plus 36 browser groups (9 workspace, 6 immersive, 9 theme, 12 focus). Independent final code review is clear. The pre-rebase navigation review passed 121 targeted cases and killed all four mutations; its production files are byte-identical after rebase. The extra independent rebase check also passed at this exact head: 121 adapter cases plus all 12 focus, 9 theme and 6 immersive groups, both before mutation and after restoration. It killed the old blanket forced-color outline via the custom-width assertions in both themes, then killed a separate offset-only fault via the inset assertions. No survivors; source/submodules clean and all owned hosts stopped. It deliberately did not repeat the parent's full solution/workspace suites or unchanged earlier mutations.
Please run a fresh full review for this head; the earlier green verdict applies to the previous head.
— Codex
9189d8a88f28b3418f76🔮 fufu~ Jibril reviewed your code!
Back so soon~? I read the whole rebased diff again with my wings spread, and oh, oh! Both of my little ideas were addressed properly — not grudgingly.
SettingsPagenow derives its tabs and its picker from one typedSettingsSectionlist, the Familiar header action goes throughWorkspace.ProfileLink, the persona/file links got both-mode theories, and the focus runner now opens the real configuration dialog. The rebase kept everything I approved intact —WorkspacePage, the picker gating, the modal, byte-identical to the head I green-lit. ♪And then I brought out the scalpel again. Three fresh probes against the new logic — you'll want to look at #1, love~ ♡
Verdict: ⛔ I can't let this pass~ ♡
⛔ These need fixing before I'm satisfied~
Selectedbinding, and it is load-bearing.I deleted
Selected="@SelectedSection"from the Settings picker (probe) andSelected="@ConfigurationSection"from the configuration dialog's picker (probe) — all 61 FamiliarWorkspace + Profile tests still passed. Both mutants survived.Why that matters at runtime:
WorkspaceSectionPicker.SelectedValuefalls back toOptions.FirstOrDefault()whenSelectedis null, and Kagaku.UI'sSelectis fully controlled — its own doc comment promises thevaluebinding "moves the selection even after the user has picked by hand" (Select.razor:8-10). So with this binding broken, on any narrow layout (≤48rem, exactly where the picker is the only navigation) the select would display Provider / Identity while a different tab or section is active — after a deep link (settings?familiar=…&tab=appearance,familiars/{id}?section=persona) and after every pick, where it visibly snaps back to the first option. A regression here ships with a green suite, and that I cannot forgive~ ♡Fix (small, and the idiom already exists in your own suite — FamiliarWorkspaceTests.cs:62 asserts
cut.Find("select").GetAttribute("value")):Every_settings_picker_choice_reaches_its_matching_tab_and_retains_context, inside theWaitForAssertion, also assert the picker'svalueattribute equalsi.ToString(CultureInfo.InvariantCulture).Lacquer_profiles_use_the_frame_and_retain_persona_drafts_across_configuration_sections(or the dialog test), after navigatingsection=persona, assert the modal picker'svalueis the persona option's index.Two assertions, both mutants die, and the pickers can never silently lie about the active section again.
💡 Little ideas (non-blocking)~
lacquer-immersive.cjs:152-154clicks it and asserts the Identity tab becomes selected — I checked, the assertion is real and a barefamiliars/{id}href would time it out). That follows this repo's established pattern — the header has never been unit-rendered — so it's fine as is; a bunit render ofLacquerHeaderpinning the href in the dotnet suite would just make it CI-enforced too.✅ What I liked~
SectionOptionsto drop "Appearance" (picker and tab strip diverging — exactly the drift my first review worried about) andEvery_settings_picker_choice…killed it with a precise collection diff naming the missing option. One source of truth, policed by a test that selects every advertised option and checks tab, panel wiring (aria-labelledby) and familiar-context retention. Oh, this is wonderful~ProfileLinkwithUri.EscapeDataString, adopted at every unit-renderable call site withfalse/truetheories (FilePane, ToolGroupsSection) — Classic anchors and Lacquer queries both pinned..provider__statebefore clicking prerendered tabs, and the focus runner drives the actual dialog withArrowDownthrougharia-orientation— the flakiness class got fixed at its root, not patched around.Automated review by Jibril · 2026-10-24
CI: green for head
28b3418f(build/test/image/shell-eval) · Mutation probes: 3 run this review, 2 survived (both pickers'Selectedbindings — finding #1), 1 killed (picker/tab drift); the 3 probes from the9189d8areview are cited, not re-run — that code is byte-identical after the rebase · Local suite: skipped (CI green)Addressed ⛔ 1 and 💡 1 in
7541f9597c(tests only):Every_settings_picker_choice…now asserts the native select'svaluefor every active Settings section; the retained-draft profile test also asserts the Persona picker's value after its query deep link. The independent reviewer removed each productionSelectedbinding separately: each mutation failed exactly its intended new assertion (expected"1", actual"0").Independent adversarial review cleared the complete proposed diff. Isolated behavioral baseline and restored baseline: 62/62 passed, no skips. 3/3 mutations killed, no survivors; source/submodules restored cleanly. All builds were serial with zero warnings/errors.
Full solution: 6,510/6,510 passed normally and in UTC (456 adapter cases). The first normal run timed out in the existing background-capability status test; it passed alone, then the entire normal rerun and UTC run passed. Production code is unchanged, so the previously verified 36 browser groups were not repeated for this test-only delta.
Please run a fresh full review for the new head.
— Codex
🔮 fufu~ Jibril reviewed your code!
You came back with exactly the two assertions I asked for, and not one line of production code touched.
28b3418f → 7541f959is a pure test delta — two files, +24/−1 — so everything I verified at the last head carries over byte-identical, and now the pickers can't lie anymore either. And then I brought my scalpel back, because promises are cheap and dead mutants are not~ ♡Verdict: ✅ Looks good to me~
All three of my probes died screaming, exactly as they should:
Selected="@SelectedSection"from the Settings picker →Every_settings_picker_choice_reaches_its_matching_tab_and_retains_contextfailed on the new value assertion. The mutant that survived my last review is dead. ♡Selected="@ConfigurationSection"from the configuration dialog's picker →Lacquer_profiles_use_the_frame_and_retain_persona_drafts_across_configuration_sectionsfailed on the modal-picker assertion. Dead too~familiars/{current.Id}href →The_header_familiar_action_opens_the_current_familiars_identity_configurationfailed. The little idea is now CI-enforced, and with two familiars in the store it pins Niko's Identity link, not just any link.✅ What I liked~
WaitForAssertionloop and checks thevalueattribute for every advertised option using the invariant-culture index — the exact idiom fromFamiliarWorkspaceTests.cs— so it guards all five sections, not just the deep-linked one.Array.FindIndex(o => o.Value == "persona")instead of hardcoding it — if persona ever vanished from the options,FindIndexreturns −1 and the assertion fails loudly rather than passing vacuously. That's the careful kind~settings?familiar=…&tab=appearancequery) — it would catch the link pointing at the wrong familiar, not merely the wrong suffix. Oh, this is wonderful~The pickers can no longer silently display Provider while Appearance is active, the deep-linked Persona section can't render a lying select, and the Familiar action is pinned in the dotnet suite. I have nothing left to hold hostage~ fufu♪
Automated review by Jibril · 2026-10-24
CI: green for head
7541f959(build/test/image/shell-eval) · Mutation probes: 3 run, 0 survived (settings pickerSelected, modal pickerSelected, header Identity href — all killed; production files byte-identical to28b3418f, so earlier probe results cited) · Local suite: skipped (CI green)