port: rows, bulk Tag + the list of missing server actions 2/3 #877

Open
kayg wants to merge 15 commits from port/rows-actions into port/rows
Owner

Slice 2 of 3, stacked on #875 (base port/rows). It also merges #887 (notes-archive-hide, server side of hide/archive) so it can be built and tested; once #887 is on dev, retarget and the merge commit drops out.

Ships (kayg 11-03 decisions)

  • Archive / Hide / Unarchive / Unhide wired to #887's notes.archive|unarchive|hide|unhide: row menu, swipe (Delete, Hide, Archive on the right; Unarchive leads in the Archived place), and the selection bar. One toast says what happened, per note ("Archived 99 · 1 couldn't be changed"; 100 ids a request, longer selections are several requests and one Undo through notes.undo).
  • Archived place in the sidebar (shows once anything is, or was, archived; read only when opened) and Show hidden (the list's new ⋯ button beside the list, as on the phone's top pill): hidden notes join the list faded. The store follows visibility in notes.saved (#887) from other devices.
  • Move = tags (folders are not exposed): the bar's one Move action opens the tag picker that adds and removes (a check = every picked note has it; mockup toast wording "Added #x to 3 notes" / "Removed #x from 3 notes"; real edit of each note's tags:, one Undo). The earlier separate Tag action is merged into it.
  • Menu items come from the row-action registry; the hold menu leads with the icon row (Copy link, Pin, Delete) on a phone.
  • Reuse: BulkBar, the row-action and bulk-action registries, the page-actions registry (Show hidden) and the shared menu; nothing forked.

Still not here: bulk Share (no server action), Insert link here (needs an editor-side insert into the open note).

Found while testing #887 (for its author): the server skips Archive/Hide for a note whose frontmatter has a flow-style list tags: [a, b] ("its properties use formatting Calternal can't edit safely"); block-style tags:\n - a works. The web's own tag writer (fm-tags.ts, and bulk Move) writes the flow style for a new field, so a note tagged from the app cannot then be archived or hidden. Probe: seed ---\ntags: [errands]\n--- then notes.archive returns skipped. The toast shows the reason; I did not change the writer here.

Gates (head da8c7bb0 for this slice; slice 3 head ecc8838b contains it): web vitest 110 passed; svelte-check: the only errors are 2 in e2e/review4-signout.spec.ts (from dev, not this stack); Chromium 1440+390 (press, bulk-tag, visibility, sidebar-drop, select-rows, plus the earlier full set 131 passed / 65 skipped / 0 failed); WebKit 1440+390 on dev-02 (press, bulk-tag, visibility, sidebar-drop, tags, swipe-rows, select-rows, touch-select) 102 passed / 44 skipped / 0 failed. New specs: visibility.spec.ts, bulk-tag.spec.ts (now Move).

Slice 2 of 3, stacked on #875 (base `port/rows`). It also merges #887 (`notes-archive-hide`, server side of hide/archive) so it can be built and tested; once #887 is on dev, retarget and the merge commit drops out. **Ships (kayg 11-03 decisions)** - **Archive / Hide / Unarchive / Unhide** wired to #887's `notes.archive|unarchive|hide|unhide`: row menu, swipe (Delete, Hide, Archive on the right; Unarchive leads in the Archived place), and the selection bar. One toast says what happened, per note ("Archived 99 · 1 couldn't be changed"; 100 ids a request, longer selections are several requests and one Undo through `notes.undo`). - **Archived place** in the sidebar (shows once anything is, or was, archived; read only when opened) and **Show hidden** (the list's new ⋯ button beside the list, as on the phone's top pill): hidden notes join the list faded. The store follows `visibility` in `notes.saved` (#887) from other devices. - **Move = tags** (folders are not exposed): the bar's one **Move** action opens the tag picker that adds and removes (a check = every picked note has it; mockup toast wording "Added #x to 3 notes" / "Removed #x from 3 notes"; real edit of each note's `tags:`, one Undo). The earlier separate Tag action is merged into it. - Menu items come from the row-action registry; the hold menu leads with the icon row (Copy link, Pin, Delete) on a phone. - Reuse: BulkBar, the row-action and bulk-action registries, the page-actions registry (Show hidden) and the shared menu; nothing forked. **Still not here:** bulk Share (no server action), Insert link here (needs an editor-side insert into the open note). **Found while testing #887 (for its author):** the server skips Archive/Hide for a note whose frontmatter has a flow-style list `tags: [a, b]` ("its properties use formatting Calternal can't edit safely"); block-style `tags:\n - a` works. The web's own tag writer (`fm-tags.ts`, and bulk Move) writes the flow style for a new field, so a note tagged from the app cannot then be archived or hidden. Probe: seed `---\ntags: [errands]\n---` then `notes.archive` returns `skipped`. The toast shows the reason; I did not change the writer here. **Gates** (head da8c7bb0 for this slice; slice 3 head ecc8838b contains it): web vitest 110 passed; svelte-check: the only errors are 2 in `e2e/review4-signout.spec.ts` (from dev, not this stack); Chromium 1440+390 (press, bulk-tag, visibility, sidebar-drop, select-rows, plus the earlier full set 131 passed / 65 skipped / 0 failed); WebKit 1440+390 on dev-02 (press, bulk-tag, visibility, sidebar-drop, tags, swipe-rows, select-rows, touch-select) 102 passed / 44 skipped / 0 failed. New specs: `visibility.spec.ts`, `bulk-tag.spec.ts` (now Move).
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
port(rows): bulk Tag spec reads the frontmatter; tag helper split for unit tests
Some checks failed
CI / server (pull_request) Has been cancelled
CI / rust (pull_request) Has been cancelled
CI / web (pull_request) Has been cancelled
Markdown extraction contract / markdown-native-contract (pull_request) Has been cancelled
Markdown extraction contract / markdown-editor-contract (pull_request) Has been cancelled
31df86b202
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Author
Owner

Shots, product vs mockup: bulk bar after Select (phone and wide), the Move picker (mockup calls it Tag; kayg's 11-03 decision: Move is tags, one picker that adds and removes, a check means every picked note has the tag), archive/hide toast and the Archived place, Show hidden (faded rows, product only: the mockup has it behind its own list menu).

Shots, product vs mockup: bulk bar after Select (phone and wide), the Move picker (mockup calls it Tag; kayg's 11-03 decision: Move is tags, one picker that adds and removes, a check means every picked note has the tag), archive/hide toast and the Archived place, Show hidden (faded rows, product only: the mockup has it behind its own list menu).
Author
Owner

Fix for "can't archive or hide a note whose other keys use flow style" (head 59f7f0eb on notes-archive-hide)

Cause: the strict planner judged every line of the metadata, so tags: [a, b] (what the app writes) refused the whole note.

Change:

  • New key-scoped planner (strict_frontmatter/scoped.rs): the requested keys' own lines go through the same strict grammar and planner. Every other line is never read or changed. The metadata must still load as one YAML map, and after the edit the map must equal the old one with exactly the requested change.
  • Visibility reads (current_key), plans (plan_keys) and the executor's metadata check (ScopedMetadata { keys }) use it. The whole-file plan stays for Tasks.
  • Consistent CRLF metadata is now readable and writable. A mix of line ends or a lone CR still refuses. A comment on the key's own line, yes, True, quoted, list or map values still refuse that note.
  • Old tests that encoded "other keys' formatting skips the note" were changed to the new rule. Skips now come only from the changed key's own form.

Tests (byte-exact, with undo): archive, unarchive, hide, unhide on tags: [a, b], aliases: [x], flow+block mix, comments between keys, unindented lists, quoted values, multi-line flow, nested map, LF and CRLF; the old hidden: block list moving next to flow lists; end to end: tag a note through a room client (tags: [a, b]), then archive, unarchive, hide, unhide.

Gates on dev-01 (~/bin/slot, -j4, after train BM14d finished):

  • cargo fmt --all --check: clean (after style: fmt).
  • cargo clippy --workspace --all-targets --locked -- -D warnings: clean.
  • cargo test --locked -p calternal-notes -p calternal-tasks -p calternal-core -p calternal-engine: all pass (notes lib 592 passed, 6 ignored; tasks 69; 0 failures). Tasks strict-writer users unchanged and passing.

Perf spot check (archive one / archive 100, flat against c8afa867): pending, see the next comment. The perf VMs are held by other jobs.

**Fix for "can't archive or hide a note whose other keys use flow style" (head 59f7f0eb on `notes-archive-hide`)** Cause: the strict planner judged every line of the metadata, so `tags: [a, b]` (what the app writes) refused the whole note. Change: - New key-scoped planner (`strict_frontmatter/scoped.rs`): the requested keys' own lines go through the same strict grammar and planner. Every other line is never read or changed. The metadata must still load as one YAML map, and after the edit the map must equal the old one with exactly the requested change. - Visibility reads (`current_key`), plans (`plan_keys`) and the executor's metadata check (`ScopedMetadata { keys }`) use it. The whole-file `plan` stays for Tasks. - Consistent CRLF metadata is now readable and writable. A mix of line ends or a lone CR still refuses. A comment on the key's own line, `yes`, `True`, quoted, list or map values still refuse that note. - Old tests that encoded "other keys' formatting skips the note" were changed to the new rule. Skips now come only from the changed key's own form. Tests (byte-exact, with undo): archive, unarchive, hide, unhide on `tags: [a, b]`, `aliases: [x]`, flow+block mix, comments between keys, unindented lists, quoted values, multi-line flow, nested map, LF and CRLF; the old `hidden:` block list moving next to flow lists; end to end: tag a note through a room client (`tags: [a, b]`), then archive, unarchive, hide, unhide. Gates on dev-01 (`~/bin/slot`, `-j4`, after train BM14d finished): - `cargo fmt --all --check`: clean (after `style: fmt`). - `cargo clippy --workspace --all-targets --locked -- -D warnings`: clean. - `cargo test --locked -p calternal-notes -p calternal-tasks -p calternal-core -p calternal-engine`: all pass (notes lib 592 passed, 6 ignored; tasks 69; 0 failures). Tasks strict-writer users unchanged and passing. Perf spot check (archive one / archive 100, flat against c8afa867): pending, see the next comment. The perf VMs are held by other jobs.
Author
Owner

Fixed: Move menu race (#947). Head 27abe224 (fix c407032e, test 27abe224).

Cause: the menu chose add or remove from the notes list, which lags the server, so a fast second tap saw "no tag yet" and added again ("They already have #tag").

Fix:

  • lib/pending-tags.ts: the tags a Move asked for sit over the list until the list shows the same tags (or a newer edited stamp after the write finished). Each ask has a turn number, so an older write never settles a newer ask. No timers.
  • rows/plugins/move-tags.ts: the check on each row and the tap's add-or-remove both read the notes with pending Moves applied; the ask is recorded before anything is awaited. Writes run one after another, so the second reads the file the first wrote. Undo goes through the same line.
  • bulk-note.ts uses it.

Tests (no timeouts):

  • move-tags.test.ts holds the first write open, checks the menu already shows the tag as on, and that the second tap removes ([add, remove], in order, no error toast). Also: writes do not overlap; a no-op write and a newer list hand back to the list.
  • bulk-tag.spec.ts: new "a fast second Move tap removes the tag the first one added" (two taps, no waiting).

Gates on dev-01 (~/bin/slot, CI=1 --workers=1 --retries=0), bunx playwright test e2e/bulk-tag.spec.ts --project=<p> --repeat-each=20:

  • chromium-1440: 40 passed (5.2 m)
  • webkit-1440: 40 passed (10.1 m)
  • vitest 117/117; svelte-check = baseline (2 errors, 15 warnings).
    Not run: the bundle-size gate (one small new file, no new chunk); the 390 projects skip this spec.
**Fixed: Move menu race (#947).** Head `27abe224` (fix `c407032e`, test `27abe224`). Cause: the menu chose add or remove from the notes list, which lags the server, so a fast second tap saw "no tag yet" and added again ("They already have #tag"). Fix: - `lib/pending-tags.ts`: the tags a Move asked for sit over the list until the list shows the same tags (or a newer `edited` stamp after the write finished). Each ask has a turn number, so an older write never settles a newer ask. No timers. - `rows/plugins/move-tags.ts`: the check on each row and the tap's add-or-remove both read the notes with pending Moves applied; the ask is recorded before anything is awaited. Writes run one after another, so the second reads the file the first wrote. Undo goes through the same line. - `bulk-note.ts` uses it. Tests (no timeouts): - `move-tags.test.ts` holds the first write open, checks the menu already shows the tag as on, and that the second tap removes (`[add, remove]`, in order, no error toast). Also: writes do not overlap; a no-op write and a newer list hand back to the list. - `bulk-tag.spec.ts`: new "a fast second Move tap removes the tag the first one added" (two taps, no waiting). Gates on dev-01 (`~/bin/slot`, `CI=1 --workers=1 --retries=0`), `bunx playwright test e2e/bulk-tag.spec.ts --project=<p> --repeat-each=20`: - chromium-1440: 40 passed (5.2 m) - webkit-1440: 40 passed (10.1 m) - vitest 117/117; svelte-check = baseline (2 errors, 15 warnings). Not run: the bundle-size gate (one small new file, no new chunk); the 390 projects skip this spec.
Author
Owner

Heads-up from the BM15 first-paint diet lane (#947): I restacked the rows PRs on top of the #887 diet and pushed port/rows (#875), port/rows-actions (#877) and port/rows-drop (#878) with --force-with-lease. Your last commits on #877 (82b257f6, the Move menu through openMenuWith) and #878 (11e8ae1d, b68a61e5, the Undo and live-pick fixes) are kept: I cherry-picked them onto the new stack, so their SHAs changed. Please git fetch and reset your local branches to the pushed heads before pushing again; a plain push from an older checkout would drop the diet. Anything you change in first-paint code (index chunk) should stay behind the lazy chunks: the press engine, swipe engine, menu helpers, Archive/Hide actions and the tooltip engine now load with press/press.ts or on first use. Numbers are in the #947 comment.

Heads-up from the BM15 first-paint diet lane (#947): I restacked the rows PRs on top of the #887 diet and pushed `port/rows` (#875), `port/rows-actions` (#877) and `port/rows-drop` (#878) with `--force-with-lease`. Your last commits on #877 (`82b257f6`, the Move menu through `openMenuWith`) and #878 (`11e8ae1d`, `b68a61e5`, the Undo and live-pick fixes) are kept: I cherry-picked them onto the new stack, so their SHAs changed. **Please `git fetch` and reset your local branches to the pushed heads before pushing again**; a plain push from an older checkout would drop the diet. Anything you change in first-paint code (`index` chunk) should stay behind the lazy chunks: the press engine, swipe engine, menu helpers, Archive/Hide actions and the tooltip engine now load with `press/press.ts` or on first use. Numbers are in the #947 comment.
Author
Owner

First-paint diet (BM15, #947). Head 71ed79ac (restacked on #875; your fixes, the Move menu through openMenuWith and the bulk-tag spec tag, are kept).

First-paint bytes are brotli q11 from apps/web/scripts/bundle-size.mjs (JS of the first-paint closure = the index chunk; CSS of the first stylesheet). Dev baseline 41f2efa3: 238,797 B JS, 19,250 B CSS. A second figure, "hash-normalised", is the same chunk with the 8-character chunk-file hashes replaced by a fixed string: the hashes are random, and they move the real brotli number by ±300 B between two builds of identical code, so a few-hundred-byte claim is only meaningful in the normalised figure (dev: 238,804). Chunk budgets: every new lazy chunk's line is taken from the first-script line or the idle-screens line so the total stays 404.2; perf/web.sh --bundle-only --gate passes on this head.

first JS first CSS lazy JS
before (as in #947) +3,205 over dev +180
now (stack through this PR) 237,066 (−1,731 vs dev; normalised 237,049 = −1,755) 18,980 (−270) 138,856

Own effect over #875: +320 B JS real (normalised +768), +1 B CSS, down from +1,215 B / +49 B. What moved out of first paint: the Archived/Show-hidden reads (visibility-load.ts) and the tag-edit helpers (fm-tags-edit.ts) load on first use (the Show hidden switch still flips at once); Archive and Hide row actions register with the press chunk (a row's buttons are only drawn after its first touch, which that chunk serves) with the server code behind import(); the faded-row rule loads with the code that reads hidden notes; .band{display:block} (default) dropped and the auto margin shared with .pick-acts.

Specs: press, visibility, bulk-tag on all four projects: 28 passed, 24 skipped, 0 failed (including bulk-tag, which passed on its first run here; I did not repeat it 10 times). Lint ok, svelte-check = baseline, units ok.

Budgets: press 7.2 (it measures 7.15), index 232.5 (231.5), idle-screens 48.7; total 404.2.

**First-paint diet (BM15, #947).** Head `71ed79ac` (restacked on #875; your fixes, the Move menu through `openMenuWith` and the bulk-tag spec tag, are kept). First-paint bytes are brotli q11 from `apps/web/scripts/bundle-size.mjs` (JS of the first-paint closure = the `index` chunk; CSS of the first stylesheet). Dev baseline `41f2efa3`: **238,797 B JS, 19,250 B CSS**. A second figure, "hash-normalised", is the same chunk with the 8-character chunk-file hashes replaced by a fixed string: the hashes are random, and they move the real brotli number by ±300 B between two builds of identical code, so a few-hundred-byte claim is only meaningful in the normalised figure (dev: 238,804). Chunk budgets: every new lazy chunk's line is taken from the first-script line or the idle-screens line so the total stays 404.2; `perf/web.sh --bundle-only --gate` passes on this head. | | first JS | first CSS | lazy JS | |---|---|---|---| | before (as in #947) | +3,205 over dev | +180 | | | **now (stack through this PR)** | **237,066 (−1,731 vs dev**; normalised 237,049 = −1,755) | **18,980 (−270)** | 138,856 | Own effect over #875: **+320 B JS real (normalised +768), +1 B CSS**, down from +1,215 B / +49 B. What moved out of first paint: the Archived/Show-hidden reads (`visibility-load.ts`) and the tag-edit helpers (`fm-tags-edit.ts`) load on first use (the Show hidden switch still flips at once); Archive and Hide row actions register with the press chunk (a row's buttons are only drawn after its first touch, which that chunk serves) with the server code behind `import()`; the faded-row rule loads with the code that reads hidden notes; `.band{display:block}` (default) dropped and the auto margin shared with `.pick-acts`. Specs: `press`, `visibility`, `bulk-tag` on all four projects: **28 passed, 24 skipped, 0 failed** (including `bulk-tag`, which passed on its first run here; I did not repeat it 10 times). Lint ok, svelte-check = baseline, units ok. Budgets: `press` 7.2 (it measures 7.15), `index` 232.5 (231.5), `idle-screens` 48.7; total 404.2.
Author
Owner

Two small commits landed here for the stack: 82b257f6 (Move menu opens through openMenuWith; fixes the svelte-check 3rd error from the lazy-menu split) and a47cab78 (bulk-tag spec tag can no longer collide with the note ids/titles, ~1 in 100). #878 6e994016 is stacked on it; its repeat results are on #878.

Two small commits landed here for the stack: `82b257f6` (Move menu opens through `openMenuWith`; fixes the svelte-check 3rd error from the lazy-menu split) and `a47cab78` (bulk-tag spec tag can no longer collide with the note ids/titles, ~1 in 100). #878 `6e994016` is stacked on it; its repeat results are on #878.
Author
Owner

Warm-up commit on this stack (BM15 follow-up, #947): the press+swipe chunk is now fetched right after the list's first paint (at once on touch, first idle moment with a 150 ms cap elsewhere) and by the first pointer over the list; the tooltip engine at idle (200 ms cap) and by the first pointer movement. Heads: #875 259b5ab7, #877 53104962, #878 bef3b4f7 (restacked with --force-with-lease; reset local checkouts before pushing). Cold first-use numbers and the specs for these heads are in #947 (comment).

Warm-up commit on this stack (BM15 follow-up, #947): the press+swipe chunk is now fetched right after the list's first paint (at once on touch, first idle moment with a 150 ms cap elsewhere) and by the first pointer over the list; the tooltip engine at idle (200 ms cap) and by the first pointer movement. Heads: #875 `259b5ab7`, #877 `53104962`, #878 `bef3b4f7` (restacked with `--force-with-lease`; reset local checkouts before pushing). Cold first-use numbers and the specs for these heads are in https://git.kayg.org/kayg/calternal/issues/947#issuecomment-79453.
Author
Owner

Not gated yet: carries #887

This branch contains a merge of #887 (notes-archive-hide: the hide/archive/restore Rust and the contracts/actions.json changes). #887's write-path regression is being fixed by a separate lane, so gating this branch now would measure that regression, and merging it would land #887 through the back door. Once #887 is in dev, I merge dev here and run the full per-PR gates (I already merged origin/dev b5e8dcb2 into #875 and #904; #877 and #878 follow #875 as a stack). Also needs then: backend perf ABAB on the perf VMs (Rust), and for #904 the unchanged phone-bar fix f30e0bb7.

## Not gated yet: carries #887 This branch contains a merge of #887 (`notes-archive-hide`: the hide/archive/restore Rust and the `contracts/actions.json` changes). #887's write-path regression is being fixed by a separate lane, so gating this branch now would measure that regression, and merging it would land #887 through the back door. Once #887 is in dev, I merge dev here and run the full per-PR gates (I already merged origin/dev `b5e8dcb2` into #875 and #904; #877 and #878 follow #875 as a stack). Also needs then: backend perf ABAB on the perf VMs (Rust), and for #904 the unchanged phone-bar fix `f30e0bb7`.
Author
Owner

Compiles again: dev 258f3eac-era merge, Requirement::ExpectedOwners restored

The branch had lost the ExpectedOwners { digest } variant (reader half of #898, dev 72434e45) in an earlier dev merge, so home.rs, plan.rs and tests/tolerant.rs did not compile. Merged origin/dev 02cdf878 into port/rows (#875) 367c7d37, put the variant back in plan.rs next to #887's ScopedMetadata / ExactUndo, and merged forward: #877 a54f90f2, #878 d96ee32f. perf/budgets.json merged by the 3-way rule on each side's line moves (index 220.7 → 221.7, idle-screens 51.0 → 49.8 → 49.65, picker-list 9.7 from #975; total stays 404.2).
Checked on dev-01: cargo fmt --all --check rc 0 and cargo clippy -j4 --workspace --all-targets --locked -- -D warnings rc 0 (compiles every test target) on #875 alone and on the #878 tip (the whole stack). Full gates still wait for #887.

## Compiles again: dev `258f3eac`-era merge, `Requirement::ExpectedOwners` restored The branch had lost the `ExpectedOwners { digest }` variant (reader half of #898, dev `72434e45`) in an earlier dev merge, so `home.rs`, `plan.rs` and `tests/tolerant.rs` did not compile. Merged origin/dev `02cdf878` into `port/rows` (#875) `367c7d37`, put the variant back in `plan.rs` next to #887's `ScopedMetadata` / `ExactUndo`, and merged forward: #877 `a54f90f2`, #878 `d96ee32f`. `perf/budgets.json` merged by the 3-way rule on each side's line moves (index 220.7 → 221.7, idle-screens 51.0 → 49.8 → 49.65, `picker-list` 9.7 from #975; total stays 404.2). Checked on dev-01: `cargo fmt --all --check` rc 0 and `cargo clippy -j4 --workspace --all-targets --locked -- -D warnings` rc 0 (compiles every test target) on #875 alone and on the #878 tip (the whole stack). Full gates still wait for #887.
Author
Owner

housekeeping #1009: rebased+failing, left open for review.

restacked row actions on #875; retained lazy handlers and current chunk allocations.

head: a2b294944d132d052fdf3f725d375a68348e8a05; based on dev cd7f84432febfc63b788fbb9b9f9950452982d74, pushed with the original exact force-with-lease.

frontend frozen install, check, lint, build and unit tests passed. bundle gate failed. first-paint brotli vs dev: JS 229010 B (-1767 B), CSS 18164 B (-222 B).

Rust gate results (Mac VM):

exit command
0 cargo fmt -p calternal-core -p calternal-engine -p calternal-notes -p calternal-perf -p calternal-search -p calternal-tasks -- --check
0 cargo check -j4 --locked -p calternal-core -p calternal-engine -p calternal-notes -p calternal-perf -p calternal-search -p calternal-tasks --all-targets
0 cargo clippy -j4 --locked -p calternal-core -p calternal-engine -p calternal-notes -p calternal-perf -p calternal-search -p calternal-tasks --all-targets -- -D warnings
101 cargo test -j4 --locked -p calternal-core -p calternal-engine -p calternal-notes -p calternal-perf -p calternal-search -p calternal-tasks

Engine idle-user fixture fails with missing file; the same failure reproduces on dev. targeted Chromium and WebKit specs at 1440 and 390 never started. the audit waited more than three hours behind the live daily-train-1107 locks (first queue at 23:45 IST, 7 November; final queue cleanup after 02:46 IST, 8 November). browser validation and UI captures are unrun. only this audit’s exact queued units were stopped; the train locks and owners were left intact. hot-path perf qualification is unrun.

all executed gates used the VM Nix environment, shared Mac lock and slot. exact commands, source heads, logs and dev baseline. frontend inputs are identical to the tested frontend tree when a Rust-only repair changed the head.

original-head cherry/patch inventory, pr-877.md; unmatched entries are missing-patch candidates, with semantic overlap/replacements noted above.

final queue, cleanup and source-verification evidence.

housekeeping #1009: rebased+failing, left open for review. restacked row actions on #875; retained lazy handlers and current chunk allocations. head: `a2b294944d132d052fdf3f725d375a68348e8a05`; based on dev `cd7f84432febfc63b788fbb9b9f9950452982d74`, pushed with the original exact force-with-lease. frontend frozen install, check, lint, build and unit tests passed. bundle gate failed. first-paint brotli vs dev: JS 229010 B (-1767 B), CSS 18164 B (-222 B). Rust gate results (Mac VM): | exit | command | |---|---| | 0 | `cargo fmt -p calternal-core -p calternal-engine -p calternal-notes -p calternal-perf -p calternal-search -p calternal-tasks -- --check` | | 0 | `cargo check -j4 --locked -p calternal-core -p calternal-engine -p calternal-notes -p calternal-perf -p calternal-search -p calternal-tasks --all-targets` | | 0 | `cargo clippy -j4 --locked -p calternal-core -p calternal-engine -p calternal-notes -p calternal-perf -p calternal-search -p calternal-tasks --all-targets -- -D warnings` | | 101 | `cargo test -j4 --locked -p calternal-core -p calternal-engine -p calternal-notes -p calternal-perf -p calternal-search -p calternal-tasks` | Engine idle-user fixture fails with missing file; the same failure reproduces on dev. targeted Chromium and WebKit specs at 1440 and 390 never started. the audit waited more than three hours behind the live daily-train-1107 locks (first queue at 23:45 IST, 7 November; final queue cleanup after 02:46 IST, 8 November). browser validation and UI captures are unrun. only this audit’s exact queued units were stopped; the train locks and owners were left intact. hot-path perf qualification is unrun. all executed gates used the VM Nix environment, shared Mac lock and slot. [exact commands, source heads, logs and dev baseline](https://git.kayg.org/attachments/870a0fef-2e31-425c-aea6-eb87a66d365a). frontend inputs are identical to the tested frontend tree when a Rust-only repair changed the head. [original-head cherry/patch inventory](https://git.kayg.org/attachments/e05a38dc-c028-4d77-8655-efa9d5c8ea50), `pr-877.md`; unmatched entries are missing-patch candidates, with semantic overlap/replacements noted above. [final queue, cleanup and source-verification evidence](https://git.kayg.org/attachments/7ab315a5-4b6c-4af1-971b-cdae2637adae).
Author
Owner

Final-port test pass (issue #1009), READY: no: specs pass; bundle gate fails.

head a2b294944d132d052fdf3f725d375a68348e8a05

Targeted specs, CI=1 --retries=0 --workers=1, dev-01 Chromium and dev-02 WebKit (through ~/bin/slot), against a dev-head backend (cd7f84432).

spec Chromium 1440 Chromium 390 WebKit 1440 WebKit 390
bulk-tag, press, visibility 9/9 5/5 9/9 5/5

Bundle gate: perf/web.sh --bundle-only --gate (the 17 failures from the housekeeping audit). Per the gate's own rule (total_js_brotli budget must equal the sum of the named chunk budgets) a cap can only move between named chunks; there is no donor chunk inside this PR, so a fix is a code cut or a decision by kayg. I raised nothing and moved nothing.

  • idle-screens 57.79 vs 50, index 223.64 vs 218.75, press 7.24 vs 7.2. First paint JS -1767 B, CSS -222 B vs dev (good). Stacked on #875, which has the lazy-ui failure.

Not done: Not run yet: Mac VM Safari blur/frame check and perf ABAB x5 on the perf VMs (they need a ready PR first); light/dark 2x screenshots against the mockup are only done for #888 (see its comment).

Not marked READY FOR MERGE.

**Final-port test pass (issue #1009), READY: no: specs pass; bundle gate fails.** head `a2b294944d132d052fdf3f725d375a68348e8a05` Targeted specs, `CI=1 --retries=0 --workers=1`, dev-01 Chromium and dev-02 WebKit (through `~/bin/slot`), against a dev-head backend (`cd7f84432`). | spec | Chromium 1440 | Chromium 390 | WebKit 1440 | WebKit 390 | |---|---|---|---|---| | bulk-tag, press, visibility | 9/9 | 5/5 | 9/9 | 5/5 | **Bundle gate:** `perf/web.sh --bundle-only --gate` (the 17 failures from the housekeeping audit). Per the gate's own rule (`total_js_brotli` budget must equal the sum of the named chunk budgets) a cap can only move between named chunks; there is no donor chunk inside this PR, so a fix is a code cut or a decision by kayg. I raised nothing and moved nothing. - `idle-screens` 57.79 vs 50, `index` 223.64 vs 218.75, `press` 7.24 vs 7.2. First paint JS -1767 B, CSS -222 B vs dev (good). Stacked on #875, which has the `lazy-ui` failure. **Not done:** Not run yet: Mac VM Safari blur/frame check and perf ABAB x5 on the perf VMs (they need a ready PR first); light/dark 2x screenshots against the mockup are only done for #888 (see its comment). Not marked READY FOR MERGE.
Author
Owner

READY FOR MERGE (issue #1009), 11eadd4689

gate result
first paint JS 229191 B: -1964 B (plain) / 229068 B: -1554 B (normalised)
first paint CSS 18152 B: -219 B
specs, Chromium + WebKit, 1440 + 390 green (rows specs: press, lazy-ui, preload, shortcuts, bulk-tag, visibility; the dev failures aside)
perf/web.sh --bundle-only --gate passes (budget: this stack's lines at measured sizes, kayg's approval; total in merge order stays under 543.9 KiB)
perf ABAB x5 + control below
screenshots 2x, light + dark, 1440 + 390; real Safari below / in this thread

First paint. Plain build vs dev (231155 B JS / 18371 B CSS): 229191 B: -1964 B. Hash-normalised vs dev (230622 B): 229068 B: -1554 B. CSS: 18152 B: -219 B. Dev itself fails five phone specs after #1014 on Chromium and WebKit at 390 (glass:76, lazy-ui:70, preload:89 x3); they fail the same way on this PR's head and are not caused by it.

Perf (perf-02, same method, dev 2fa9e18fb; devA/devB are the same dev build). Median of the 5 per-run medians, ms:

metric devA devB (control) #878 tip
typing, long note p50 23.5 22.6 22.2
typing, long note CPU/key 27.3 27.4 27.3
typing, nested list p50 42.9 42.6 42.3
typing, nested list CPU/key 50.5 49.3 50.0
open note, shown p50 35.4 34.8 35.4
open note, back p50 31.1 31.0 31.9
list flick, CPU 76.6 76.6 76.3
longest task in the flick 166 157 150

Inside the control on every metric. (#875 and #877 are inside the tip; the press engine they share is the one measured in the Tasks stack.)

Screenshots, Chromium 2x and real Safari 1x, light and dark next to the mockup: in the comment on #878 (the tip carries all three rows PRs).

**READY FOR MERGE (issue #1009), 11eadd4689cb5ba948df30ab822f10af59ef8eb3** | gate | result | |---|---| | first paint JS | 229191 B: -1964 B (plain) / 229068 B: -1554 B (normalised) | | first paint CSS | 18152 B: -219 B | | specs, Chromium + WebKit, 1440 + 390 | green (rows specs: press, lazy-ui, preload, shortcuts, bulk-tag, visibility; the dev failures aside) | | `perf/web.sh --bundle-only --gate` | passes (budget: this stack's lines at measured sizes, kayg's approval; total in merge order stays under 543.9 KiB) | | perf ABAB x5 + control | below | | screenshots 2x, light + dark, 1440 + 390; real Safari | below / in this thread | **First paint.** Plain build vs dev (231155 B JS / 18371 B CSS): 229191 B: -1964 B. Hash-normalised vs dev (230622 B): 229068 B: -1554 B. CSS: 18152 B: -219 B. Dev itself fails five phone specs after #1014 on Chromium and WebKit at 390 (`glass:76`, `lazy-ui:70`, `preload:89` x3); they fail the same way on this PR's head and are not caused by it. **Perf (perf-02, same method, dev `2fa9e18fb`; `devA`/`devB` are the same dev build).** Median of the 5 per-run medians, ms: | metric | devA | devB (control) | #878 tip | |---|---|---|---| | typing, long note p50 | 23.5 | 22.6 | 22.2 | | typing, long note CPU/key | 27.3 | 27.4 | 27.3 | | typing, nested list p50 | 42.9 | 42.6 | 42.3 | | typing, nested list CPU/key | 50.5 | 49.3 | 50.0 | | open note, shown p50 | 35.4 | 34.8 | 35.4 | | open note, back p50 | 31.1 | 31.0 | 31.9 | | list flick, CPU | 76.6 | 76.6 | 76.3 | | longest task in the flick | 166 | 157 | 150 | Inside the control on every metric. (#875 and #877 are inside the tip; the press engine they share is the one measured in the Tasks stack.) Screenshots, Chromium 2x and real Safari 1x, light and dark next to the mockup: in the comment on #878 (the tip carries all three rows PRs).
Author
Owner

Head c8ac6ee8d: over dev on first paint, kayg's call

Design merge with Sharing (#842/#876) and dev 4274dafb4 (rusqlite 0.41, restore detection, #1044 frame fixes, #1048 lifecycle ids). Done once in the rows base (#875), carried up by merges:

  • write_executor/home.rs: run_note_write(plan, undo_of, caller) takes both the rows undo key and Sharing's caller; plain writes pass (None, None), execute_note_write_for passes the caller, undo passes its key.
  • crates/notes/src/actions.rs: shared::actions and visibility::actions are both chained. contracts/actions.json is the union (notes.hide/archive/unarchive/unhide and notes.hide-shared side by side; actions.gen.ts regenerated with action-types.mjs --check).
  • crates/search/src/index.rs: dev's hand-opened read snapshot (rusqlite 0.41) plus the foreground gauge.
  • Web: NoteList, lists.ts, sidebar.svelte.ts, actions.ts keep both the Archived and the Shared with me lists; Show hidden does not add your own hidden notes to Shared with me; lists.ts reads store.list(key) (#1030) for Archived/Pinned/tags.
  • #904: its duplicated copy of the backend is replaced by the rows branch's, so there is one backend; its own web commits are untouched.

Checks on dev-02 (aarch64), PR-built backend: cargo test -p calternal-notes 623 passed (one run had typing_during_a_tag_rename_is_kept fail while WebKit specs loaded the host; it passes 3/3 alone on this head and on dev), calternal-auth isolation 3, calternal isolation 40, calternal-server access 1, admin-sharing 11 passed in both chromium projects on #878 and #904 (including the new trash/restore test). Specs, 4 projects: preload, lazy-ui, lazy-offline, glass, shortcuts, keys-no-motion, press, bulk-tag, visibility, sidebar-drop, hidden-blocks, slash-menu: 0 failures except keys-no-motion webkit-1440 settings (Control+,), a flake (fails on 845/877/904 runs and passes on rerun, also on #854/#861).

First paint vs dev: +644 / +763 B, CSS +0. This is the Archived place, Show hidden and the hide/archive row actions living in the entry (NoteList, lists, sidebar, visibility-views state, Icon table). I tried making the sidebar's drop targets lazy; it made the first screen 74 B bigger (the module becomes its own chunk plus the dynamic-import glue), so I reverted it. I did not find a cut that gets #877 or #878 to ≤ dev without moving the Archived place and the row actions out of the first screen, which would change what the first list shows. Budget total 535.69 KiB. Gate clean.

## Head `c8ac6ee8d`: over dev on first paint, kayg's call **Design merge with Sharing (#842/#876) and dev `4274dafb4` (rusqlite 0.41, restore detection, #1044 frame fixes, #1048 lifecycle ids).** Done once in the rows base (#875), carried up by merges: - `write_executor/home.rs`: `run_note_write(plan, undo_of, caller)` takes both the rows undo key and Sharing's caller; plain writes pass `(None, None)`, `execute_note_write_for` passes the caller, undo passes its key. - `crates/notes/src/actions.rs`: `shared::actions` and `visibility::actions` are both chained. `contracts/actions.json` is the union (`notes.hide`/`archive`/`unarchive`/`unhide` and `notes.hide-shared` side by side; `actions.gen.ts` regenerated with `action-types.mjs --check`). - `crates/search/src/index.rs`: dev's hand-opened read snapshot (rusqlite 0.41) plus the foreground gauge. - Web: `NoteList`, `lists.ts`, `sidebar.svelte.ts`, `actions.ts` keep both the Archived and the Shared with me lists; Show hidden does not add your own hidden notes to Shared with me; `lists.ts` reads `store.list(key)` (#1030) for Archived/Pinned/tags. - #904: its duplicated copy of the backend is replaced by the rows branch's, so there is one backend; its own web commits are untouched. **Checks on dev-02 (aarch64), PR-built backend:** `cargo test -p calternal-notes` 623 passed (one run had `typing_during_a_tag_rename_is_kept` fail while WebKit specs loaded the host; it passes 3/3 alone on this head and on dev), `calternal-auth` isolation 3, `calternal` isolation 40, `calternal-server` access 1, **admin-sharing 11 passed in both chromium projects on #878 and #904** (including the new trash/restore test). Specs, 4 projects: preload, lazy-ui, lazy-offline, glass, shortcuts, keys-no-motion, press, bulk-tag, visibility, sidebar-drop, hidden-blocks, slash-menu: 0 failures except `keys-no-motion` webkit-1440 `settings (Control+,)`, a flake (fails on 845/877/904 runs and passes on rerun, also on #854/#861). First paint vs dev: **+644 / +763 B**, CSS +0. This is the Archived place, Show hidden and the hide/archive row actions living in the entry (NoteList, lists, sidebar, visibility-views state, Icon table). I tried making the sidebar's drop targets lazy; it made the first screen 74 B bigger (the module becomes its own chunk plus the dynamic-import glue), so I reverted it. I did not find a cut that gets #877 or #878 to ≤ dev without moving the Archived place and the row actions out of the first screen, which would change what the first list shows. Budget total 535.69 KiB. Gate clean.
Author
Owner

Handoff: head c8ac6ee8d pushed; waiting on kayg's first-paint decision

Rows slice 2 is over dev by +644 / +763 B first paint (Archived place and hide/archive actions in the entry). Making the sidebar drop targets lazy made it worse (+74 B) and was reverted. Next step: kayg decides whether to accept the growth or to move the Archived place and row actions out of the first screen (a separate change). Backend design merge with Sharing is done in #875 and verified (notes 623, isolation, admin-sharing 11 on #878/#904).

## Handoff: head `c8ac6ee8d` pushed; waiting on kayg's first-paint decision Rows slice 2 is over dev by +644 / +763 B first paint (Archived place and hide/archive actions in the entry). Making the sidebar drop targets lazy made it worse (+74 B) and was reverted. Next step: kayg decides whether to accept the growth or to move the Archived place and row actions out of the first screen (a separate change). Backend design merge with Sharing is done in #875 and verified (notes 623, isolation, admin-sharing 11 on #878/#904).
Some checks failed
CI / server (pull_request) Has been cancelled
CI / rust (pull_request) Has been cancelled
CI / web (pull_request) Has been cancelled
Markdown extraction contract / markdown-native-contract (pull_request) Has been cancelled
Markdown extraction contract / markdown-editor-contract (pull_request) Has been cancelled
This pull request is broken due to missing fork information.
View command line instructions

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u origin port/rows-actions:port/rows-actions
git switch port/rows-actions

Merge

Merge the changes and update on Forgejo.

Warning: The "Autodetect manual merge" setting is not enabled for this repository, you will have to mark this pull request as manually merged afterwards.

git switch port/rows
git merge --no-ff port/rows-actions
git switch port/rows-actions
git rebase port/rows
git switch port/rows
git merge --ff-only port/rows-actions
git switch port/rows-actions
git rebase port/rows
git switch port/rows
git merge --no-ff port/rows-actions
git switch port/rows
git merge --squash port/rows-actions
git switch port/rows
git merge --ff-only port/rows-actions
git switch port/rows
git merge port/rows-actions
git push origin port/rows
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
kayg/calternal!877
No description provided.