Skip to content

Add the set view with the per-item diff rendered progressively - #2576

Merged
pournasserian merged 1 commit into
v2from
2574-set-view
Oct 19, 2026
Merged

pournasserian merged 1 commit into
v2from
2574-set-view

Conversation

@pournasserian

Copy link
Copy Markdown
Member

Closes #2574

What

The second slice of F-DSK (#2422): the set view (DSK-03), with the per-item diff rendered progressively (PERF-04). It is read only; approve, reject and revert are the next slice.

  • /sets/{id} (ChangeSetView.razor, static server rendering):
    • The rationale as the heading, the state, the initiator, when the set was opened and when it entered its state.
    • The set it reverts as a link, and a rejected set's reason.
    • Sources: a source that is an http or https address is a link with target="_blank" and rel="noopener noreferrer". Anything else, such as a file name, a person or a javascript: address, is text.
    • Checks: says whether the checks failed, and that results per check appear once checks run on every set (CHK-03, F-CHK F-CHK: Check engine with deterministic and LLM-judged checks #2420).
    • Routing: says that routing has not decided. Routing rules (Add routing rules and their evaluation #2565) are not on v2 yet.
    • History: every event whose subject is the set, oldest first, with who acted and when, read through IAuditReader. Refused moves are listed too. The first 200 are shown, with a link to the full history.
    • An unknown id calls NavigationManager.NotFound(), so the page answers 404 with the not-found page.
  • ChangeSetItems (Components/Review): the interactive island. It reads IChangeSetDirectory.DiffAsync and renders the merged DiffView. The first 25 items arrive with the prerendered page and the circuit adds the rest in batches. A refused read is reported in place of the items.
  • The desk links each set to its view instead of its audit history.

Why

  • A static page with one interactive island. PERF-04 needs the circuit only for the diff batches. The rest of the page, including the source links and history, then reads and links without a circuit. The review actions in the next slice can post forms to this page, so a decision does not depend on the circuit (ADR-0006).
  • Times from the history. The view reads the set's history anyway, so the opened and state times come from it, the same events IChangeSetLists uses.
  • Only web addresses become links. Sources are free text from agents. Linking only http and https keeps javascript: and local paths from becoming clickable.
  • Not in this PR: review actions, the routing decision from Add routing rules and their evaluation #2565 once it merges, check results per check, item names in the diff, and keyboard review.

Tests

  • bUnit ChangeSetViewTests:
    • The rationale, state, initiator and the two times from the history.
    • Only the web address among four sources is a link, with its target and rel. Openable accepts http and https and refuses javascript:, file:, mailto: and a bare host.
    • The history in order, with a refused move listed.
    • A revert link and a rejection reason.
    • The check and routing notes.
    • An unknown set renders nothing.
    • The island renders the diff with an assigned render mode and reports a refused diff.
    • Logical utilities only.
  • DeskTests: a set row links to sets/{id}.
  • Integration, host:
    • An anonymous request goes to sign-in, and an unknown set answers 404 with the not-found page.
    • A set of 30 items opened by a user: the desk links to it, and the page shows the rationale, the web source as a new-tab link, no javascript: link and the text source. The history lists opened, submitted and awaiting review. The prerendered island holds the first 25 items, is aria-busy and carries the Blazor marker for the circuit.
    • A rejected revert set shows its reason and links to the published set it reverts.

dotnet build -warnaserror and the full suite pass locally against a postgres:17 container as well as SQLite. One loaded run had eight PostgreSQL and job-lease timing failures in classes this PR does not touch; all 102 tests of those classes passed on a rerun. The batches over the circuit were not checked in a browser here: the prerendered first batch is tested, and #2479 checked the batching on /ui. No new package and no new project reference.

Checklist

  • Linked to an issue a maintainer has confirmed
  • Targets v2
  • Tests or docs updated where behaviour changed
  • No tool-generated co-author trailers or footers

@pournasserian pournasserian added this to the M1 Governance core milestone Oct 19, 2026
@pournasserian pournasserian added type:feature New capability v2 FluentCMS v2 (v2 branch) area:desk labels Oct 19, 2026
@pournasserian
pournasserian merged commit 0aa766a into v2 Oct 19, 2026
2 checks passed
@pournasserian
pournasserian deleted the 2574-set-view branch October 19, 2026 04:24
pournasserian added a commit that referenced this pull request Oct 20, 2026
…test gaps (#2657)

Closes #2627

## What

Follow-ups from the independent review of #2576 (F-DSK, #2422).

- **The set view's diff island carries its prerendered diff into the
circuit.**
- The island keeps the diff it prerendered in
`PersistentComponentState`, and the circuit takes it back.
- The circuit shows the same items at once. It doesn't read the diff
again and never shows "Loading the items…" in their place.
- `ItemDiff.Restore` and `ValueDiff.Restore` rebuild the diff model from
what was kept, as `ChangeOperation.Restore` already does for operations.
- **The circuit hub takes messages up to 1 MB.** Kept state travels in
the circuit's first message, so the 32 KB default would refuse a large
set's diff. The hub requires a signed-in person. Kept state reaches the
hub about 1.8 times as large as its JSON, because Blazor encodes it
twice and encrypts it. So a diff over 480 KB of JSON isn't kept, and the
circuit reads it again as before. `AdminLimits` holds both sizes.
- **A source with a look-alike host** shows the host it resolves to next
to the link text. For example, `https://аpple.com/` with a Cyrillic "а"
shows `(host xn--pple-43d.com)`. The address the link opens is
unchanged.
- **History labels go through the localizer.** An event without its own
label reads "Other event (operation)", and the "UTC" suffix is
localized.

## Why

- **Persisted state, not a second read.** On PostgreSQL the second read
was truly asynchronous, so the list emptied while it ran: the scroll
jumped, folded items opened again, and focus on a summary was lost.
Carrying the diff costs the size of the state, which the hub limit
covers.

## Tests

- **Browser, on PostgreSQL when it is configured (as in CI):** a
200-item set arrives in batches to all 200 items. The number of items
shown never drops below the prerendered batch when the circuit attaches.
Without the fix, the test fails on PostgreSQL.
- **Browser:** a set of 100 long pages, whose diff is too large to keep,
still attaches its circuit and shows all 100 items. With the earlier
limit of 768 KB, this test fails.
- **Host, on SQLite and PostgreSQL:**
  - The set view shows its rationale, sources, history and first items.
  - A look-alike source shows its punycode host, and only that one.
  - A rejected revert shows its reason.
- A set an agent opened over MCP shows the agent and renders with
`dir="rtl"`.
- **bUnit:**
- The circuit takes the prerendered diff without reading it again or
showing the busy state.
- `AsciiHost` names a look-alike host and leaves an ASCII host or an
IPv6 address alone.
  - A diff too large to keep is read again by the circuit.
- **Unit:** a restored item diff holds what it was restored from.

`dotnet build -warnaserror`, the unit and bUnit suite, and the set view,
review, keyboard and accessibility tests on SQLite and PostgreSQL pass
locally. No new package and no new project reference.

## Checklist

- [x] Linked to an issue a maintainer has confirmed
- [x] Targets `v2`
- [x] Tests or docs updated where behaviour changed
- [x] No tool-generated co-author trailers or footers
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

type:feature New capability v2 FluentCMS v2 (v2 branch)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Set view: rationale, sources, history and the per-item diff rendered progressively

1 participant