Skip to content

One thread composer (shared mention model + speech), un-shadowed type-node URLs, colorful SVG on native - #2140

Merged
rbuergi merged 5 commits into
mainfrom
feat/one-thread-composer
Aug 24, 2026
Merged

rbuergi merged 5 commits into
mainfrom
feat/one-thread-composer

Conversation

@rbuergi

@rbuergi rbuergi commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

Follow-up to #2129, built live with the maintainer driving the app. Three strands:

1. ONE thread composer — the core model, everywhere

  • useMentionModel in @meshweaver/react core: the platform-free @-mention half of the composer (caret token tracking, debounced generation-guarded MeshOps.autocomplete fetch, highlight, splice-on-pick). The web ThreadChat leaf consumes it (no behavior change); the new RN ComposerBar is the native skin — dropdown, mic (tap-dictate + hold-PTT via the existing PushToTalkController), glyph send.
  • Mesh.autocomplete in @meshweaver/client-web: THE one AutocompleteRequest implementation (portal + portal-next each hand-rolled the same observe — the Three shells hand-roll the same four /api/mesh fetches — the drift generator behind #1474 (token, error semantics and basePath already differ) #1497 drift shape), targeted at the viewer's home hub. RN wires it through a partition thunk (a signed-in remote's identity resolves via whoami after connect).
  • The app-level "Message the mesh…" bar is removed — the composer is the mesh-rendered ThreadChatControl, same as Blazor. Verified live: typing @Doc against prod opens real mesh suggestions.

2. Store categories were dead on EVERY client — including prod Blazor

Clickable stacks now post ClickedEvent in both JS packs (the tiles are isClickable stacks — inert before), MeshWebConnection.onMessage delivers the server's unsolicited NavigationRequest (the client twin of the Blazor portal hub's WithHandler), and the RN app resolves the uri via POST /api/mesh/resolve into address/area/id (query rides in the Id, area-prefixed — the LayoutAreaReference.GetParameterValue contract).

That exposed the real root cause, reproduced on prod Blazor (/Store/Catalog/Catalog?category=Games → "Collection ?category=Games is not mapped in Address Store/Catalog"): a NodeType DEFINITION node shadows same-named AREA URLs in ResolveNavigationPath — Store/Catalog (the type node) steals the prefix of the Store node's Catalog area. Fixed with a navigation-only RewriteTypeNodeShadow (routing/reads stay literal; exact navigation TO the type node untouched), pinned by TypeNodeShadowResolutionTest ×3; adjacent nav regressions re-run green ×15. Coordinated with #2137 (content-visibility): predicate stays the narrow NodeType=="NodeType" for now; the comment names the agreed widening path.

3. Colorful SVG on native

One IconGlyph (shared classifyIcon → SvgXml/SvgUri/Image/emoji, instance-resolved URLs) for the Icon leaf and the left-menu rows (which inlined /static/….svg into the label); doc pages interleave standalone inline <svg> diagrams through SvgXml (previously in ignoredDomTags — every diagram silently dropped on a device).

i18n: chat.recording/chat.transcribing in en+de, server catalogs + react mirror (drift guard ✓, LocalizationTest 56 ✓).

Verification: grpc-web 82 ✓ · react 293 ✓ · RN 164 ✓ · RN Playwright e2e ✓ · Hosting + Messaging.Hub Release -warnaserror clean · Store category click verified end-to-end against prod (web) · home/composer/mic verified on the iOS simulator against the sidecar.

What's New entry included (Category: Fix).

🤖 Generated with Claude Code

rbuergi and others added 4 commits August 24, 2026 13:14
…complete, RN parity, app bar retired

WIP checkpoint of the composer consolidation (branch continues):

- @meshweaver/client-web: Mesh.autocomplete — THE one AutocompleteRequest
  implementation (portal + portal-next each hand-rolled the same observe; the
  #1497 drift shape), targeted at the viewer's home hub; [] on any failure.
  Fake-transport responder + tests.
- @meshweaver/react core: useMentionModel — the platform-free @-mention half
  of the composer (token tracking, debounced generation-guarded fetch,
  highlight, splice-on-pick). The web ThreadChat leaf now consumes it (same
  behavior, no duplicate logic); exported via core.
- RN: ComposerBar — the native skin over the same model (dropdown, mic with
  tap-dictate + hold-PTT via the existing PushToTalkController, glyph send),
  mounted by the ThreadChat leaf; buildMeshOps wires autocomplete (partition
  thunk: a remote's identity resolves via whoami AFTER connect) and userId
  became a getter for the same reason. The app-level "Message the mesh…" bar
  is REMOVED — the composer is the mesh-rendered ThreadChatControl, same as
  Blazor.
- Clickable STACKS post the ClickedEvent in BOTH JS packs (web DefaultStackSkin
  + RN stack skin) — Blazor's LayoutStack behavior; the Store's category tiles
  were inert cards in every JS shell without it. (The server answers with a
  NavigationRequest the JS clients don't consume yet — next commit.)
- i18n: chat.recording / chat.transcribing in both server catalogs + the react
  mirror (drift guard green).

grpc-web 81 ✓, react 293 ✓, RN 164 ✓; typechecks + react build green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…lients; colorful SVG on native

Completes the composer/navigation change set (first half in the previous
commit):

- RESOLVER (the root cause behind dead Store categories, REPRODUCED ON PROD
  BLAZOR): a NodeType DEFINITION node shadows same-named AREA URLs —
  Store/Catalog (the type node) stole /Store/Catalog/Catalog?category=… from
  the Store node's Catalog area, so the framework type-catalog answered
  "Collection ?category=… is not mapped" on every client. Navigation-only
  RewriteTypeNodeShadow (same scope rule as RewriteLegacyUserHome: routing +
  reads literal, exact navigation TO the type node untouched), pinned by
  TypeNodeShadowResolutionTest ×3; adjacent nav regressions (legacy user home,
  redirects) re-run green ×15. Predicate deliberately narrow
  (NodeType=="NodeType"); the comment names the #2137 widening path
  (ExcludeFromContext / registration set) per coordination with that branch.
- NavigationRequest lands on JS clients: MeshWebConnection.onMessage
  (unsolicited typed deliveries — the client twin of the Blazor portal hub's
  WithHandler), and the RN app resolves the uri server-side
  (POST /api/mesh/resolve, portal-next's fetchAreaTarget twin) into a
  NavTarget with area/id — the query rides in the Id, prefixed by the area
  name (the LayoutAreaReference.GetParameterValue contract; a bare "?…" id
  reads as a data-collection id and fails).
- Colorful SVG on native: ONE IconGlyph (classifyIcon → SvgXml / SvgUri /
  Image / emoji, instance-resolved URLs) used by the Icon leaf and the
  left-menu rows (which had inlined "/static/….svg" into the LABEL); doc
  pages interleave standalone inline <svg> diagrams through SvgXml (they were
  in ignoredDomTags — every diagram silently dropped on a device).
- i18n: chat.recording / chat.transcribing (en+de, server + react mirror);
  LocalizationTest 56 ✓, drift guard ✓.

grpc-web 82 ✓, react 293 ✓, RN 164 ✓; Hosting + Messaging.Hub Release
-warnaserror clean; verified live: Store category click end-to-end vs prod
(web) and the home/composer on the iOS simulator vs the sidecar.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Copilot AI lite review requested due to automatic review settings August 24, 2026 11:37
@github-actions

github-actions Bot commented Aug 24, 2026 •

Copy link
Copy Markdown
Contributor

Test Results (shard 3)

    9 files  ±0      9 suites  ±0   5m 3s ⏱️ +3s
1 679 tests ±0  1 674 ✅ ±0  5 💤 ±0  0 ❌ ±0 
2 178 runs  +1  2 173 ✅ +1  5 💤 ±0  0 ❌ ±0 

Results for commit 6eee647. ± Comparison against base commit 3cdcad4.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Aug 24, 2026 •

Copy link
Copy Markdown
Contributor

Test Results (shard 0)

1 262 tests  ±0   1 262 ✅ ±0   11m 41s ⏱️ - 4m 49s
    9 suites ±0       0 💤 ±0 
    9 files   ±0       0 ❌ ±0 

Results for commit 6eee647. ± Comparison against base commit 3cdcad4.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Aug 24, 2026 •

Copy link
Copy Markdown
Contributor

Test Results (shard 5)

   10 files  ±0     10 suites  ±0   7m 15s ⏱️ + 2m 35s
1 254 tests ±0  1 253 ✅ ±0  1 💤 ±0  0 ❌ ±0 
1 255 runs  ±0  1 254 ✅ ±0  1 💤 ±0  0 ❌ ±0 

Results for commit 6eee647. ± Comparison against base commit 3cdcad4.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Aug 24, 2026 •

Copy link
Copy Markdown
Contributor

Test Results (shard 4)

2 140 tests  ±0   1 841 ✅ +1   10m 29s ⏱️ +39s
    9 suites ±0     299 💤 ±0 
    9 files   ±0       0 ❌  - 1 

Results for commit 6eee647. ± Comparison against base commit 3cdcad4.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Aug 24, 2026 •

Copy link
Copy Markdown
Contributor

Test Results (shard 2)

3 287 tests   - 18   3 287 ✅  - 18   7m 54s ⏱️ +9s
    8 suites ± 0       0 💤 ± 0 
    8 files   ± 0       0 ❌ ± 0 

Results for commit 6eee647. ± Comparison against base commit 3cdcad4.

This pull request removes 38 and adds 20 tests. Note that renamed tests count towards both.
MeshWeaver.Hosting.Monolith.Test.TypedContentUpdateTest ‑ TypedUpdate_AbsentContent_YieldsNullSoTheCallerCanInitialise
MeshWeaver.Hosting.Monolith.Test.TypedContentUpdateTest ‑ TypedUpdate_JsonContent_ConvertsAndPreservesUntouchedFields
MeshWeaver.Hosting.Monolith.Test.TypedContentUpdateTest ‑ TypedUpdate_UnconvertibleContent_FaultsAndLeavesTheNodeUntouched
MeshWeaver.Hosting.Monolith.Test.TypedContentUpdateTest ‑ TypedUpdate_UnconvertibleContent_FaultsOnTheCrossHubPathToo
MeshWeaver.Hosting.Monolith.Test.TypedContentUpdateTest ‑ TypedUpdate_UnresolvableJsonContent_ConvertsAndPreservesUntouchedFields
MeshWeaver.Hosting.Monolith.Test.TypedContentUpdateTest ‑ UntypedUpdate_WithTheAsOrDefaultIdiom_IsRescuedByTheExactContentTypeRoute
MeshWeaver.Hosting.Monolith.Test.UserActivityAreaTest ‑ ActivityArea_CanBeResolved_ForUserRoland
MeshWeaver.Hosting.Monolith.Test.UserActivityAreaTest ‑ ActivityArea_WorksForRuntimeCreatedUser
MeshWeaver.Hosting.Monolith.Test.UserActivityAreaTest ‑ EditProfileArea_CanBeResolved_ForUserRoland
MeshWeaver.Hosting.Monolith.Test.UserActivityAreaTest ‑ OverviewArea_CanBeResolved_ForUserRoland
…
MeshWeaver.Hosting.Monolith.Test.TypeNodeShadowResolutionTest ‑ AreaUrlShadowedByTypeNode_ResolvesToParentWithFullRemainder
MeshWeaver.Hosting.Monolith.Test.TypeNodeShadowResolutionTest ‑ SharedResolvePath_DoesNotUnshadow_PreservesReadRouteInvariant
MeshWeaver.Hosting.Monolith.Test.TypeNodeShadowResolutionTest ‑ TypeNodeItself_StillResolvesDirectly
MeshWeaver.Hosting.Monolith.Test.UpsertInnerCreateObservationTest ‑ SelfTargetedCreate_OnTheMeshHub_DropsItsReply_WhenTheSubjectIsRegisteredAfterPost
MeshWeaver.Hosting.Monolith.Test.UpsertInnerCreateObservationTest ‑ Upsert_OfAMissingNode_Answers_ThroughTheInnerCreate
MeshWeaver.Hosting.Monolith.Test.UserDirectoryContentShapeTest ‑ EveryContentShape_IsIndexedAndProjects(shape: \"JsonElement\")
MeshWeaver.Hosting.Monolith.Test.UserDirectoryContentShapeTest ‑ EveryContentShape_IsIndexedAndProjects(shape: \"JsonObject\")
MeshWeaver.Hosting.Monolith.Test.UserDirectoryContentShapeTest ‑ EveryContentShape_IsIndexedAndProjects(shape: \"typed\")
MeshWeaver.Hosting.Monolith.Test.UserIdentityLeakTest ‑ AccessService_Context_ReturnsOnlyAsyncLocal
MeshWeaver.Hosting.Monolith.Test.UserIdentityLeakTest ‑ ActivityArea_OwnerSeesPersonalDashboard
…

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Aug 24, 2026 •

Copy link
Copy Markdown
Contributor

Test Results (shard 1)

1 769 tests  +21   1 769 ✅ +21   9m 8s ⏱️ +18s
   10 suites ± 0       0 💤 ± 0 
   10 files   ± 0       0 ❌ ± 0 

Results for commit 6eee647. ± Comparison against base commit 3cdcad4.

This pull request removes 17 and adds 38 tests. Note that renamed tests count towards both.
MeshWeaver.Hosting.Monolith.Test.UpsertInnerCreateObservationTest ‑ SelfTargetedCreate_OnTheMeshHub_DropsItsReply_WhenTheSubjectIsRegisteredAfterPost
MeshWeaver.Hosting.Monolith.Test.UpsertInnerCreateObservationTest ‑ Upsert_OfAMissingNode_Answers_ThroughTheInnerCreate
MeshWeaver.Hosting.Monolith.Test.UserDirectoryContentShapeTest ‑ EveryContentShape_IsIndexedAndProjects(shape: \"JsonElement\")
MeshWeaver.Hosting.Monolith.Test.UserDirectoryContentShapeTest ‑ EveryContentShape_IsIndexedAndProjects(shape: \"JsonObject\")
MeshWeaver.Hosting.Monolith.Test.UserDirectoryContentShapeTest ‑ EveryContentShape_IsIndexedAndProjects(shape: \"typed\")
MeshWeaver.Hosting.Monolith.Test.UserIdentityLeakTest ‑ AccessService_Context_ReturnsOnlyAsyncLocal
MeshWeaver.Hosting.Monolith.Test.UserIdentityLeakTest ‑ ActivityArea_OwnerSeesPersonalDashboard
MeshWeaver.Hosting.Monolith.Test.UserIdentityLeakTest ‑ ActivityArea_ShowsNodeOwnerName_NotViewerName
MeshWeaver.Hosting.Monolith.Test.UserIdentityLeakTest ‑ ActivityArea_VisitorSeesPublicProfile
MeshWeaver.Hosting.Monolith.Test.ViewerTimeZoneRenderTest ‑ CircuitViewerZone_ReachesTheOwnerSideRender(area: \"ClockRollover\", expected: \"2026-07-30 01:30\")
…
MeshWeaver.Hosting.Monolith.Test.TypedContentUpdateTest ‑ TypedUpdate_AbsentContent_YieldsNullSoTheCallerCanInitialise
MeshWeaver.Hosting.Monolith.Test.TypedContentUpdateTest ‑ TypedUpdate_JsonContent_ConvertsAndPreservesUntouchedFields
MeshWeaver.Hosting.Monolith.Test.TypedContentUpdateTest ‑ TypedUpdate_UnconvertibleContent_FaultsAndLeavesTheNodeUntouched
MeshWeaver.Hosting.Monolith.Test.TypedContentUpdateTest ‑ TypedUpdate_UnconvertibleContent_FaultsOnTheCrossHubPathToo
MeshWeaver.Hosting.Monolith.Test.TypedContentUpdateTest ‑ TypedUpdate_UnresolvableJsonContent_ConvertsAndPreservesUntouchedFields
MeshWeaver.Hosting.Monolith.Test.TypedContentUpdateTest ‑ UntypedUpdate_WithTheAsOrDefaultIdiom_IsRescuedByTheExactContentTypeRoute
MeshWeaver.Hosting.Monolith.Test.UserActivityAreaTest ‑ ActivityArea_CanBeResolved_ForUserRoland
MeshWeaver.Hosting.Monolith.Test.UserActivityAreaTest ‑ ActivityArea_WorksForRuntimeCreatedUser
MeshWeaver.Hosting.Monolith.Test.UserActivityAreaTest ‑ EditProfileArea_CanBeResolved_ForUserRoland
MeshWeaver.Hosting.Monolith.Test.UserActivityAreaTest ‑ OverviewArea_CanBeResolved_ForUserRoland
…

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Aug 24, 2026 •

Copy link
Copy Markdown
Contributor

Test Results

    55 files  ±0      55 suites  ±0   51m 33s ⏱️ - 1m 2s
11 391 tests +3  11 086 ✅ +3  305 💤 ±0  0 ❌ ±0 
11 891 runs  +4  11 586 ✅ +4  305 💤 ±0  0 ❌ ±0 

Results for commit 6eee647. ± Comparison against base commit 3cdcad4.

♻️ This comment has been updated with latest results.

Copilot stopped reviewing on behalf of rbuergi due to an error August 24, 2026 11:57
@rbuergi
rbuergi merged commit 9d20d65 into main Aug 24, 2026
46 of 48 checks passed
rbuergi added a commit that referenced this pull request Aug 24, 2026
…d globalThis.expo at import

Conflict resolutions (per the cross-session analysis + verified by running):

- package.json: the 57 line wholesale, KEEPING main's newer non-Expo pins
  (marked ^18.0.10, react-native-svg 15.15.5, vitest ^4.1.11); lockfile
  regenerated, never hand-merged.
- App.tsx: main's side — the app-level chat bar was deleted (#2140); the
  composer lives in the ThreadChat leaf. rnComposer adopts the recorder's
  57-era name (ExpoAudioRecorder).
- vitest.config.ts: the branch's premise — "expo-constants and friends must
  keep resolving to the real packages" — is EMPIRICALLY WRONG once code under
  test imports them (post-#2140 the connection/composer paths do): SDK 57's
  entries still read `globalThis.expo.EventEmitter` (expo-modules-core) at
  import, which only a real Expo runtime provides. Main's per-package mocks
  return (minus expo-av, which is gone on 57) plus a new expo-audio mock for
  the composer's recorder.

Verified on the merged tree: typecheck ✓, RN 168 ✓, web-export Playwright
e2e 5 ✓.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@rbuergi
rbuergi deleted the feat/one-thread-composer branch August 28, 2026 08:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant