Repository navigation
Forms (#875), continuing Polle's #1281 - #1923
Conversation
The rights walk now consults the per-query verdict memo by subject before fetching an ancestor, and loads ancestors from materialized propvals instead of decoding their Loro snapshots. Query loops stop calling resolve_query_member after 16 consecutive denials while filling a page, so a fully-private listing no longer fetches every match. Co-authored-by: Polle Pas <polleps@gmail.com>
Each member can carry its own read grant, so a streak of private siblings must not skip later readable rows. Keep resolving until the page is full of authorized hits; the ancestor-fetch memo is what makes that cheap. Co-authored-by: Polle Pas <polleps@gmail.com>
FormCondition resources on pages, fields, and layout blocks, evaluated in document order (AND; empty = always visible). Shared fixtures keep the TS renderer and Rust submit validator in lockstep. Hidden required fields are not errors; submitted values for them are dropped. Co-authored-by: Polle Pas <polleps@gmail.com>
Co-authored-by: Polle Pas <polleps@gmail.com>
The compiler emitted a broken _w_ctx reference, which crashed the form builder as soon as the page settings pane mounted the editor. Co-authored-by: Polle Pas <polleps@gmail.com>
Wuchale rewrote "<FaPlus /> Add condition" into W_tx_ with an undefined _w_ctx_, which crashed the form builder as soon as the page pane mounted. Wrap the label in a span so it extracts as a plain string. Co-authored-by: Polle Pas <polleps@gmail.com>
Three independent failures, each masked by the previous one. 1. pnpm 11 aborted every `pnpm run` with ERR_PNPM_ABORTED_REMOVE_MODULES_DIR_NO_TTY. Bumping `packageManager` to pnpm@11.10.0 brought in a deps-status check that runs before each script and re-invokes `pnpm install` itself. That implicit install never sees the `--shamefully-hoist` CLI flag, reads the hoisting config as changed, and tries to purge node_modules — with no TTY to confirm, it aborts. Hoisting moves to PNPM_CONFIG_SHAMEFULLY_HOIST so every invocation agrees; the `yes |` prompt hack is no longer needed. Kept as an env var rather than `shamefullyHoist` in pnpm-workspace.yaml: that file is shared with developers, and flattening node_modules locally would hide undeclared dependencies. 2. `handlers/form.rs` include_str!s assets_tmp/form-assets/index.html, but copy_form_assets deliberately skips when browser/form-app/dist is absent. What build.rs treats as graceful degradation is a hard compile error. dist is gitignored *and* in dagger's source-exclude list, so no container can ever have it — hence a placeholder in build.rs rather than per-container stubs. This also fixes `cargo check` on a fresh clone before `pnpm build`. 3. testdata/form-conditions.json is read from both sides — readFileSync in form-renderer's conditions.test.ts, include_str! in forms.rs's test module — and was mounted in neither container. The Rust one is the worse of the two: it fails compilation of every --all-targets build, taking out clippy and nextest together. Verified on a real engine: js-lint, js-test (774 tests), js-test-integration, rust-clippy and rust-test all green. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
CI's `pnpm run lint` and `cargo fmt --check` were both failing behind the pnpm abort fixed in the previous commit, so the drift had gone unreported. Pure formatting — no behaviour change. 17 JS/TS files via oxfmt (the workspace sets jsxSingleQuote, and the new form code used double quotes) and 6 Rust files via cargo fmt. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Every JS container died ~2 minutes in with ERROR Failed to switch pnpm to v11.10.0. Looks like pnpm CLI is missing at "/root/.local/share/pnpm/.tools/@pnpm+linux-x64/11.10.0/bin" The containers installed `pnpm@latest-10` via corepack (now 10.34.5), and browser/package.json declares pnpm@11.10.0, so the first pnpm invocation in the workspace tried to self-switch. pnpm 10's switch downloads the `@pnpm/<platform>` package and expects the CLI under `<version>/bin`, which pnpm 11 no longer ships there — the switch is broken across the 10→11 boundary (pnpm/pnpm#12528, still open). It happened to work on the engine the last CI fix was verified against, presumably a cached older 10.x layer; against a current latest-10 it fails deterministically. Fix: install the declared version outright. The pnpm 11 npm package is a self-contained 18MB JS bundle, so `npm install -g pnpm@11.10.0` needs no corepack and no platform package, and a matching version never triggers a switch. The version lives in one constant next to a note that it must track browser/package.json. The e2e container had the same latent bug — pinned 10.15.1 via get.pnpm.io, self-switching on its first `pnpm install` — so it takes the same constant. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
Ready for the next TrekMeester batch: everything passes locally (listed in the description), and I'll post the CI result here once it's green. @joepio, for your trekmeester. Posted by Claude on behalf of Michiel. |
Polle's update from #1281, documenting the ancestor-fetch memo in the collection rights walk that this PR carries. It was held back with the unrelated doc edits, but it is about code here. Co-Authored-By: Polle Pas <polleps@gmail.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Note from Michiel's coordination session: the German, Spanish and French translations for the new forms UI text still need to be generated. The project's translation flow ( |
Main pipeline run 36706474093 failed in jsTypecheck: form-app could not resolve `@tomic/form-renderer` or `altcha`, and pnpm warned that its node_modules was missing. jsSource() installs only for the package.json files it copies in first, and form-app and form-renderer were not among them. The typecheck also never built form-renderer's dist. Checked against a replica of the container's layout (only the copied manifests, `pnpm@10 install --frozen-lockfile --shamefully-hoist`, then the full tree): without this change it gives the same four errors as CI; with it, the recursive typecheck passes for all ten packages. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01X1zLLQwLgyqZt3L9GXCRsW
…875) Main runs 36706474093 and 36714878089 failed Downstream with "cannot update the lock file" although atomic-saas#173 (same branch name, open) carries the lockfile entries. Locally, that branch passes `cargo check --locked --all-targets` against this commit and atomic-saas main fails with CI's exact error, so the job built main. The pick step's open-PR lookup ends in `|| echo 0`, so any failure there (for example a token without pull-request read access) read as "no open PR" and fell back to main without a word. A failed lookup now pairs with the namesake branch and emits a warning with gh's error. The cargo step also prints the atomic-saas commit, since the pick step's output falls outside the log tail the API returns. Checked the step script with a stubbed gh: failed lookup, open PR, no PR. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01X1zLLQwLgyqZt3L9GXCRsW
Brings in dacd78d (SearchBoxWindow test stores stay offline), the fix for the data-browser unit-stage failure that turned develop red. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01X1zLLQwLgyqZt3L9GXCRsW
Main pipeline run 36717450608 failed building the e2e server: build.rs panicked with "Failed to spawn `pnpm install`". Mounting `browser/form-app/dist` at /code/browser/form-app/dist is what creates /code/browser in the Rust container. So build.rs no longer takes its "no browser folder, skip the JS build" path. It finds no data-browser/dist, but does find its other watched sources (lib/src, wasm/src), and runs `pnpm install`, which that container lacks. The JS is already built in the JS container and placed in assets_tmp, so set ATOMICSERVER_SKIP_JS_BUILD=true there, as the integration and checks containers already do. build.rs then keeps the mounted assets_tmp and still refreshes assets_tmp/form-assets from the mounted form-app/dist. The same container also builds the release binary, which gets the same fix. Reproduced locally with the branch laid out like that container (no browser/ except form-app/dist, a stub assets_tmp, no pnpm on PATH): `cargo check -p atomic-server` panics exactly as in CI, and with the variable it passes and embeds the form-app bundle, not the placeholder. .dagger regression tests pass. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011KbrNpTmoDhqsVVrSxb64R
|
CI status (atomic-server worker, taking over from the Forms session): Run 36717450608 (head 1853aa2):
Root cause: this PR mounts Fix, 3f91ebd: set Verified locally, with the branch laid out like that container (no
Main pipeline re-dispatched on Generated by Claude Code |
|
CI status for run 36751007165 (head 3f91ebd): This failure is new, and it is not the build.rs problem. The previous fix held: the server build passed and the run reached the I don't think this PR causes it:
So I changed no code and re-dispatched Main once: run 36788767633. If it fails the same way again, the next step is to make the test robust, for example by stubbing the store's resource fetches in Generated by Claude Code |
Main run 36788767633 failed `create a form, sync its properties, and persist across reload` on all three attempts with "Test timeout of 60000ms exceeded", each time at a different step (openSubject back to the Form, the option-label poll, the third "Add option") and never at that step's own timeout. The walk does four full page boots (Table, Form, reload, Table) plus about ten sync waits, and openSubject alone can take 10s+ on a loaded Dagger shard (forms.spec.ts took 498s there). Mark it test.slow() like the breadth test above it, and drop that test's comment claiming the walk does not need it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PzwoGrfrNmVphiKmSEcxGK
|
Re-run 36788767633 failed on something different from the vitest rejection in 36751007165, and this one is the PR's own test. Failure.
None hit the step's own timeout. The walk does four full page boots plus ~10 sync waits, and Fix: 515595a marks the walk New run: https://github.com/ontola/atomic-server/actions/runs/36809778568 Generated by Claude Code |
|
Ready for a batch. Main pipeline run 36809778568 on head 515595a is green: Mancave CI, Downstream (atomic-saas) and Main all passed. It includes the Merge order: ontola/atomic-saas#173 (the paired Posted by Michiel's coordinator agent (Claude Code) Generated by Claude Code |
Conflicts resolved: - useFileObjectUrl: develop split it into useFileObjectUrl(resource) and useBlobObjectUrl(blobDid). The SVG fix's mimetype now travels as an optional third argument; useFileObjectUrl reads it from the File resource. - reconcile.test.ts: develop's Response-based mockFetch and setManagedDeviceToken(null), plus this branch's VITE_MANAGED_API_BASE stub. - CHANGELOG entries kept from both sides; catalogs re-extracted.
|
Trekmeester (for Joep): merged develop into this branch (db31cb7; a merge, no history rewritten). The PR is mergeable again. Conflicts resolved:
Checked locally: data-browser Separate design point (Joep): sharing a form needs a server. In SaaS, |
The share link used the store's server URL, which in the hosted app can be the shared static origin and gives guests a 404. Use the Cloud Server enrollment's node, and without any hosting node replace Share and Publish with a nudge: Cloud Server in the hosted app, a plain pointer to Sync settings elsewhere.
Supersedes #1281. This is Polle's Forms work (@Polleps), brought up to date with develop and finished for a first Beta. Polle's 71 commits are kept as they are; the commits on top are listed below.
Closes #875
What Forms is
A form or survey you build in a drive and share as a link. Guests fill it in without an account, and each answer becomes a row in a Table.
browser/form-apponbrowser/form-renderer) served at/form/{id}, with a self-hosted proof-of-work captcha (ALTCHA), a progress bar, and answers saved on the device until they submit.Still to do from the #875 wishlist, as follow-up issues: password, requiring an account, redirect after submit, "submit another", seeing who submitted.
Commits on top of Polle's branch
lib/src/db.rs: took develop'sDb::from_kv().open()and added the fetch counters there.resource.ts:DERIVED_BY_SERVERmoved toserver-managed-props.tson develop; the form summary property is now added there.urls.rs: a duplicateEMOJIconstant that would not compile.TableResource.tsx: the PR's remote-row rebase is dropped, because develop already shows rows added elsewhere without resetting the session..dagger/src/index.ts: develop's version, plus the form-app mounts..pofiles: develop's versions, regenerated.AGENTS.local.md,CLAUDE.local.md; still gitignored);CLAUDE.md,CONTRIBUTINGport, the wuchale/OpenRouter note, READMEs, the sveltekit template, and two unrelated planning notes.fix(lib): keep forms working on develop's store.notifications. Its atomicdata.dev URLs 404 on the public catalog.initLoroSaveCursorIfFreshre-seat it past the unsent edit, so the retry sent nothing. The cursor is now rewound to the empty version instead.fix(server): bare form ids resolve under develop'satomic:scheme (Emit atomic: identifiers and replace prefix checks with helpers #1585).feat(forms): thank-you message setting in the builder. The runtime already displayed one.fix(forms): keep a published form inside its own drive. Security; see below.creationCatalog.ts).atomic:parents no longer get a path-based subject, which the server refused.filePicker.spec.ts.Security (commit 6), and why this design
Guests have no agent, so the server reads the definition and signs the row itself. Before this commit, anyone who could edit a Form could:
/form/{id}/image.forms::FormScopesets one rule for all three: a form writes only into its own drive, and shows guests only resources from that drive or ones that are public anyway. Why the drive rather than something narrower:Also in that commit:
parent,isA,drive, rights,lastCommit,createdByorpublicKeyon the row.helpers::peer_ipdoes), not onX-Forwarded-For, and forgets stale addresses.Tests
atomic_lib287 passed.atomic-server --lib353 passed, including newFormScopetests and a cross-drive submit inform_submission_flow. Clippy with-D warningsis clean.@tomic/lib1085, data-browser 1495, form-renderer 90, form-app 3; typecheck clean in every package.pnpm test-e2e:local, 2 workers):forms,forms-submission(now also checks the custom thank-you message),forms-from-tables,tables,documents,filePickerandsearch: 33 of 33 passed. The full suite runs on CI.Notes for review
altcha(captcha) andlightningcss(sanitizes custom CSS; an alpha release).🤖 Generated with Claude Code