Skip to content

[release] v0.100.5#4473

Merged
mmabrouk merged 41 commits into
mainfrom
release/v0.100.5
May 28, 2026
Merged

[release] v0.100.5#4473
mmabrouk merged 41 commits into
mainfrom
release/v0.100.5

Conversation

@github-actions
Copy link
Copy Markdown
Contributor

New version v0.100.5 in

  • web
    • web/oss
    • web/ee
  • services
  • api
  • sdks
    • sdks/python
  • clients
    • clients/python
    • clients/typescript
  • kubernetes
    • kubernetes/helm

ardaerzin and others added 24 commits May 25, 2026 12:56
Sticky and maxWidth columns now honor user-resized widths and the resize
handle is wired uniformly across fixed, constrained, and flexible columns.
Column-group headers (e.g. Evaluators) become resizable; the drag delta
distributes proportionally across leaf children so the group expands
uniformly instead of one column absorbing the entire change.

handleResize commits the new width on every drag frame so AntD's colgroup
updates live — header and body resize together instead of snapping on
release.

ResizableTitle no longer carries an inline width when idle, so AntD's
colgroup is the sole source of truth outside of an active drag.
… anchor sticky-right resize handle inside cell

Two table layout fixes:

- After fast horizontal scrolling, AntD's body cell widths can drift
  away from the header colgroup until something forces a column-level
  re-render. A debounced scroll listener on .ant-table-body bumps a
  layoutNudge counter 150ms after horizontal scroll settles; the memoed
  finalColumns then produces fresh column object references and AntD
  rebuilds its layout state. The listener ignores vertical scroll so
  normal browsing has no extra re-render cost.
- The resize handle on sticky-right columns (e.g. the observability
  Actions column) is anchored inside the cell (right: 0; width: 9px)
  instead of overhanging by 9px, removing the ~18px ghost slot that
  used to appear on the table's right edge.
NodeNameCell renders the full span name through Typography.Text with
ellipsis={{tooltip: name}} so truncation tracks the rendered column
width and the full name is reachable via tooltip. The
nodeDisplayNameAtomFamily that hardcoded a 15-character JS slice is
removed.
Prefer externalEntity over currentEntity when computing createFieldsEntity to avoid stale atom data from previous sessions where afterClose reset never fired (e.g. parent unmounted mid-animation). This ensures Effect 2's default-name computation uses fresh data on first open.
The 'Configure' and 'View details' items in the automatic evaluator
row menu both called handleConfigure, opening the same drawer. The
'View details' entry was a stub introduced with the new evaluators
table that never got its own handler — remove it.
A column group can't shrink smaller than every child being at its own
minimum simultaneously, so the parent floor must be the sum of child
minimums, not the smallest. No behavioural change today because
table-layout: fixed ignores the parent th's minWidth — this is a
hygiene fix that becomes load-bearing the moment we add minConstraints
to the Resizable handle.
…nal targeting flag

The "Signup 2" survey is type=api with schedule=once, which makes PostHog
auto-generate an internal_targeting_flag_key (invisible in the dashboard).
posthog.getActiveMatchingSurveys runs that flag through the eligibility
filter, and for brand-new identified users it evaluates to false before the
flag context settles — so the survey is silently filtered out and useSurvey
rejects with survey-unavailable.

Switch the fetcher to posthog.getSurveys, which returns the raw survey list
without the eligibility filter. We filter client-side by name + start/end
date. The "show once" decision is owned by our own backend on form submit
anyway, so bypassing the SDK-side gate is correct here.

Also widen the auto-redirect list in PostSignupForm so transient errors
(posthog-unavailable from the load timeout, survey-fetch-error from the
new fetch timeout) fall through to /get-started instead of leaving the user
on a blank page. We lose survey data on those paths, not signups.
…consumer

Restructures /post-signup to remove the race-condition workarounds that had
accumulated in PostSignupForm. Replaces the single ~480-line component (which
fetched its own posthog, profile, orgs, survey and defended against each being
absent) with a clear separation:

  PostSignupRoute (new)
    - Owns every async dependency via usePostSignupReadiness.
    - Renders one of: <Skeleton>, <Form>, <Fallback>, or fires a router.replace.
    - The only place that decides "are we ready to show the form?".

  usePostSignupReadiness (new)
    - Composes useSurvey + useProfileData + useOrgData into a discriminated
      state: loading | ready | skip | fallback.
    - "skip" reasons (no API key, survey deleted) → redirect to /get-started.
    - "fallback" reasons (transient PostHog or fetch errors) → show a small
      "we couldn't load the welcome survey, continue" UI instead of bouncing
      the user silently.

  PostSignupForm
    - Now a pure consumer. Receives survey, user, orgs, posthog as props that
      are guaranteed populated. No internal data fetching, no spinner, no
      auto-redirect effect, no debug logs.

  PostSignupHeader, PostSignupSkeleton, PostSignupFallback (new)
    - Small presentational components extracted from the original file so the
      loading and fallback states keep the brand header visible.

  pages/post-signup/index.tsx
    - Slimmed to a 5-line route handler. Debug console.logs removed.

The timeouts inside useSurvey are unchanged — they remain the single source
of truth for "PostHog SDK didn't load" / "survey fetch hung", and the
readiness hook just routes their resulting error codes into the right
rendering branch. Components no longer carry their own timers.
Adds three sections capturing patterns from recent FE work, primarily
PR #4425 (workflow Fern migration + playground vitest setup):

- "Frontend API: Use the Fern Client" — all new FE API code goes
  through @agenta/sdk's getAgentaSdkClient, not raw axios. Pattern,
  anti-patterns, migration policy, references to existing Fern-using
  domains.

- "Code Placement: Packages vs. Application Code" — quick heuristic
  + hard rules, with a forward pointer to the existing
  agenta-package-practices skill for the full ruleset.

- "Package Unit Tests" — layout (tests/unit/, not src/), minimal
  vitest config, scripts, Fern-client mocking pattern, what to
  test / skip. References commit 1c0a900 (exclude in-src tests
  from package build).

Dev Environment Tips section now points to the three new sections
so they're discoverable from the top of the file.

Source: Slack thread between Mahmoud and Arda, 2026-05-27.
…mpty-form flash

Previously, clicking Submit called form.resetFields() before router.push() and
the user saw a blank form for the ~200-500ms it took Next.js to navigate to
/get-started. Confusing for new signups — looked like the form was wiped out
or had silently errored.

Three small changes:
- Drop the resetFields() call. The page is unmounting anyway, so resetting
  fields has no effect except triggering that flash.
- Add an isSubmitting state that flips on at the start of handleSubmitFormData
  and persists through navigation (the component unmounts before any reset
  could matter).
- Wrap the form in a Spin with "Setting up your workspace…" and put the
  submit button in its loading state while isSubmitting is true. The form
  stays visible but dimmed, so the user gets clear feedback that submission
  is in flight.
Four refinements to the new sections, all surfaced by /plan-eng-review:

1. Frontend API: Use the Fern Client
   - Add a Prerequisite subsection: new packages adopting Fern must
     declare @agenta/sdk in package.json dependencies. Without it,
     tsc --noEmit fails on the import before runtime.
   - Migration policy: replace the single paragraph with three explicit
     bullets so the edge case "new function in existing axios-using
     file" is covered (use Fern; don't migrate sibling functions).

2. Package Unit Tests
   - Mocking the Fern client: add a one-paragraph note about singleton
     behaviour. The mock returns a fresh object per call; tests that
     depend on the production singleton need a stricter mock.
   - New "Integration tests" subsection covering vitest.integration
     .config.ts: separate config, raised timeouts, sequential
     execution, when to choose integration over unit. References
     @agenta/entities's existing integration test infra (4 files,
     setup/global.ts + setup/worker.ts).

No structural changes, no anchor changes, no removed content.
…ness watchdog

Two regressions observed in local testing:

1. New signups landed directly on /get-started without seeing the survey.
   Root cause: useSurvey was firing as soon as posthog.__loaded flipped true,
   but posthog.surveys (the SDK extension that fetches /api/surveys/) hadn't
   finished initializing yet. The top-level posthog.getSurveys at that point
   synchronously calls back with [] + {isLoaded:false, error: SURVEYS_NOT_AVAILABLE},
   which my code read as "survey not found" and routed to skip → redirect.

   Fix: gate the getSurveys call behind posthog.onSurveysLoaded, so by the
   time we query for the survey the extension has settled and /api/surveys/
   has returned (or errored loudly). This is the part of JP's hotfix that
   was correct — the only thing wrong with his fix was using
   getActiveMatchingSurveys (eligibility-filtered) rather than getSurveys.
   We now have both: wait for surveys-loaded, then read the raw list.

2. Some signups got stuck on the loading skeleton indefinitely. The
   readiness hook stayed in `loading` whenever any of (surveyLoading,
   profileLoading, posthog, user, survey) was falsy, so a stalled profile
   query or a null user-after-profile-loaded kept the spinner alive forever.

   Fix: add a 10-second watchdog. If the gate is still in `loading` after
   that, it transitions to `fallback` (reason: watchdog-timeout) and
   PostSignupFallback shows the existing "we couldn't load the welcome
   questionnaire, continue" UI. The 10s budget sits above useSurvey's own
   6s timeouts so its specific errors still take precedence, and it's a
   single backstop for anything else (profile, orgs, future dependencies).

Bonus cleanup in useSurvey: the SDK's onSurveysLoaded fires its callback
synchronously when surveys are already loaded, which used to race the
unsubscribe assignment. Guard against that — release the subscription
immediately if the promise already settled inside the call.
…prefetch /get-started

The Spin overlay approach was unreliable: antd renders the spinner inside
the wrapped Form, and the form briefly unmounts mid-route during Next.js's
chunk fetch — so on Submit the user sometimes saw a blank page instead of
the loading affordance.

Two changes:

1. Replace the Spin-over-Form pattern with a full-page PostSignupSubmitting
   view that takes over the moment isSubmitting flips true. No overlay
   positioning, no half-disabled form — just "Setting up your workspace"
   with a spinner. The form is going to unmount anyway; rendering an
   unambiguous transition state in its place is clearer than trying to keep
   the form visible while it's being dismantled.

2. Prefetch /get-started from PostSignupRoute on mount. In dev mode Next.js
   compiles routes lazily and the post-submit router.push can take 1-3s
   while the chunk is built; in prod the prefetch is cheap and ensures the
   route swap is effectively instant. Pages-router prefetch is idempotent
   so calling it on every mount is safe.
…g navigation

The transition view existed but never reached the screen. React 18's automatic
batching put setIsSubmitting(true) in the same task as posthog.capture and
router.push, and the prefetch we added to /get-started made the route swap fast
enough that the new render committed *after* the form unmounted. The user
saw the form being torn down — looking empty — right up until /get-started
replaced it.

Use flushSync to commit the submitting render synchronously, then await one
requestAnimationFrame so the browser paints it before we begin the route
change. This gives the "Setting up your workspace" view a guaranteed visible
moment regardless of how fast the prefetched route swaps in.
Temporary instrumentation to figure out why the PostSignupSubmitting view
isn't reaching the screen on submit. Logs at every step:

  [post-signup][diag] PostSignupRoute render (status: ...)
  [post-signup][diag] PostSignupRoute MOUNTED / UNMOUNTED
  [post-signup][diag] PostSignupForm render (isSubmitting: ...)
  [post-signup][diag] PostSignupSubmitting render
  [post-signup][diag] handleSubmitFormData start
  [post-signup][diag] flushSync(setIsSubmitting=true) done
  [post-signup][diag] rAF yielded
  [post-signup][diag] posthog.capture('survey sent') called
  [post-signup][diag] calling router.push('/get-started')
  [post-signup][diag] routeChangeStart / routeChangeComplete

Will be removed once we have the trace.
Three correctness fixes raised in review:

1. useSurvey.isSurveyRunning was treating any survey with a future end_date as
   not-running. Now compares both start_date and end_date against "now",
   treating end_date === null as "no scheduled end".

2. Survey lookup used name.includes(surveyName), which would also match
   "Signup 20" if a future survey is added with that name. Switched to
   strict equality.

3. router.push('/get-started') in the submit handler is now awaited; on
   rejection we reset isSubmitting so the user can retry instead of being
   stuck on the "Setting up your workspace" view forever.

Also removes the temporary [post-signup][diag] console.log statements that
landed in dee7651 — they served their purpose tracking down the dev-server
location issue and shouldn't ship to prod.
Four trivial fixes from CodeRabbit's CR pass:
- @agenta/sdk prerequisite: "before the code runs" → "at type-check
  time" (tsc --noEmit is compile-time, not runtime).
- Three fenced code blocks gain `text` language identifier so
  markdownlint renders them cleanly: packages-vs-app decision tree,
  unit tests directory layout, integration tests directory layout.

No content or rule changes.
Two independent bugs surfaced in #bugs as the "demo workspace banner
issue":

1. URL pollution (Mahmoud's investigation). projectAtom derived
   workspaceId from selectedOrgAtom, which lags /projects/ by one
   /organizations/{id} fetch. During the gap, pickPreferredProject
   ran with workspaceId=null and hit the cross-org
   `projects.find(p => p.is_default_project)` fallback. For users
   with is_demo=true membership, that returned the Demo Workspace's
   default project. WorkspaceRedirect then router.replace'd to
   /p/<demo-project>, pinning the URL and rendering the banner in
   the wrong workspace.

   Fix: projectAtom falls back to selectedOrgIdAtom (URL-derived,
   resolves on first render) so workspaceId is always populated.
   projectMatchesWorkspace already accepts either workspace_id or
   organization_id, so the URL-derived org UUID filters correctly.

2. Banner-missing-on-reload race. Two parallel "session exists"
   atoms existed — @agenta/shared/state's sessionAtom (entity
   packages) and oss/state/session's sessionExistsAtom. The eager
   init in appState/atoms.ts set the shared one; the oss one
   waited for SessionListener's React effect. projectsQueryAtom
   gated on the oss atom AND on profileQueryAtom.data.id, which
   forced /projects/ to wait for /profile/ sequentially. On cold
   reload the demo banner couldn't render until ~2 RTTs +
   1 effect tick later.

   Fix: consolidate to a single source of truth — sessionExistsAtom
   is now a re-export of @agenta/shared/state's sessionAtom. The
   ~14 callers keep their imports unchanged. SessionListener and
   useSession drop their manual dual-write code. projectsQueryAtom
   drops the redundant profile.data.id gate (fetchAllProjects uses
   session cookies, not user.id). Cold-reload banner now appears
   after one network RTT, with the same speedup applying to every
   other query gated on sessionExistsAtom (access, observability,
   selectedOrg, profile).

Includes a vitest regression spec for pickPreferredProject behavior
(workspaceId=null returns cross-org demo; workspaceId scoped to own
org returns own project). web/oss has no vitest runner yet, so
*.test.ts is added to tsconfig exclude — the spec runs once vitest
is wired.
@vercel
Copy link
Copy Markdown

vercel Bot commented May 28, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
agenta-documentation Ready Ready Preview, Comment May 28, 2026 5:08pm

Request Review

@dosubot dosubot Bot added the size:XS This PR changes 0-9 lines, ignoring generated files. label May 28, 2026
@dosubot dosubot Bot added the size:L This PR changes 100-499 lines, ignoring generated files. label May 28, 2026
@dosubot dosubot Bot added size:XL This PR changes 500-999 lines, ignoring generated files. and removed size:L This PR changes 100-499 lines, ignoring generated files. labels May 28, 2026
…pdated-patterns

chore(docs): document new frontend patterns in AGENTS.md
@dosubot dosubot Bot added size:XXL This PR changes 1000+ lines, ignoring generated files. and removed size:XL This PR changes 500-999 lines, ignoring generated files. labels May 28, 2026
@github-actions
Copy link
Copy Markdown
Contributor Author

github-actions Bot commented May 28, 2026

Railway Preview Environment

Status Destroyed (PR closed)

Updated at 2026-05-28T19:09:25.089Z

ardaerzin and others added 3 commits May 28, 2026 13:12
The own-org "Default" project had is_default_project: true in the
fixture, which made `projects.find(p => p.is_default_project)` (the
cross-org fallback inside pickPreferredProject when workspaceId is
null) return the OWN project first — not the demo one. That
contradicted the "with workspaceId=null, returns demo project"
assertion and meant the test wasn't locking the bug it claimed to.

Mirror Mahmoud's actual /projects/ response from the investigation:
own-org "Default" is_default_project=false, Demo Workspace "Default"
is_default_project=true. With that ordering, the find lands on the
demo project — exactly the path that pollutes the URL on cold reload.
…issues

[FE fix] demo workspace banner issues
…u-top-two-actions-have-identical-effect

[AGE-3773] fix(frontend): remove duplicate 'View details' evaluator menu item
@vercel
Copy link
Copy Markdown

vercel Bot commented May 28, 2026

Deployment failed with the following error:

The provided GitHub repository does not contain the requested branch or commit reference. Please ensure the repository is not empty.

[Frontend / Fix]: Fix name memoization issues in entity commit modal
@dosubot dosubot Bot added the lgtm This PR has been approved by a maintainer label May 28, 2026
@mmabrouk mmabrouk merged commit 57a499c into main May 28, 2026
43 of 45 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

lgtm This PR has been approved by a maintainer size:XXL This PR changes 1000+ lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants