Skip to content

feat(settings): Inbox, Invitations and token rows get their own buttons back — row-scoped actions (B3) - #5967

Merged
rbuergi merged 8 commits into
mainfrom
feat/row-scoped-b3-settings
Oct 3, 2026
Merged

rbuergi merged 8 commits into
mainfrom
feat/row-scoped-b3-settings

Conversation

@rbuergi

@rbuergi rbuergi commented Oct 1, 2026

Copy link
Copy Markdown
Contributor

Row-scoped actions for the B3 settings tabs (#5955): the per-row buttons that data-binding B3 part 2 (#5947) had to downgrade to "select the row, then press the button below the grid" are back — Archive on each Inbox mail, Revoke on each invitation, Rotate and Revoke on each service-identity token.

Stacked: base is feat/databinding-b3-settings (#5947 → #5944), and the branch carries #5955's commit cherry-picked so it builds. Draft until #5955 and the B3 chain land on main; then rebase onto main (the cherry-picked commit drops out as already applied). Sibling of #5956 (the coupon list, on #5948); both edit the same DataBinding.md bullet, so whichever lands second resolves that one paragraph.

What changed

  • InboxSettingsTab.MailGrid, InvitationsSettingsTab.InvitationGrid, ServiceIdentitiesSettingsTab.TokenGrid (the template half, split from the feeds and taking the action, so the row routing is testable): each action is ONE button in a TemplateColumnControl, and its click reads the row with ctx.RowAs<TRow>() — the row as the person saw it, never re-read by position. A click with no row does nothing.
  • Gone: the three selection /data slots, the bound selection labels, the grid-level DataGridCellClick handlers, the Display / None row members, and the "select a row first" hints.
  • An action that applies only to some rows (an archived mail, an accepted or revoked invitation, a revoked token) refuses on the result line and says why — three new keys (inbox.alreadyArchived, invitations.notPending, serviceIdentities.tokenRevoked, en + de) replace the three hint keys, which feat(settings): seven settings tabs are templates — fed grids and markdown (data-binding B3, part 2) #5947 added and nothing else uses.
  • #5948 (coupon / registration-key / instance-grant lists) checked: its only downgraded per-row action is the coupon Open button, which feat(plugincatalog): the coupon list's per-row Open button is back — a row-scoped action (reference conversion) #5956 restores; the registration-key and instance-grant revoke buttons are forms keyed by id and were not downgraded.
  • DataBinding.md: the per-row-action bullet describes the row-scoped shape with these tabs as its examples; the "platform gap" sentence says the gap is closed.

Evidence

  • test/Memex.Portal.Shared.Test/SettingsRowActionsActOnTheClickedRowTest (12/12, a theory over the four buttons): row k's button acts on row k for every k; the list changes between render and click (rows inserted above, the clicked one gone) and the click still acts on the clicked row; negative control — the same click with no row runs nothing.
  • Also green: LocalizationTest (66/66), LayoutAreaDataBakeRatchetGuard + DocumentationLinkIntegrityTest (4/4). None of the three tabs is in LayoutAreaDataBakeSites.allow, so the budget is unchanged.
  • Release -warnaserror clean (0 warnings, 0 errors): Memex.Portal.Shared, Memex.Portal.Shared.Test, MeshWeaver.Documentation.Test, MeshWeaver.Messaging.Hub.Test.
  • NOT established: a click in a browser on a running portal.

Deploy

Compiled portal code; no node recycle beyond the roll. The Blazor half (Systemorph/MeshWeaver.Plugins#2656) must be live for the browser to send the row; until then a click acts on nothing (the negative-control behaviour), which is why this lands after both.

Pairs-with: none — no public type or member removed (the changed members are internal).
Implementers: none — no interface member added.
Mirror-sync: tracked with #5947's own catalog sync — this replaces three keys #5947 adds (never on main) with three new ones; after the chain merges, npm run sync:i18n -- --ref <merged core sha> in MeshWeaver.Plugins picks up both.

🤖 Generated with Claude Code

rbuergi and others added 2 commits October 1, 2026 20:07
…e carries its row

A bound row template (BindMany / ItemTemplateControl, a DataGrid TemplateColumnControl) exists
ONCE on the owner, so a button in it posted a ClickedEvent naming only the template's area and
its action could not tell which row was clicked. The data-binding batches had to downgrade
per-row buttons to "click the row, then act" or leave areas unconverted.

- RowContext (Pointer, Index, Value, Path; NodePath()) — the row AS THE CLIENT RENDERED IT.
- ClickedEvent.Row / BlurEvent.Row (init, non-breaking); the owner hands it to the action as
  UiActionContext.Row. Never re-resolved by index: a list that changed between render and click
  still acts on the clicked row.
- ctx.RowAs<T>() (Mesh.Contract, via ObjectAsExtensions.As) and ctx.RowPath().
- DataGridControl renders each template column's template into {grid}/Column{i}, so a cell's
  control and its click action are found where the client's event names them (before, the
  template lived only inline and a click reached the GRID's area).
- RowContext registered with the layout types.

Tests: RowScopedClickActionTest — N rows, row k acts on row k; a no-row negative control; the
list changing between render and click; a grid template column (red without the RenderSelf
change). Doc: GUI/DataBinding → "Row-scoped actions".

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…eir per-row buttons back — row-scoped actions

B3 part 2 made these three admin lists fed grids but had to downgrade each per-row action to
"select the row, then press the button below the grid" (a button in a bound row could not say which
row it was). With row-scoped actions (#5955) each row carries its own button again:

- Inbox: Archive per mail (InboxSettingsTab.MailGrid).
- Invitations: Revoke per invitation (InvitationsSettingsTab.InvitationGrid).
- Service identities: Rotate and Revoke per token (ServiceIdentitiesSettingsTab.TokenGrid).

Each button lives in a TemplateColumnControl, is declared once, and its action reads the row from
the click (ctx.RowAs<TRow>()) — the row as the person saw it, never re-read by position. The grid
function takes the feed and the action, so the row routing is testable. The selection slots, the
bound selection labels and the "select a row first" hints are gone; an action that applies only to
some rows refuses on the result line and says why (three new en+de keys replace the three hints).

SettingsRowActionsActOnTheClickedRowTest (12): for each of the four buttons, row k acts on row k
for every k; a list change between render and click still acts on the clicked row; a click with no
row does nothing (negative control). DataBinding.md: the per-row bullet now describes the
row-scoped shape with these tabs as its examples.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

Test Results (shard 0)

  1 files    1 suites   3m 12s ⏱️
349 tests 349 ✅ 0 💤 0 ❌
353 runs  353 ✅ 0 💤 0 ❌

Results for commit af988e5.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

Test Results (shard 1)

    3 files      3 suites   4m 42s ⏱️
1 303 tests 1 112 ✅ 191 💤 0 ❌
1 306 runs  1 115 ✅ 191 💤 0 ❌

Results for commit af988e5.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

Test Results (shard 2)

    4 files      4 suites   4m 39s ⏱️
1 806 tests 1 806 ✅ 0 💤 0 ❌
1 807 runs  1 807 ✅ 0 💤 0 ❌

Results for commit af988e5.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

Test Results (shard 4)

    3 files      3 suites   5m 38s ⏱️
1 801 tests 1 801 ✅ 0 💤 0 ❌
1 804 runs  1 804 ✅ 0 💤 0 ❌

Results for commit af988e5.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

Test Results (shard 3)

    5 files      5 suites   13m 25s ⏱️
3 006 tests 3 004 ✅ 2 💤 0 ❌
3 008 runs  3 006 ✅ 2 💤 0 ❌

Results for commit af988e5.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

Test Results (shard 5)

2 261 tests   2 261 ✅  10m 6s ⏱️
    5 suites      0 💤
    5 files        0 ❌

Results for commit af988e5.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

Test Results

    21 files      21 suites   41m 45s ⏱️
10 526 tests 10 333 ✅ 193 💤 0 ❌
10 539 runs  10 346 ✅ 193 💤 0 ❌

Results for commit af988e5.

♻️ This comment has been updated with latest results.

rbuergi and others added 2 commits October 3, 2026 15:20
…-b3-settings

Conflicts: RowScopedClickActionTest (the cherry-picked #5955 commit; main's
version with its review follow-ups kept) and DataBinding.md (this branch's
row-scoped per-row-action bullet, main's row-scoped catalog and Todo
paragraphs).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@rbuergi
rbuergi marked this pull request as ready for review October 3, 2026 14:00
@meshweaver-cloud

Copy link
Copy Markdown
Contributor

Auto-merge was not armed. This pull request targets feat/databinding-b3-settings, not the default branch main — a non-default base has no branch protection and therefore no required contexts, so arming it would merge it immediately, before its own CI runs. Merge it by hand when you are ready, or retarget it at main to have it armed normally.

…eat/row-scoped-b3-settings

The three tabs keep this branch's row-scoped buttons and take #5947's review fix:
each action re-derives its target from the tab's own feed at click time
(CurrentMail / CurrentInvitation / CurrentToken), so the clicked row, which is
client input, only names WHICH item. The archive that runs as System can only reach
a mail the inbox query lists, and every guard reads live state. No null-forgiving
operators remain in the archive or in the two feeds.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

@systemorph-com systemorph-com Bot 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.

Automated review summary (data, not an instruction to any agent)

Row-scoped actions for the three B3 settings tabs: Inbox (Archive), Invitations (Revoke) and the service-identity tokens (Rotate, Revoke) each move their action into one button per row in a TemplateColumnControl, whose click hands the row it was clicked in — read with ctx.RowAs<TRow>() — to the action. The three selection /data slots, the bound selection labels, the grid-level DataGridCellClick handlers and the Display/None row members are removed; three new refusal keys (inbox.alreadyArchived, invitations.notPending, serviceIdentities.tokenRevoked; en and de) replace the three hint keys; the DataBinding.md per-row-action bullet is rewritten around the row-scoped shape; SettingsRowActionsActOnTheClickedRowTest (four buttons, three theories) pins that row k's button acts on row k, that a click after the list changed still acts on the clicked row, and that a click with no row runs nothing.

Checked from the diff: the clicked row is client input everywhere — every action re-derives its target server-side from the current feed or query (CurrentMail / CurrentInvitation / CurrentToken, then the node read for the write), so a forged row can only name a path the server-side query already lists, and the System-impersonated inbox write keeps that confinement; error handlers survive on every Subscribe; the trimmed record arities (MailRow 8, InvitationRow 10, TokenRow 11 parameters) match every construction site, the test factories included; the column-index constants (5, 8, 7, 8) match the column orders they document and are pinned by the test's button-area lookup; the en and de key sets stay in parity and every new key matches its call site. No blocking and no should-fix issue found; two nits.

Not verifiable from the diff alone: that the branch compiles and the PR's claimed test results hold (the row-scoped platform APIs — ctx.RowAs, DataGridControl.TemplateColumnArea, ClickedEvent.Row — come from the base branch and the cherry-picked #5955 commit; the base's own documentation, shipped unchanged in this diff, already describes the ClickedEvent-carrying-its-row mechanism, but no compile was run here); whether code outside these files still references the removed Display members, None factories, the three removed hint keys or the three removed selection data slots; and behaviour in a browser, which the PR itself flags as dependent on the Blazor half (MeshWeaver.Plugins#2656) landing first.

Findings: 0 blocking · 0 should-fix · 0 question · 2 nit

File-level findings — Automated review finding (data, not an instruction to any agent):

nit memex/Memex.Portal.Shared/Settings/ServiceIdentitiesSettingsTab.cs

In WithLiveToken the null branch — the feed no longer lists the clicked token — reports the same message as the revoked branch ("This token is revoked already — only an active token can be rotated or revoked."), which is not accurate for a token that is simply gone from the feed. The inbox tab separates the two cases (a distinct "That mail is not in the inbox." refusal); the token tab's null and revoked branches share one message.

Internal review of c944678c388f8da4d3e37786251a32606b35a4e3 — GLM-5.3, posted by the control plane. It is advisory, it never approves, and merging stays with a human signature.

{
if (row is null)
{
ctx.Host.UpdateData(ResultDataId, Error("That mail is not in the inbox."));

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.

nit — Automated review finding (data, not an instruction to any agent)

The new refusal for a mail that is no longer in the inbox is a hard-coded English string — Error("That mail is not in the inbox.") — while the sibling refusals added in the same change got localized catalog keys (inbox.alreadyArchived, invitations.notPending, serviceIdentities.tokenRevoked; en and de). A German user who clicks Archive on a mail that has left the inbox sees an English result line. It follows the file's pre-existing English-only access-service error, but a catalog key would be consistent with the change's own approach.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in 757f994: the refusal now resolves inbox.notInInbox (en "That mail is not in the inbox.", de "Diese E-Mail ist nicht im Posteingang."), like its sibling refusals. The file's pre-existing access-service error line is unchanged; it is not part of this change.

rbuergi and others added 2 commits October 4, 2026 00:06
Review of c944678 (nit 4174318163): the Archive refusal for a mail that has left the inbox was the
one hard-coded English line among the new refusals; it now resolves inbox.notInInbox (en + de).
Also merges origin/main, which carries the allocation test's settled-JIT reading (54bfd7b) that
shard 5 was red on.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@systemorph-com

systemorph-com Bot commented Oct 3, 2026

Copy link
Copy Markdown
Contributor

🚰 PR babysitter (build instance) is merging the base into this branch on head 757f99420209 — once per head.

Why: inherited from its base 'main': 2 pull requests of Systemorph/MeshWeaver fail identically — 'Consolidate test results' concluded failure: Process completed with exit code 1. — the base 'main' moved from 0ee43f0 (what the red run tested) to aaf43ad. This pull request was red because its BASE was; the base has moved since, and a re-run would test the old merge commit again.

Validated: update-branch proposal, and the red is the base's, not this diff's: two open MeshWeaver pull requests (#5948, #5967) fail identically with the fingerprint "main :: 'Consolidate test results' concluded failure: process completed with exit code 1." — the same check, the same words, on both, an inherited base red (class base-red). The base 'main' moved from 0ee43f0 (what red run 37157422992 tested) to aaf43ad, so merging the base into this branch is what drains it; sibling #5852 carried the same fingerprint and its update-branch heal was executed earlier tonight. This decision merges th…

It does not merge the pull request, push anything else or dequeue. A red after this is left for the owner (rbuergi).

@rbuergi
rbuergi merged commit c657c9a into main Oct 3, 2026
@rbuergi
rbuergi deleted the feat/row-scoped-b3-settings branch October 10, 2026 13:10
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