Fix #394: visibility select follows truth #399

Merged
crueber merged 1 commit from fix/issue-394 into main 2026-09-12 17:48:22 +00:00
Owner

Root cause is client display state (backend proven coherent): the Settings visibility select was write-once-seeded (getVis() === null gate) and never followed the shared access:{full} entry afterward, and its no-data-yet null rendered as "public".

What this changes (frontend only, no backend, no new deps):

  • New headless helper web/src/lib/visibilityReseed.js (createTrack/reseed/rebase/isDirty/isSeeded + docVisibility): first doc seeds value+baseline, later docs reseed BOTH while clean, dirty forms never clobbered, ?? public only for a present doc missing the field (no-data-yet is null).
  • Settings GeneralTab mirrors the Access baseline/dirty pattern: reseed effect over the shared entry (sets only on field change, settles), baseline rebase on save success (authoritative echo) and on failure reseed-to-truth, blank disabled select + loading line while unseeded, warn-line unsaved-changes marker, save disabled while unseeded.
  • Access.jsx had the same hole (render-time write-once seed, vis defaulting to public): symmetric fix — entry-following effect reseeding the whole form while clean (baseline rebases ONLY while clean, so a dirty form keeps its CAS version and concurrent saves still 409 into reload), null default + disabled select + loading line + unsaved marker; render-time seed() side effect deleted.
  • Law-12 decision appended to docs/go/12_web_ui.md.

Verification: new web/test/unit/visibility-reseed.test.js 9/9; full non-smoke suite 777/777 green; vite build + esbuild SDK bundle green. The 2 smoke tests fail only because they probe a live :8080 occupied by an unrelated instance (left untouched); they skip cleanly (3/3) with no server. Browser proof open (shared-daemon loopback guard).

Root cause is client display state (backend proven coherent): the Settings visibility select was write-once-seeded (`getVis() === null` gate) and never followed the shared `access:{full}` entry afterward, and its no-data-yet null rendered as "public". What this changes (frontend only, no backend, no new deps): - New headless helper `web/src/lib/visibilityReseed.js` (createTrack/reseed/rebase/isDirty/isSeeded + docVisibility): first doc seeds value+baseline, later docs reseed BOTH while clean, dirty forms never clobbered, `?? public` only for a present doc missing the field (no-data-yet is null). - Settings GeneralTab mirrors the Access baseline/dirty pattern: reseed effect over the shared entry (sets only on field change, settles), baseline rebase on save success (authoritative echo) and on failure reseed-to-truth, blank disabled select + loading line while unseeded, warn-line unsaved-changes marker, save disabled while unseeded. - Access.jsx had the same hole (render-time write-once seed, vis defaulting to public): symmetric fix — entry-following effect reseeding the whole form while clean (baseline rebases ONLY while clean, so a dirty form keeps its CAS version and concurrent saves still 409 into reload), null default + disabled select + loading line + unsaved marker; render-time seed() side effect deleted. - Law-12 decision appended to docs/go/12_web_ui.md. Verification: new web/test/unit/visibility-reseed.test.js 9/9; full non-smoke suite 777/777 green; vite build + esbuild SDK bundle green. The 2 smoke tests fail only because they probe a live :8080 occupied by an unrelated instance (left untouched); they skip cleanly (3/3) with no server. Browser proof open (shared-daemon loopback guard).
Settings select was write-once-seeded and never followed the shared
access entry afterward; unseeded null rendered as public. Both selects
now carry a baseline, reseed while clean, never clobber edits, show an
unsaved-changes marker, and render loading as a blank disabled select.
Rule extracted to web/src/lib/visibilityReseed.js with node cover;
doc decision in 12_web_ui.md.
Sign in to join this conversation.
No description provided.