Repository navigation
Preview only, do not merge: easier onboarding, all work combined - #1364
Draft
JorimTheuns wants to merge 69 commits into
Draft
JorimTheuns wants to merge 69 commits into
JorimTheuns wants to merge 69 commits into
Conversation
…rst value once Server events for a host go to the same PostHog person the browser identifies: the lowercased email, through hostDistinctId in @dembrane/analytics, else the Directus user id the browser aliases. New events for the project's creator: server_conversation_created (portal or upload), server_transcript_ready (after the finalize claim) and server_first_value_reached (the first conversation in a non-sample project they created). First value is claimed in host_milestone (migration 0050), whose primary key lets one caller through, so retries and concurrent conversations send it once. The migration backfills hosts who already had a conversation. /v2/me gains is_invited: an invite to the email existed before the account did. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BKdjPZBaxnCMragWmqzZTL
…ng funnel Hosts are identified by their lowercased email, as before, so existing history stays on its person. HostIdentitySync (inside Protected) sets email, is_invited and org_id from /v2/me on every signed-in page and aliases the Directus user id once, so server events join the same person. The portal never identifies participants. Email is no longer an event property on login and registration. src/lib/analytics/events.ts holds one typed map of events and a small track() wrapper; the onboarding path moves to it with names unchanged, and every event carries flow_version 1. New: onboarding and project wizard steps, project_create_abandoned, key_terms_updated, portal_link_shared, project and portal settings saved (field names only), transcript_viewed and summary_viewed. Later onboarding events are reserved in the map. Catalogs re-extracted for moved source lines. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BKdjPZBaxnCMragWmqzZTL
The banner that replaces the sign prompt says which document it was about when it is closed. The tasks summary now carries next_task_document_id and next_task_document_kind; the frontend copy of the contract is synced. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BKdjPZBaxnCMragWmqzZTL
- What the signer typed is kept per document in session storage (never the signature), so a reload continues at the same field. Cleared once signed and when someone else signs in or out. Works with storage blocked. - A warning before leaving while a drawn signature, or input the browser could not keep, would be lost. - Separate messages for signed out (401), no access (403) and not found (404); the last two say which address is signed in and switch to the one the offer was sent to. - The tasks prompt is a slim banner at the top of the dashboard instead of a modal; closed, it returns in the next browser session until nothing waits. - Signing events through posthog, with only the document id and kind and the step. - Dutch for the new strings written by hand; other locales await the fill step. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BKdjPZBaxnCMragWmqzZTL
de-DE, fr-FR, es-ES, it-IT, uk-UA and cs-CZ written by hand, in the register and words each catalog already uses, without the fill step. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BKdjPZBaxnCMragWmqzZTL
Mantine fixes Alert to role="alert", which a screen reader announces on every page load. The block is now a Paper with the same rule and tint, a labelled region, and a test that it is never an alert. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BKdjPZBaxnCMragWmqzZTL
document_signing_resumed {step_index} is sent when a stored draft is put back.
document_signing_abandoned is sent at most once per page life, never after
document_signed, and not for the development double mount.
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BKdjPZBaxnCMragWmqzZTL
…ranch Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BKdjPZBaxnCMragWmqzZTL
…t branch Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BKdjPZBaxnCMragWmqzZTL
Contributor
|
Preview is up for
Updated on every push while the PR has the preview label. |
… is created @dembrane/projects gains a template registry (project-templates.ts): five typed presets (conversation, series, event, upload, blank) checked with zod, each a subset of PROJECT_UPDATE_FIELDS plus the portal's name label, round tags and Verify topics, worded in the project's language. blank is exactly what the create wizard wrote before. No preset touches anonymize_transcripts, the legal basis or anything paid. POST /v2/workspaces/:id/projects takes an optional template_key and writes the preset in the creating transaction, through projects' storage as the clone does, on the free path under the allowance lock and on the paid path alike. An unknown key is a 422 project.template_unknown and writes nothing. Without a key a project starts bare, as before, so old clients and the iOS app keep working. GET /v2/project-templates lists each template with a summary of what it sets up, for the cards. The project keeps template_key and template_version (migration 0051, nullable). The workspace project list says which project is the sample. tenancy may now import projects (package map regenerated). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BKdjPZBaxnCMragWmqzZTL
Creating a project is two choices: what the host is going to do (one card per template, or a quiet "Start blank"), then a name. The server applies the template, so the wizard no longer PATCHes its fixed defaults; it only sends the context and key terms the host typed. Key terms, access, the assistant switch and the review sit behind "More options" with their old defaults. The upload template lands on the upload page. Blocks: SelectableCardGrid (common, a radio group on Mantine's Radio.Card with one tab stop and arrow keys), TemplateCard and TemplatePicker (props in, callbacks out) with TemplatePickerConnected to fetch GET /v2/project-templates. The facts on each card come from the template's summary. A new self-serve host (not invited, no projects of their own) goes from onboarding to the picker in their workspace; invited and returning hosts land on /o as before. Workspace home shows the picker as the one primary action while the host has only the sample. data-tour anchors on the picker and project home's key areas. A new project's language follows the dashboard's language for all eight (the platform transcribes and summarises each), English otherwise. Analytics: flow_version 2. project_template_selected (template_key, surface) when a host commits to a template, template_key on project_created, project_create_step_viewed sends template, name and more_options. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BKdjPZBaxnCMragWmqzZTL
…h word each for tags and key terms New strings extracted and compiled. English is the source and Dutch is written by hand with "je"; German, French, Spanish, Italian, Ukrainian and Czech are model-translated in each catalog's own form of address and listed in machine-translations.json for a native check. Dutch: key terms are "kernbegrippen" everywhere (as the nl-NL docs say; the one "sleutelterm" goes), and tags are "tags" everywhere, as most of the app already said: "Trefwoorden" read as key terms. Two formal "u" left in tag strings now say "je". Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BKdjPZBaxnCMragWmqzZTL
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BKdjPZBaxnCMragWmqzZTL
…test branch Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BKdjPZBaxnCMragWmqzZTL
The limiter's counters already know when their window ends; check() now puts that in retry_after_seconds on rate_limit.exceeded. The dashboard may read Better Auth's X-Retry-After (exposed through CORS). Registration's email shape rule moves to its own module so the form can mirror it, with a test that fails if the two differ. No limit, window, key or store changes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BKdjPZBaxnCMragWmqzZTL
…again Every login failure goes through describeAuthError with its own message: wrong credentials (kept generic), unverified (with resend), rate limited (countdown from the server's wait, Log in off until it ends), blocked before reaching dembrane (other network or browser, the emailed code, support), offline and server. A failure after the sign-in worked is no longer a failed login: the person is told they are in and can carry on. user_login_failed gains cause and http_status; login_rate_limit_shown and login_blocked_shown are new. Forgot your password is always shown. Registration applies the server's email rule, "Change email" goes back to the email field with the other answers kept, the fields carry password manager hints, and password length counts code points as the server does. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BKdjPZBaxnCMragWmqzZTL
Seven new strings, written by hand in each catalog's form of address and recorded in machine-translations.json for review. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BKdjPZBaxnCMragWmqzZTL
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BKdjPZBaxnCMragWmqzZTL
…t branch # Conflicts: # dembrane/frontend/src/locales/cs-CZ.po # dembrane/frontend/src/locales/cs-CZ.ts # dembrane/frontend/src/locales/de-DE.po # dembrane/frontend/src/locales/de-DE.ts # dembrane/frontend/src/locales/en-US.po # dembrane/frontend/src/locales/en-US.ts # dembrane/frontend/src/locales/es-ES.po # dembrane/frontend/src/locales/es-ES.ts # dembrane/frontend/src/locales/fr-FR.po # dembrane/frontend/src/locales/fr-FR.ts # dembrane/frontend/src/locales/it-IT.po # dembrane/frontend/src/locales/it-IT.ts # dembrane/frontend/src/locales/nl-NL.po # dembrane/frontend/src/locales/nl-NL.ts # dembrane/frontend/src/locales/uk-UA.po # dembrane/frontend/src/locales/uk-UA.ts
…and overrides on create POST /v2/workspaces/:id/project-setup/suggestions proposes key terms and a few settings before the project exists, from the name, context, pasted text and one linked page. One text_fast call with a JSON schema; every term is checked in code afterwards (at most 30 with the host's own, 6 words, 60 characters, found literally in what the host gave or the page we read, no common words, deduped, the source's capitals). Settings only from the template registry's preset fields, on top of the chosen template. The page is read on our server through webhooks' guarded fetch (public addresses only, every redirect checked, 5 s, 1 MB, html or plain text). Same permission as creating a project, rate limited per person, metered against the workspace's AI budget. Web search stays off behind PROJECT_SETUP_WEB_SEARCH. The workspace project create takes optional overrides (preset fields only), written over the preset in the same transaction. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BKdjPZBaxnCMragWmqzZTL
…reate flow, and suggest key terms in the portal editor The name step gets an optional block: one text box, an optional link and "Suggest key terms". Proposed terms come back as chips with a reason each (accept, remove, accept all, "x of 30" near the cap) and any settings worth changing as checkboxes over the template. Accepted terms land in the key terms the wizard already submits; ticked settings ride on the create as overrides. Create is never held up. The portal editor's key terms get the same block, key terms only. The field labels and value marks of the assistant's proposal card move into a shared piece both use. Analytics carry counts and flags only. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BKdjPZBaxnCMragWmqzZTL
Written by hand in each catalog's terms (kernbegrippen in Dutch, informal je; German keeps the catalog's Sie), and listed in machine-translations.json for review. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BKdjPZBaxnCMragWmqzZTL
While the blocked notice shows, it carries "Email me a sign-in code instead" and the same link under Log in steps aside, as every notice carries its own way out (the signed-in notice's Continue). Forgot your password stays. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BKdjPZBaxnCMragWmqzZTL
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BKdjPZBaxnCMragWmqzZTL
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BKdjPZBaxnCMragWmqzZTL # Conflicts: # dembrane/frontend/src/locales/cs-CZ.ts # dembrane/frontend/src/locales/de-DE.ts # dembrane/frontend/src/locales/en-US.ts # dembrane/frontend/src/locales/es-ES.ts # dembrane/frontend/src/locales/fr-FR.ts # dembrane/frontend/src/locales/it-IT.ts # dembrane/frontend/src/locales/nl-NL.ts # dembrane/frontend/src/locales/uk-UA.ts
A host's own label (a table, a meeting, an interview code) gets no example, so nothing invites a person's name; the default "Session name" gets a neutral one, "Group 1, Table 3, etc.", in eight languages (model-written, in the ledger). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BKdjPZBaxnCMragWmqzZTL
Walks every template's host script in every language: ECHO only with Verify and Explore both on, and then before either choice; Verify or Explore alone only when that one is on. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BKdjPZBaxnCMragWmqzZTL
…and without a label Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BKdjPZBaxnCMragWmqzZTL
Every German string addressed to the reader moves from Sie and Ihr to du and dein, in the main, accounts, errors and templates catalogs (1,014 strings). Where Sie means they or she it stays. Each changed string is in the catalog's machine-translations.json for a native speaker to review. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BKdjPZBaxnCMragWmqzZTL
The portal's German tutorial cards (16 texts) and the default host guide title. The privacy cards stay as they are. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BKdjPZBaxnCMragWmqzZTL
The server catalog's German messages (119: emails, invitations, notices), the 14 German prompt templates and the German conversation summary prompt in reports address their reader as du. Server messages are in the catalog's machine-translations.json for review; the recorded Jinja output follows the summary prompt. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BKdjPZBaxnCMragWmqzZTL
The report prompt in reports/src/templates.ts and the chat package's copies of the three German chat prompts now match the converted templates; the offer email test and the chat prompt fixture expect the new German. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BKdjPZBaxnCMragWmqzZTL
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BKdjPZBaxnCMragWmqzZTL
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BKdjPZBaxnCMragWmqzZTL # Conflicts: # dembrane/frontend/src/locales/cs-CZ.ts # dembrane/frontend/src/locales/de-DE.ts # dembrane/frontend/src/locales/en-US.ts # dembrane/frontend/src/locales/es-ES.ts # dembrane/frontend/src/locales/fr-FR.ts # dembrane/frontend/src/locales/it-IT.ts # dembrane/frontend/src/locales/nl-NL.ts # dembrane/frontend/src/locales/uk-UA.ts
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BKdjPZBaxnCMragWmqzZTL # Conflicts: # dembrane/frontend/src/features/accounts/locales/de-DE.po # dembrane/frontend/src/features/accounts/locales/de-DE.ts # dembrane/frontend/src/locales/de-DE.ts # dembrane/frontend/src/locales/machine-translations.json
The seven German login and recovery messages this PR adds address the reader as du, as the rest of German does once #1371 lands (model-written, in the ledger). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BKdjPZBaxnCMragWmqzZTL
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BKdjPZBaxnCMragWmqzZTL # Conflicts: # dembrane/frontend/src/locales/de-DE.ts # dembrane/frontend/src/locales/machine-translations.json
CommonDropzone rendered its `accept` prop as the accept-state content, so the audio upload's list of types was printed while dragging and never filtered. `accept` is now the drop zone's own setting (types with their extensions) and the accept-state content is `acceptContent`. The audio upload passes the same types and extensions isAudioFile checks, so valid files behave as before and anything else is refused by the drop zone with the existing "unsupported format" message. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BKdjPZBaxnCMragWmqzZTL
…in one budget project.context has no length limit and was injected whole into about ten prompts. contextForPrompt (@dembrane/core) returns it unchanged up to 4,000 characters and cuts a longer one at a paragraph, line or sentence boundary; the stored text is never changed. Routed through it: the assistant's first turn (agentic runs), chat and chat summaries, the conversation summary, Explore replies, reports, canvas (gather and ticks), the map's titles and fact-checks, Verify and the outside agents' project tool. A test fails when a source reads `project.context` (or p/row/prepared) without the helper, unless listed with why it is not a prompt. The context field's quiet note now shows past the same 4,000 characters and says that only the start is read. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BKdjPZBaxnCMragWmqzZTL
…nto a project setup POST /api/v2/workspaces/:id/project-setup/digest takes the text the browser extracted from a host's files (never a file), pasted text and a link, and proposes name, goal, context, key terms, a listed template with its reason and two runner-ups, rounds as tags, the name box label, Verify topics, a custom Explore prompt and host script steps, each with provenance. Same gates as suggestions (project:create, its own limit of 10 per hour per person, the AI budget, the payer wrapper) and the same guarded page read. Long sources (over 6,000 characters) are condensed first with one call each; the digest call reads at most 30,000 characters and every cut says so in `notes`. The rules in code: key terms as suggestions (now for named sources), a listed template or nothing, built-in Verify topics, short deduped tags, names and labels that share words with the material, a hard-capped context without e-mail addresses, phone numbers or runs of more than five personal names, and provenance only where the snippet occurs in the named source. Source text is fenced and cannot close its fence. Months and weekdays join the stoplist. Creating a project takes `digest` beside the template and overrides: context, key terms, tags in place of the template's, name label, Verify topics, an Explore prompt (custom mode), host script steps appended to the template's guide and the goal as the first goal revision, all in the creating transaction and held to an allow-list. test/digest-quality runs five synthetic source sets against naive and adversarial fake models; --real runs them on Vertex. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BKdjPZBaxnCMragWmqzZTL
… project Above the template picker: "Have an agenda or invitation? Drop it here and dembrane sets the project up." A drop zone (also the file dialog, pasted text and a link; dropping anywhere on the picker page works too) reads PDF (text layer, pdf.js), .docx and .pptx (body, slides and speaker notes, fflate) and text or Markdown in the browser: up to 5 files of 10 MB and 200,000 characters, each file's problem shown on its own, scans, old Office formats, spreadsheets and pictures refused with what works instead. Only the text, the file name, its kind and length are sent. The extractors load on the first drop. The review "Here is what dembrane understood" shows name, goal, context, key terms, the template with its reason and runner-ups, rounds as tags, Verify options, the name box, the Explore question and host script steps, each edited in place or switched off, each with its source and snippet. Create sends the template and what is on in one request. A failed or empty digest falls back to the picker with the text in the project context, cut to the 4,000 character budget. On the name step a small drop zone fills the context and proposes key terms. The blocks and their own catalog are lazy, so the portal's first load does not grow. Analytics: project_digest_started, _ready, _failed, _applied and started_from on project_created, counts and buckets only. fflate 0.4.8 was already in the lockfile (via posthog-js); it is now declared, no new package. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BKdjPZBaxnCMragWmqzZTL
… sizes and title order Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BKdjPZBaxnCMragWmqzZTL
The compiled catalogs conflicted; the .po files merged cleanly and the catalogs were extracted and compiled again. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BKdjPZBaxnCMragWmqzZTL
Clears the code scanning alert on incomplete escaping. Callers only pass fixed names, so behaviour is unchanged. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BKdjPZBaxnCMragWmqzZTL
This branch was successfully deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Preview only. Do not merge.
This branch combines the open pull requests of the initiative "Easier onboarding in echo" so they can be tested together end to end on one preview. Each change is reviewed and merged through its own pull request:
More pull requests are merged into this branch as they are built. Nothing here is new work.
Checked on the combined result: frontend lint, types, strict translations, contract sync and all unit tests; platform lint, types, package and config checks, schema check, and the database tests for accounts, conversations, analytics, account and db.
Initiative: https://app.notion.com/p/4149cd84270581e9979bc1a5910e3ed8
Generated by Claude Code