Skip to content

feat(approvals): Approvals rides the module lane — and its views drop hand-built HTML - #1654

Merged
rbuergi merged 3 commits into
mainfrom
feat/approvals-module
Aug 16, 2026
Merged

rbuergi merged 3 commits into
mainfrom
feat/approvals-module

Conversation

@rbuergi

@rbuergi rbuergi commented Aug 16, 2026

Copy link
Copy Markdown
Contributor

What moved

src/MeshWeaver.Approvals/ is a new module project (OgCard/MarkdownExport precedent), taking the four approval sources out of MeshWeaver.Graph:

File Was
ApprovalsView.cs src/MeshWeaver.Graph/ApprovalsView.cs
ApprovalLayoutAreas.cs src/MeshWeaver.Graph/ApprovalLayoutAreas.cs
ApprovalExtensions.cs src/MeshWeaver.Graph/ApprovalExtensions.cs
ApprovalNodeType.cs src/MeshWeaver.Graph/Configuration/ApprovalNodeType.cs

Activation is the module lane: an assembly-level ApprovalsModuleAttribute : MeshNodeProviderAttribute whose single BuilderConfigurations hook invokes the same public AddApprovals(this MeshBuilder) a test fixture calls — one code path for both lanes, so they cannot drift (the MarkdownExportProviderAttribute shape; the brief's decomposed Nodes/DefaultNodeHubConfigurations split would have re-opened exactly the drift the shared-path rule forbids, and MeshNodeProviderAttribute.BuilderConfigurations' own doc names this case). MeshWeaver.Approvals.dll is listed in Modules:Assemblies of both portals (Monolith + Distributed) and referenced from Memex.Portal.Shared so the DLL rides the publish output.

What stayed platform-level

  • Approval/ApprovalStatus records (MeshWeaver.Mesh.Contract/Approval.cs) — the type-registry seeding (MeshNodeExtensions.WithGraphTypes) and SatelliteTableMapping.Defaults ("_Approval","annotations","Approval") depend on them, so stored approvals keep deserializing and routing with the module delisted.
  • The "Approval" => "annotations" query-routing rule in GraphConfigurationExtensions (wire literal, storage routing).
  • A 3-line marker in Graph (ApprovalsMarker.cs: ApprovalsEnabled + HasApprovals()): the module's per-node-hub registration sets the marker; MarkdownOverviewLayoutArea keeps guarding its embedded Approvals section with it, so the section self-suppresses when the module is delisted.

Mesh-wide instead of Markdown-only — deliberate

AddApprovals used to hang off the Markdown NodeType's HubConfiguration only. The module registers via ConfigureDefaultNodeHub on EVERY per-node hub, so approvals can be requested on any node type. The registrations removed from Graph: GraphConfigurationExtensions.AddGraph()'s .AddApprovalType(), MarkdownNodeType's .AddApprovals(), and AddApprovalViews() moved with the node type.

The Controls.Html violations are fixed in the move

All hand-built HTML strings in the approval views are gone (the banned Controls.Html($"<div><strong>…") shape), replaced with typed controls rendering the same information in the same order:

  • Headers → Controls.H2/Controls.H3; form labels → Controls.Body (bold).
  • Overview detail rows (Status/Requester/Approver/Purpose/Due/Decision Date/Created) → horizontal Controls.Stack label+value rows; status → Controls.Badge with the original status colors.
  • "Go to document" <a> → Controls.NavLink.
  • Thumbnail card rows → Stack + Badge + Body; the private EscapeHtml is deleted (typed controls escape text by nature). One rendering nuance: the connector in "Approved by: X on date" became a language-neutral "·" separator (same fields, untranslatable English word dropped).
  • Content is Approval casts → ContentAs<Approval>() (the trap-door rule), so a degraded JsonElement payload still renders instead of silently dropping.

While rewriting those lines, the previously hard-coded English strings were localized: 19 new approval.* keys in strings.{en,de}.json, mirrored byte-identically into clients/react/src/i18n/ (drift guard: localize.test.ts); menu.requestApproval/ui.approve/ui.reject/ui.goToDocument reused.

Fallback story

Delisting MeshWeaver.Approvals.dll removes the Approvals UI mesh-wide (menu entry, form, inline section, approval Overview/Thumbnail); the markdown overview's embed self-suppresses via the marker; approval data, its typing, and its satellite routing stay intact and come back when the module is re-listed. Swept repo (content/, samples/*/Data, clients/, memex/) and the live mesh (search/search_chunks) — zero external consumers of the moved symbols or area names.

Tests

  • test/MeshWeaver.Graph.Test/ApprovalModuleTest.cs — the module-surface tests split out of ApprovalAndNotificationTest.cs (which keeps the platform-level data-model tests), plus a pin that ConfigureHub sets the ApprovalsEnabled marker the overview guard reads.
  • test/Memex.Portal.Shared.Test/ApprovalsModuleTest.cs — module-shape pin (assembly carries MeshNodeProviderAttribute with a builder registration), mirroring OgCardModuleTest.

Verification (all -c Release -warnaserror, one project per invocation)

  • Builds clean (0 warnings, 0 errors): MeshWeaver.Approvals, MeshWeaver.Graph (--no-incremental), Memex.Portal.Shared, Memex.Portal.Monolith, MeshWeaver.Graph.Test, Memex.Portal.Shared.Test, MeshWeaver.Hosting.Monolith.Test, MeshWeaver.Documentation.Test.
  • MeshWeaver.Graph.Test: Passed 1161/1161 (fresh .trx; the 5 new module tests executed).
  • Memex.Portal.Shared.Test: Passed 362/362 (pin test executed).
  • MeshWeaver.Hosting.Monolith.Test --filter "FullyQualifiedName~DocumentExport|FullyQualifiedName~Markdown": Passed 19/19 (the overview-embed path with the module absent from fixtures — the marker guard suppresses the section, matching delisted behavior).
  • MeshWeaver.Documentation.Test DocumentationLinkIntegrityTest: Passed 1/1 (new What's New page).
  • MeshWeaver.Messaging.Hub.Test --filter "FullyQualifiedName~LocalizationTest": Passed 56/56 (catalog completeness with the 19 new keys).
  • React i18n mirror verified byte-identical to the server catalogs after the key additions (the localize.test.ts drift guard asserts exactly this equality).
  • MeshWeaver.Approvals.dll confirmed present in the Monolith portal's publish output (rides Memex.Portal.Shared).
  • What's New entry: 2026-08-16-approvals-module.md (Category: Feature).

🤖 Generated with Claude Code

Approval workflows move out of MeshWeaver.Graph into a new module project
(the OgCard/MarkdownExport precedent): an assembly-level ApprovalsModuleAttribute
whose single BuilderConfigurations hook invokes the same public
AddApprovals(MeshBuilder) a fixture calls, so the Modules:Assemblies lane and
the compiled-in lane share one code path. Registration is now mesh-wide
(ConfigureDefaultNodeHub) instead of Markdown-only — approvals can be requested
on any node type — and both portals list MeshWeaver.Approvals.dll.

Stays platform-level: the Approval record + type-registry seeding + the
_Approval → annotations satellite mapping (stored approvals keep deserializing
and routing while the module is delisted), and a 3-line ApprovalsEnabled marker
in Graph so MarkdownOverviewLayoutArea's embedded section self-suppresses when
the module is absent.

Fixed during the move: every hand-built Controls.Html string in the approval
views is replaced with typed controls (H2/H3/Body/Badge/NavLink/Stack rows;
the private EscapeHtml is deleted), Content-is casts become ContentAs<Approval>,
and the previously hard-coded English strings are localized (19 approval.* keys,
mirrored byte-identically into clients/react/src/i18n).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Copilot AI lite review requested due to automatic review settings August 16, 2026 08:08

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR extracts Approvals from MeshWeaver.Graph into a new MeshWeaver.Approvals module that can be activated via Modules:Assemblies, and updates the approval UI to use typed controls instead of hand-built HTML while expanding approvals registration to all per-node hubs.

Changes:

  • Introduces src/MeshWeaver.Approvals/ as a module project with an assembly-level MeshNodeProviderAttribute registration hook.
  • Moves/rewires approvals node type + views + hub registration into the module, leaving only a small marker/guard surface in MeshWeaver.Graph.
  • Adds approval.* localization keys on server and mirrors them into the React i18n catalogs; updates portal appsettings to load the module.

Reviewed changes

Copilot reviewed 24 out of 24 changed files in this pull request and generated no comments.

Show a summary per file
File Description
test/MeshWeaver.Graph.Test/MeshWeaver.Graph.Test.csproj Adds module project reference for new module-surface tests.
test/MeshWeaver.Graph.Test/ApprovalModuleTest.cs New tests pin module registration surfaces and marker behavior.
test/MeshWeaver.Graph.Test/ApprovalAndNotificationTest.cs Narrows scope to platform-level models; moves module assertions to new test class.
test/Memex.Portal.Shared.Test/ApprovalsModuleTest.cs Pins module-lane activation via assembly attribute + builder registration.
src/MeshWeaver.Messaging.Hub/Localization/strings.en.json Adds English approval.* localization keys.
src/MeshWeaver.Messaging.Hub/Localization/strings.de.json Adds German approval.* localization keys.
src/MeshWeaver.Graph/MeshWeaver.Graph.csproj Grants InternalsVisibleTo for module to reuse internal layout plumbing.
src/MeshWeaver.Graph/Configuration/MarkdownNodeType.cs Removes Markdown-only approvals wiring (now module-provided).
src/MeshWeaver.Graph/Configuration/GraphConfigurationExtensions.cs Removes graph-level approval type registration (now module-provided).
src/MeshWeaver.Graph/ApprovalsMarker.cs Adds a Graph-level marker/guard API to let embedders self-suppress when module is delisted.
src/MeshWeaver.Documentation/Data/WhatsNew/2026-08-16-approvals-module.md Adds “What’s New” entry documenting module activation and scope changes.
src/MeshWeaver.Approvals/README.md Documents module purpose, activation, and data storage behavior.
src/MeshWeaver.Approvals/MeshWeaver.Approvals.csproj New module project definition referencing Graph.
src/MeshWeaver.Approvals/ApprovalsView.cs Updates request/inline views to typed controls and localized labels; removes hand-built HTML.
src/MeshWeaver.Approvals/ApprovalsModuleAttribute.cs Adds assembly-level module registration attribute.
src/MeshWeaver.Approvals/ApprovalNodeType.cs Moves approval node type definition into module.
src/MeshWeaver.Approvals/ApprovalLayoutAreas.cs Moves approval overview/thumbnail areas into module and replaces HTML with typed controls.
src/MeshWeaver.Approvals/ApprovalExtensions.cs Centralizes mesh + per-node-hub registration under AddApprovals/module hook.
MeshWeaver.slnx Adds the new module project to the solution.
memex/Memex.Portal.Shared/Memex.Portal.Shared.csproj References module so it rides publish output.
memex/Memex.Portal.Monolith/appsettings.json Adds MeshWeaver.Approvals.dll to module assemblies list.
memex/aspire/Memex.Portal.Distributed/appsettings.json Adds MeshWeaver.Approvals.dll to module assemblies list.
clients/react/src/i18n/strings.en.json Mirrors new English approval keys for React drift guard.
clients/react/src/i18n/strings.de.json Mirrors new German approval keys for React drift guard.
Suppressed comments (1)

src/MeshWeaver.Approvals/ApprovalsView.cs:82

  • The due-date placeholder is a specific calendar date, which can become misleading over time and doesn’t match the localized “YYYY-MM-DD” format hint. Prefer a format placeholder so the UI stays correct regardless of current date.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@github-actions

github-actions Bot commented Aug 16, 2026 •

Copy link
Copy Markdown
Contributor

Test Results (shard 0)

914 tests   913 ✅  10m 26s ⏱️
 10 suites    1 💤
 10 files      0 ❌

Results for commit 1f7ae0d.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Aug 16, 2026 •

Copy link
Copy Markdown
Contributor

Test Results (shard 4)

1 542 tests   1 536 ✅  7m 20s ⏱️
   11 suites      6 💤
   11 files        0 ❌

Results for commit 1f7ae0d.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Aug 16, 2026 •

Copy link
Copy Markdown
Contributor

Test Results (shard 5)

1 367 tests   1 366 ✅  6m 11s ⏱️
   11 suites      1 💤
   11 files        0 ❌

Results for commit 1f7ae0d.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Aug 16, 2026 •

Copy link
Copy Markdown
Contributor

Test Results (shard 3)

   11 files     11 suites   6m 25s ⏱️
2 195 tests 2 004 ✅ 191 💤 0 ❌
2 554 runs  2 363 ✅ 191 💤 0 ❌

Results for commit 1f7ae0d.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Aug 16, 2026 •

Copy link
Copy Markdown
Contributor

Test Results (shard 1)

2 178 tests   2 075 ✅  7m 30s ⏱️
   11 suites    103 💤
   11 files        0 ❌

Results for commit 1f7ae0d.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Aug 16, 2026 •

Copy link
Copy Markdown
Contributor

Test Results (shard 2)

2 367 tests   2 363 ✅  7m 46s ⏱️
   11 suites      4 💤
   11 files        0 ❌

Results for commit 1f7ae0d.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Aug 16, 2026 •

Copy link
Copy Markdown
Contributor

Test Results

    65 files      65 suites   45m 42s ⏱️
10 563 tests 10 257 ✅ 306 💤 0 ❌
10 922 runs  10 616 ✅ 306 💤 0 ❌

Results for commit 1f7ae0d.

♻️ This comment has been updated with latest results.

rbuergi and others added 2 commits August 16, 2026 10:35
…e into the module

Both PR reds shared one root cause: CreateNode's NodeType-existence check
(MeshExtensions step 3) refuses NodeType 'Approval' in any mesh that does not
activate the Approvals module, now that AddGraph no longer registers it.

- Shard 5: ThreadUrlResolutionTest's _Approval theory case creates an Approval
  satellite through the guarded CreateNode — the fixture now opts in via the
  module's builder extension (.AddApprovals()), like the portals do via
  Modules:Assemblies. (MeshWeaver.Approvals arrives transitively through the
  project's existing Memex.Portal.Shared reference.)
- Doc content gate: the SAMPLES stage failed on 'install: FutuRe' — the tree
  ships two Approval satellites (…/TransactionMapping/MappingRules/_Approval/a1)
  whose install the tester's mesh refused for the same reason. The tester's
  portal-shaped composition now calls .AddApprovals(), the exact precedent of
  its .AddAI(...) line ('a portal always registers these; without them those
  installs are refused'). The gate is untouched — no allow-list entry added.
  (The reported 'digest-mismatch: error' was a red herring: it is the
  download-artifact step echoing its own config option, and the digest matched.)

Verified: ThreadUrlResolutionTest 20/20 passed (incl. the exact failing case);
samples stage GREEN locally (FutuRe installs 145 files; only the 8 pre-existing
known-debt entries stay red); doc stage ALL GREEN; both touched projects build
clean with -c Release -warnaserror.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…chrome areas

Shard-4 red on PR #1654: GoodPackage_CompilesRendersAndExecutesTestsGreen saw
TestsDetail = 'all rendered cases green' instead of '1/1 passed'. Root cause:
RenderMenus is a GLOBAL predicate renderer that writes the node menu into
EVERY layout-area store under $Menu:{context}, and the Approvals module's
menu entry carries the ✅ emoji as its Icon — AreaProbe.ClassifyTestsFrame
walked ALL strings in the frame, so an early menu-bearing frame (permissions
resolve fast; the Tests markdown renders later) matched the '✅ without a
summary' rule and Take(1) latched the premature verdict. A ❌-shaped icon
would have failed a green run the same way. A race, hence CI-only.

Fix at the defect: CollectStrings skips the reserved '$'-prefixed chrome
areas ($Menu, $Menu:{context}, $Dialog, $NavMenu) — chrome strings are
labels and icons, never test output — covering both the wire key encoding
('"$Menu"') and the synthetic-seam plain keys. The Approvals icon stays ✅
(deliberate, reads as sign-off in any locale); no assertion weakened.

Deterministic repro pinned: AreaProbeTest.MenuChromeFrame_ThenGreenTable
replays the exact CI frame order (chrome first, content second) and demands
the real 'N/M passed' detail.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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.

2 participants