Skip to content

Batch 2026-10-01 (2): drive-unreachable screen, view tabs, row comments, AI fixes, presence order, vault quiet, canvas selection (12 PRs) - #1966

Merged
joepio merged 35 commits into
developfrom
claude/batch-2026-10-01b
Oct 2, 2026
Merged

joepio merged 35 commits into
developfrom
claude/batch-2026-10-01b

Conversation

@joepio

@joepio joepio commented Oct 1, 2026 •

Copy link
Copy Markdown
Member

Requested by Joep

Before: finished PRs by Joep, Polle and Michiel each waited for a Mancave run. Some were behind develop, and some had small bugs that only show up combined with current develop.

After: they sit together on one branch off develop (1f892aa, after #1947), decided by one full run. Squash-merge this, then close the included PRs. Every PR was read before it went in; big or impactful ones stay out with a review comment.

Included:

Fixes made while batching:

Left out, with reasons on the PRs:

Checked locally: data-browser and e2e tsc, pnpm run -r lint, and data-browser vitest 1519/1519. The only @tomic/lib failure is genesis-roundtrip.integration, which needs atomicdata.dev's loroUpdate property; that currently 404s from outside CI.

claude and others added 30 commits September 17, 2026 21:50
A table row is a resource of its own, so its comment thread is already the
ordinary one — Messages whose `about` points at the row. What was missing was
a way to reach it: the Comments panel could only ever be about the page's own
resource, and a row is never that.

Two pieces:

- The grid takes a `RowHeaderAddonComponent`, rendered in each row's gutter
  left of the row number. It knows rows only by index, so the table page
  supplies the index -> subject mapping (`getRowSubject`) and the component.
  The gutter is widened to hold a bubble beside the row number, and row-scoped
  affordances marked `data-row-affordance` are revealed by hovering anywhere in
  the row rather than only over the gutter.
- The panel gains a target. `commentSubject` says which resource the thread is
  about, `undefined` meaning the page's own — what the NavBar button opens.
  Aiming it at another resource switches threads instead of toggling, so
  clicking row 3's bubble while row 1's thread is up does what it looks like.
  A close request carries no target and so cannot be expressed as a toggle:
  `closePanelState` closes the panel whatever it is aimed at, which is what a
  deleted target, a navigation and the drawer's own dismiss all need.

The bubble stays put, with its count, for a row that has comments (highlighted
while some are unseen) and appears on hover for one that has none.

Not covered: reaching the bubble by keyboard. It shares the gutter's
mouse-only reachability with the existing expand button, because the grid owns
Tab for cell navigation.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SooZ4RQshFY8YncRNq6Qpe
The gutter bubble never appeared for the rows it most needed to: the ones you
had just typed. It asked the collection for every row's subject and treated
`index >= memberCount` as "no resource yet", which is not what that boundary
means. A row added this session keeps its local `_new:` key for its whole life
on the page — materializing persists it but deliberately does NOT turn it into
a collection member, so react-window never remounts it mid-edit. Past the
member count sat real, saved rows, reported as having nothing to point at.

So the context now reports which of the two kinds a row is (`rowSource`),
mirroring what `TablePage/TableRow.tsx` itself renders, and the bubble uses the
matching hook for each: `useMemberFromCollection` for a member,
`useResource` on the `_new:` key for a session row, which the store aliases to
the real subject once it materializes. The two cases genuinely need different
hooks — a session row has no subject at all until then — which is why this
reports the source rather than resolving to a subject.

Caught by `table-row-comments.spec.ts`, which now passes end to end against a
real server.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SooZ4RQshFY8YncRNq6Qpe
Choosing Calendar from the menu of a table's only view tab turned that
view into a calendar, and the table layout was gone (#1806). The user
should always be able to switch between all the applicable views.

- A tab's menu now has an "Add a view" section (built-in kinds and
  apps), the same as the + tab: it adds a view named after the type and
  switches to it, leaving the tab's view intact.
- "Change this view to" is offered only while another view of the
  current type remains, so the last table view (or the only view of an
  app) can never be converted away. setViewKind enforces the same rule
  and adds a view instead.
- Delete is disabled on the last table view while other views exist.
- A converted view whose name was a default ("Default View", a type
  label, a template view name, or an "All <rows>" table name) takes the
  new type's name; a name a person chose is kept.

Closes #1806

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
useDriveApps published nothing until its whole mount chain had finished:
findSchema, the local app query, then an awaited getResource per app, one
after the other. Live events that landed meanwhile were held back too. After
a reload, an app whose sync delta could not apply to the page's copy
("incomplete Loro import ... missing base state") sits as a placeholder under
repair, and getResource waits up to 10 s on it, which is as long as the
money e2e waits for the "New app" menu item.

- Attach the listeners before anything else and fold every answer in as it
  arrives, reading the live resource each time, so no answer overrules a
  newer one and no slow read holds back the others.
- Read the query's members in parallel.
- Expose refresh(), and call it when "+ Add view" and a view tab's menu
  open, so opening a menu re-runs the query and re-reads what it knows.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sets up a fresh Claude Code on the web container so agents can lint, test
and run the stack straight away: the pinned Rust toolchain and WASM targets,
wasm-pack, wasm-opt, pnpm install, the @Tomic package builds, Playwright's
browsers for @tomic/e2e and a warm debug build of atomic-server (which also
builds data-browser and the WASM).

Two sandbox workarounds:
- The proxy blocks cdn.playwright.dev, so the Chrome for Testing builds
  come from the storage.googleapis.com mirror.
- Containers have no IPv6, so ATOMIC_IP=0.0.0.0 is exported for the
  session; atomic-server binds to :: by default and fails otherwise.

Only runs when CLAUDE_CODE_REMOTE=true. .gitignore now tracks the shared
hook and settings while keeping local .claude state ignored, and AGENTS.md
says to keep the hook in sync with the environment requirements.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0168N2XS6KXcewxhP3XTwDa6
The 45 second stall notice is a fixed deadline, so a machine that is slow and
one that is stuck report the same thing. Sentry ATOMIC-BROWSER-1J holds twelve
of them across four releases, spread over every step (identity 7, workspace 3,
storage 2, and one that never left the sign-in check), from Chrome on Windows
and Mac alike, and nothing in them says which kind it was. Setup measures 17
to 28 seconds on a desktop CPU, so 45 seconds is under twice the ordinary
time, and a slow laptop could reach it while still making progress.

The report now carries how long each step took and whether the tab was in the
background, which browsers throttle. And a run that does finish after the
notice sends a second, informational report with its total time and its
slowest step, so a stall that ends is a slow machine, and one with no such
follow-up is a hang.

That is all this changes: what to fix depends on which of the two the reports
turn out to be, and twelve events do not say.
The message was the same for the status check, both start calls and both finish calls, and dropped the HTTP status. It now ends with the request and status, for example (/use/finish: 401).
Only a cloud environment's setup script is cached between sessions; hooks
run every session on a fresh clone. The previous hook redid the whole cold
setup, server build included (10-20 min), while blocking the session.

Split it in three:
- cloud-setup.sh installs toolchains, node_modules, Playwright browsers and
  crates, skipping what is present. It fits the setup script's five-minute
  cache limit (about a minute from cold), so AGENTS.md suggests calling it
  from there; the hook also runs it, so sessions work without that.
- cloud-build.sh builds the @Tomic packages and the debug atomic-server,
  writing packages.done / server.done markers under /tmp/atomic-setup.
- session-start.sh exports ATOMIC_IP, runs cloud-setup.sh, starts
  cloud-build.sh in the background and tells the agent, through
  additionalContext, how to wait for it.

With a cached environment the hook now blocks for about a minute (mostly
linking node_modules) instead of the full build.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0168N2XS6KXcewxhP3XTwDa6
A second tab signing in as a different account takes over the shared portal
cookie. A backup pass the first tab already had in flight then reaches the
control plane as that account and is told the enrollment does not exist,
which was logged as a failed backup. When the signed-in account no longer
matches the one the pass started for, it is now skipped quietly, like the
401 case.
A drive with no copy here and nothing to fetch it from (a local-only drive
while no other device is open, or no server at all) now gets its own page:
what is going on, the Cloud Vault restore first when a backup exists, a
device diagram, automatic retries every 10 seconds, and the raw error under
Technical details. The open drive counts as a drive even before this device
registered it as local-only.
subscribePresence() is deferred behind authenticate().then(), while the
presence manager sends its first update (and reSubscribeAll() its
rebroadcast) synchronously. The update reached the server ahead of the
subscribe frame and was dropped as 'presence update from non-subscriber'.

Hold presence updates for a drive while its subscribe is pending and send
them right behind it. Nothing is held before authentication, and a
withdrawn subscription drops what was held, so the server's subscribe-time
read check stays the only gate.
…ng a raw error

# Conflicts:
#	browser/data-browser/src/locales/de.po
#	browser/data-browser/src/locales/en.po
#	browser/data-browser/src/locales/es.po
#	browser/data-browser/src/locales/fr.po
…e the last one

# Conflicts:
#	browser/data-browser/src/locales/de.po
#	browser/data-browser/src/locales/en.po
#	browser/data-browser/src/locales/es.po
#	browser/data-browser/src/locales/fr.po
# Conflicts:
#	browser/data-browser/src/chunks/AppPage/useDriveApps.live.test.tsx
#	browser/data-browser/src/chunks/TablePage/TableViewTabs.tsx
Resolved: RightPanelContext keeps develop's overlay block and adds
commentSubject. Fixed: an unsaved draft row (develop now pre-mints drafts
with their final subject) no longer shows a comment bubble; it is hidden
while resource.new.
Resolved against develop's #1876 startRun: the step callback keeps the
performance.mark and now also records stepStarts. DemoManifest is no longer
used here.
joepio added 4 commits October 1, 2026 20:21
…can call (e2eTestResults was 'unknown command')
- CanvasPage: the develop merge declared FAN_DRAG_THRESHOLD twice (#1962 landed
  on develop and in this batch); keep it once, each constant with its comment.
- #1960: usageMetadata/compactionTokens as a pure module with tests, including
  that the last tool step's input wins once the SDK merges every part.
- #1523: rowIsCommentable, tested with a real deferred-genesis draft from the
  store (final subject, still new) and the legacy _new: key.
- #1941: DriveUnavailable test: the 10 s retry and Retry now must not leak a
  rejection (fails with an unhandled rejection without the fix).
@joepio
joepio merged commit 5e189e6 into develop Oct 2, 2026
8 checks passed
@joepio
joepio deleted the claude/batch-2026-10-01b branch October 2, 2026 06:23
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.

2 participants