fix(train #1047): jobs that trash or restore a note fail on the fence's event ID, and the cover pill sits under the desk pills #1048

Closed
kayg wants to merge 0 commits from fix/lifecycle-event-id into dev
Owner

Fixes the red daily train on dev faa4d4aad (#1047). Two separate causes; neither is a spec problem.

1. Jobs that trash or restore a note fail (composer-tasks:62 and :99)

Culprit: c2d74992a "recover edits and fence note lifecycle", merged with #876 (0878cb550). First-parent bisect of 20eab8d3a..faa4d4aad on composer-tasks.spec.ts:62 (chromium-1440, dev-01): d0bf2838c passes, 0878cb550 fails, nothing earlier does.

The job step key is job:<job>:<n>. The job ID carries the engine's 256-byte idempotency namespace (action_key), so the key is always over 200 bytes. Step::Trash, Step::Restore and the delete step now tell the sharing fence about the move under "{key}:sharing-delete" / "-restore", and the fence rejects an event ID over 200 bytes (valid_event_id) with invalid_input. So every job step that trashes or restores a note failed, shared or not: Undo of a task capture (notes.undo answers 400, the composer text never comes back), and the second case, a pasted list's Undo. Reproduced directly: tasks.create then notes.undo {job} on a --dev-user server gives 400 invalid_input.

Fix: Home::transition_share_lifecycle hashes the operation's name (blake3) to a fixed-length ID, lifecycle:<64 hex>. The same operation keeps the same ID, so a retried step still replays its event (the other admission paths already hash theirs the same way). Unit test in home.rs with a real-length key; the spec below runs the whole path.

2. The cover pill is under the desk pills (cover:96 and cover:112)

Not a write problem: in the float layout the note's pills (Share, Page actions, DeskPills, 14 px from the card's top, 32 px tall) float over the card's top-right corner, exactly where the cover pill sat (12 px). Playwright's click log: <button aria-label="Share" class="pill gb share"> ... intercepts pointer events on Done and Remove. Measured at 1440: pills x 1344-1416, y 24-56; Remove x 1336-1407, y 33-59. A real person could not press Remove or Done either.
Fix: on a desktop the cover pill goes to top: 54px, below the pills, as the phone rule already does (top: safe-top + 66px).

Specs

  • admin-sharing: Undo of a task capture works with no share and with its note shared: owner tasks.create, notes.undo must end done with no error; then the same with the task note shared with a recipient, who must lose it. Fails on dev (the job ends with the fence's error), passes here.
  • cover: nothing sits over the cover pill: every cover button is the element at its own centre.

Gates and the dev-vs-fix comparison are in the comment below.

🤖 Generated with Claude Code

Fixes the red daily train on dev `faa4d4aad` (#1047). Two separate causes; neither is a spec problem. ## 1. Jobs that trash or restore a note fail (composer-tasks:62 and :99) **Culprit:** `c2d74992a` "recover edits and fence note lifecycle", merged with #876 (`0878cb550`). First-parent bisect of `20eab8d3a..faa4d4aad` on `composer-tasks.spec.ts:62` (chromium-1440, dev-01): `d0bf2838c` passes, `0878cb550` fails, nothing earlier does. The job step key is `job:<job>:<n>`. The job ID carries the engine's 256-byte idempotency namespace (`action_key`), so the key is always over 200 bytes. `Step::Trash`, `Step::Restore` and the delete step now tell the sharing fence about the move under `"{key}:sharing-delete"` / `"-restore"`, and the fence rejects an event ID over 200 bytes (`valid_event_id`) with `invalid_input`. So **every job step that trashes or restores a note failed, shared or not**: Undo of a task capture (`notes.undo` answers 400, the composer text never comes back), and the second case, a pasted list's Undo. Reproduced directly: `tasks.create` then `notes.undo {job}` on a `--dev-user` server gives `400 invalid_input`. **Fix:** `Home::transition_share_lifecycle` hashes the operation's name (blake3) to a fixed-length ID, `lifecycle:<64 hex>`. The same operation keeps the same ID, so a retried step still replays its event (the other admission paths already hash theirs the same way). Unit test in `home.rs` with a real-length key; the spec below runs the whole path. ## 2. The cover pill is under the desk pills (cover:96 and cover:112) Not a write problem: in the float layout the note's pills (Share, Page actions, `DeskPills`, 14 px from the card's top, 32 px tall) float over the card's top-right corner, exactly where the cover pill sat (12 px). Playwright's click log: `<button aria-label="Share" class="pill gb share"> ... intercepts pointer events` on Done and Remove. Measured at 1440: pills x 1344-1416, y 24-56; Remove x 1336-1407, y 33-59. A real person could not press Remove or Done either. **Fix:** on a desktop the cover pill goes to `top: 54px`, below the pills, as the phone rule already does (`top: safe-top + 66px`). ## Specs - `admin-sharing`: *Undo of a task capture works with no share and with its note shared*: owner `tasks.create`, `notes.undo` must end `done` with no error; then the same with the task note shared with a recipient, who must lose it. Fails on dev (the job ends with the fence's error), passes here. - `cover`: *nothing sits over the cover pill*: every cover button is the element at its own centre. Gates and the dev-vs-fix comparison are in the comment below. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
A job step key is job:<job>:<n>, and the job ID carries the engine's 256-byte
idempotency namespace, so the key is always over the fence's 200-byte event ID
limit. c2d74992a (#876) passed it to the fence unchanged, so every job step that
trashes or restores a note failed with invalid_input, shared or not: Undo of a
task capture (composer-tasks.spec:62) and the like. The ID is now a fixed-length
hash of the operation's name; the same operation keeps the same ID.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
fix(web): the cover pill sits below the desk pills on a desktop, so Done and Remove get their clicks (train #1047)
Some checks are pending
CI / server (pull_request) Waiting to run
CI / rust (pull_request) Waiting to run
CI / web (pull_request) Waiting to run
Markdown extraction contract / markdown-native-contract (pull_request) Waiting to run
Markdown extraction contract / markdown-editor-contract (pull_request) Waiting to run
c6981fbd9b
The note's pills (Share, Page actions) float over the card's top-right corner,
where the cover pill sat, and took its clicks: Reposition's Done and Remove could
not be pressed. Adds a spec that no cover button has something on top of it.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Author
Owner

Gates for #1048, head 41ed1c692 (dev-01, x86_64, Chromium and WebKit, one ~/bin/slot each; CALTERNAL_BIN = this branch's build).

Bisect (composer-tasks.spec.ts:62, chromium-1440, first-parent 20eab8d3a..faa4d4aad, 19 merges): faa4d4aad fails (reproduced) -> 1619002ac pass -> d0bf2838c pass -> 7b03bfcea fail -> 0878cb550 fail. First bad: 0878cb550 (Merge #876), last good d0bf2838c. Inside #876 the commit is c2d74992a (the step keys it passes to the fence). Server-side repro on the #876 build: tasks.create, then notes.undo {job} -> 400 invalid_input.

Gate Result
composer-tasks + cover, --repeat-each=2, chromium-1440 20 passed, 2 skipped
same, chromium-390 18 passed, 4 skipped
same, webkit-1440 20 passed, 2 skipped
same, webkit-390 18 passed, 4 skipped
admin-sharing --repeat-each=2 on admin-chromium-1440/390 + admin-webkit-1440/390 (incl. the new spec) 41 passed, 0 failed
new spec jobs that trash or restore a note... with the fix, 4 admin projects passed
same spec with dev's home.rs (negative control, admin-chromium-1440) fails (the job ends with the fence's error)
cargo test -p calternal-notes 572 passed, 0 failed, 6 ignored (includes the new unit test)
cargo clippy --workspace -- -D warnings, cargo fmt --check -p calternal-notes clean
svelte-check 0 errors, 18 warnings (unchanged)
vitest 22 files, 223 tests passed

Notes:

  • One chromium-390 run of composer-tasks + cover (before the final head) exited 1 with no failing test listed in its summary; the same command passed on re-run and in the --repeat-each=2 run above. I did not find the cause; flagging it as unexplained, not hidden.
  • Not run: the perf VMs. The change adds one blake3 hash (under 1 KB) per Trash/Restore job step and 5 lines of CSS in cover.css; first-paint bytes were not measured against dev.
  • Could not save on a Can-edit shared note (separate from this fix, still being checked): over plain http with --dev-user the recipient's room is refused with forbidden, because a recipient's write needs an installation and a --dev-user session has none (ensure_share_controls_applied). A real sign-in has one: the admin spec edits as a recipient on the real https server and reaches the owner. I have not yet run the reload-persistence spec on dev; that follows.
**Gates for #1048, head `41ed1c692`** (dev-01, x86_64, Chromium and WebKit, one `~/bin/slot` each; `CALTERNAL_BIN` = this branch's build). **Bisect** (`composer-tasks.spec.ts:62`, chromium-1440, first-parent `20eab8d3a..faa4d4aad`, 19 merges): `faa4d4aad` fails (reproduced) -> `1619002ac` pass -> `d0bf2838c` pass -> `7b03bfcea` fail -> `0878cb550` fail. **First bad: `0878cb550` (Merge #876), last good `d0bf2838c`.** Inside #876 the commit is `c2d74992a` (the step keys it passes to the fence). Server-side repro on the #876 build: `tasks.create`, then `notes.undo {job}` -> `400 invalid_input`. | Gate | Result | |---|---| | composer-tasks + cover, `--repeat-each=2`, chromium-1440 | 20 passed, 2 skipped | | same, chromium-390 | 18 passed, 4 skipped | | same, webkit-1440 | 20 passed, 2 skipped | | same, webkit-390 | 18 passed, 4 skipped | | admin-sharing `--repeat-each=2` on admin-chromium-1440/390 + admin-webkit-1440/390 (incl. the new spec) | 41 passed, 0 failed | | new spec *jobs that trash or restore a note...* with the fix, 4 admin projects | passed | | same spec with dev's `home.rs` (negative control, admin-chromium-1440) | **fails** (the job ends with the fence's error) | | `cargo test -p calternal-notes` | 572 passed, 0 failed, 6 ignored (includes the new unit test) | | `cargo clippy --workspace -- -D warnings`, `cargo fmt --check -p calternal-notes` | clean | | svelte-check | 0 errors, 18 warnings (unchanged) | | vitest | 22 files, 223 tests passed | Notes: - One chromium-390 run of composer-tasks + cover (before the final head) exited 1 with no failing test listed in its summary; the same command passed on re-run and in the `--repeat-each=2` run above. I did not find the cause; flagging it as unexplained, not hidden. - Not run: the perf VMs. The change adds one blake3 hash (under 1 KB) per Trash/Restore job step and 5 lines of CSS in `cover.css`; first-paint bytes were not measured against dev. - `Could not save` on a Can-edit shared note (separate from this fix, still being checked): over plain http with `--dev-user` the recipient's room is refused with `forbidden`, because a recipient's write needs an installation and a `--dev-user` session has none (`ensure_share_controls_applied`). A real sign-in has one: the admin spec edits as a recipient on the real https server and reaches the owner. I have not yet run the reload-persistence spec on dev; that follows.
Author
Owner

Merged into dev at dd6eebd43 (head 41ed1c692); closing.

Merged into dev at `dd6eebd43` (head `41ed1c692`); closing.
kayg closed this pull request 2026-11-10 05:03:37 +00:00
Some checks are pending
CI / server (pull_request) Waiting to run
CI / rust (pull_request) Waiting to run
CI / web (pull_request) Waiting to run
Markdown extraction contract / markdown-native-contract (pull_request) Waiting to run
Markdown extraction contract / markdown-editor-contract (pull_request) Waiting to run

Pull request closed

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!1048
No description provided.