Skip to content

[PF-1992] Migration orchestrator - #4946

Open
vedrani wants to merge 125 commits into
masterfrom
feature/pf-1992-migration-orchestrator
Open

[PF-1992] Migration orchestrator#4946
vedrani wants to merge 125 commits into
masterfrom
feature/pf-1992-migration-orchestrator

Conversation

@vedrani

@vedrani vedrani commented May 8, 2026

Copy link
Copy Markdown
Collaborator

PF-1992

PF-1992 — Migration orchestrator + supporting docs

Closes the PF-1992 ticket: ships the autonomous-migration orchestrator, gate scripts, diff helpers, locked decision docs, per-component plan files, prompt pack, manifest, and reference materials for the upcoming PF-1994 / PF-2024 / PF-2025 / PF-2020-2023 batches.


TL;DR

A workflow-agnostic orchestrator that drives Claude Code subprocess invocations through a 14-step per-component loop: agent migrates source → gate (build/tsc/lint/jest/cypress/happo) → push → poll CI → classify failures → auto-fix or feed-to-agent → on green flip to awaiting_review → separate --review-sweep mode handles human reviews on its own cadence. Validated by 12+ canaries on Note + Form + Container + Button across the Tier 0/1 surface; per-canary cost ~$0.30-1.00 in Anthropic API.

The orchestrator is single-workflow today (migration); the Workflow interface is built for extension to future workflows (Figma → component, bug-fix, etc.) without touching the core loop.

This PR is infrastructure-only. Per-component migrations land as separate PRs against feature/picasso-modernization (already created), driven by yarn orchestrate.


Scope

Surface Files LOC
Orchestrator core (loop, state machine, retries, sweep, locks) bin/lib/orchestrator-core.ts ~3,000
Workflow descriptor interface bin/lib/workflow.ts ~400
Failure classifier (CI failure → action) bin/lib/failure-classifier.ts ~300
Review classifier (PR comments → action) bin/lib/review-classifier.ts ~300
Token telemetry (cost.json per canary) bin/lib/token-telemetry.ts ~250
Migration workflow descriptor + entrypoint bin/migration-orchestrator.ts ~270
Gate script (build/tsc/lint/jest/cypress/happo strict gate) bin/migration-gate.sh ~470
Diff helper (prop-surface + import + happo summary) bin/migration-diff.sh ~260
classes prop compatibility shim packages/base/Utils/src/utils/with-classes.ts + tests ~130
Manifest (28 component-migration units) docs/migration/manifest.json + schema ~400
Per-component plans (Tier 0 + Tier 1) docs/migration/components/*.md ~700
Locked decision docs docs/migration/decisions/*.md ~700
Agent prompts (light + heavy paths) docs/migration/PROMPT-*.md ~350
Rules (api-preservation, styling, JSS crib, base-ui-react crib) docs/migration/rules/*.md ~600
Token reference docs/migration/tokens/picasso-tailwind-tokens.md ~150
References (agent-loop, escalation, PR workflow, lessons) docs/migration/references/*.md ~500
Runbook docs/migration/ORCHESTRATOR.md ~300

Total: ~5,400 LOC (code) + ~3,500 LOC (markdown docs). Most documentation files are templated content, not contentious code.


Reviewer guide — what to read in what order

This PR is large but has a clear priority hierarchy. Pick a slice based on your role; you don't need to read everything.

Highest value (please review thoroughly)

Engineering reviewer — focus here:

  • bin/lib/orchestrator-core.ts — the 14-step run() loop, runBatch, runReviewSweep + sweepOne, Phase 3.3 CI iteration, workspace-overlay fix, per-item locks.
  • bin/migration-gate.sh — gate stage flow + the new strict Happo REST API gate (per migration plan v4 §6.3).
  • bin/lib/failure-classifier.ts — pure-function CI-failure classifier; 10-step heuristic decision tree.
  • bin/lib/review-classifier.ts — pure-function review classifier with confidence scoring.
  • packages/base/Utils/src/utils/with-classes.tsclasses prop compatibility shim (consumer-API impact across 23 downstream repos).

Domain / Picasso reviewer — focus here:

  • docs/migration/decisions/ (4 docs):
    • backdrop-replacement.md — custom <div> + scroll-lock (no @base-ui/react analog)
    • popper-replacement.md@floating-ui/react direct dependency (preserves position-anchored API)
    • classes-shim.md — Tailwind-routing shim policy (walks back v3-era "remove classes" plan)
    • integration-branch.mdfeature/picasso-modernization long-lived branch
  • docs/migration/components/*.md — 19 per-component plan files. Slot-keys section is canonical for the upcoming agent runs; corrections welcome.
  • docs/migration/PROMPT-light.md + PROMPT-heavy.md — agent system prompts. Tier 0 (light) = @mui/base@base-ui/react package swap; Tier 1+ (heavy) = full rewrite.
  • docs/migration/manifest.json — 28 component-migration units across 6 tiers per migration plan v4 §3.9.

Designer — review one Tier 0 canary PR's Happo diffs once those land (intentional pixel changes from MUI v4 → @base-ui/react DOM cleanup are expected).

Medium value (skim)

  • bin/lib/workflow.tsWorkflow descriptor interface. Note: critique flagged this as premature abstraction (only 1 consumer today). Documented decision: leave as-is until 2nd workflow lands; inline post-migration if no 2nd workflow surfaces.
  • bin/lib/orchestrator-core.ts — retry logic for gh.viewPR / gh.createPR / gh.fetchJobLog (3 similar 4-attempt exp-backoff blocks), Phase 3.3 iteration loop, token telemetry hooks, .envrc auto-load.
  • bin/lib/token-telemetry.ts — reads Claude Code session jsonl (~/.claude/projects/<encoded-cwd>/<session-id>.jsonl), aggregates token usage, computes USD estimates at Sonnet 4.5 list pricing, writes migration-runs/<date>/<id>/cost.json.
  • docs/migration/rules/api-preservation.md — preserved classes prop policy (per migration plan v4 §2.3).

Lowest value (template / reference)

  • bin/migration-diff.sh — diff snapshot/report shell helper.
  • docs/migration/tokens/picasso-tailwind-tokens.md — token reference (auto-generated content).
  • docs/migration/rules/jss-to-tailwind-crib.md + styling.md — JSS → Tailwind transformation table.
  • docs/migration/references/lessons-learned.md — auto-accumulated by the orchestrator post-each-successful-PR; will grow during PF-1994.

Architecture decisions (locked, May 2026)

Per docs/modernization/PI-4318-PF-1992-design-decisions.md + migration plan v4. Each has a decision doc:

Decision Doc Rationale
Long-lived feature/picasso-modernization integration branch (renamed from picasso-modernization to fit Picasso CI's master + feature/** trigger config) decisions/integration-branch.md Single revertible point per tier; master stays clean of half-migrated state
Backdrop replacement = custom <div> + Tailwind + scroll-lock decisions/backdrop-replacement.md No standalone Backdrop in @base-ui/react; preserves external consumer API
Popper replacement = @floating-ui/react direct dep decisions/popper-replacement.md @base-ui/react/popover is trigger-anchored; would force every consumer to refactor
classes prop preserved via Tailwind-routing shim decisions/classes-shim.md Walks back v3-era "remove classes" plan; preserves ~80% of consumer usage in 23-repo portfolio
Strict Happo gate (zero-diff OR designer-accepted via REST API) (lives inline in bin/migration-gate.sh; design captured in migration plan v4 §6.3) Catches real visual regressions vs. flake retries
Async review sweep (decoupled from migrate-mode) (in-code design comment in bin/lib/orchestrator-core.ts) Operator review cadence is hours-to-days; sync wait blocks orchestrator
Per-item file locks at migration-runs/.locks/<id> (stale-PID detection) (in-code) Prevents concurrent migrate vs. sweep collisions
Token telemetry per canary (in-code) Operator visibility into per-component / aggregate spend

Validation evidence

The orchestrator is validated end-to-end by 12+ canary runs on Note (sandbox), Form, Container, and Button against feature/picasso-modernization:

Path Validated by Evidence
Inner gate loop (build → tsc → lint → jest → cypress → happo → react19) All canaries 18-29 migration-runs/<date>/<id>/report.json
--batch multi-component sequential Validate C [batch] [1] ... [batch] [2] ... [batch] --max-items=2 cap reached
Phase 3.3 CI iteration (auto-fix-snapshot + feed-to-agent) Canary 19 / 20 / 28 Auto-regenerated Pagination snapshots when Tier 0 ripple hit consumer packages
Workspace overlay (Phase 2.5b) Canary 22+ Consumer-package tests now resolve to worktree's source, not main repo's
Async sweep + per-item locks Smoke-tested via dry-run + --review-sweep no-work [sweep] no items in awaiting_review state — nothing to sweep
Token telemetry Canary 29 + Validate C Form: $0.54; Container: $0.41; Form (re-run): $0.24
Resilience (5xx retries, spending-cap detect, 60s post-push sleep) Canaries 25-29 Each surfaced + fixed a distinct latent bug
Strict Happo gate Smoke-tested at gate-stage level (not yet end-to-end on a real diff — needs Day 2 Tier 1 to fully exercise) (Schema TBD — relies on Happo REST API shape; fall-through to PASS on shape mismatch)

Out of scope (separate tickets)

  • TypeScript 5.5/5.6 upgrade (migration plan v4 §9.1) — separate parallel ticket; PF-1994 cannot start until both PF-1992 + the TS ticket land.
  • Pipelined state-machine refactor (v4 Step 2) — current sequential --batch is adequate for one operator + Anthropic singleton; revisit post-migration if parallelism is needed.
  • Slack webhook (v4 Step 3) — ergonomic polish, not load-bearing; deferrable.
  • Codemod authoring for breaking-change props (@toptal/picasso-codemod v53+) — feeds PF-1995.
  • Tier 4 sibling packages (charts, query-builder, RTE) — PF-2020/2021/2022.
  • Tier 5 provider runtime (Picasso Provider rewrite + root peer-dep removal) — PF-2023.
  • picasso@next dist-tag publishing during Phase 2 — design decision §9.9 still open.

Known critique acknowledgments (deferred refactors)

A fresh-eyes architectural review surfaced four legitimate over-engineering hotspots. Each is left as-is for this PR because none ship migration value and all are low-risk to defer:

  1. Workflow interface (bin/lib/workflow.ts) — premature abstraction with one consumer (migration). Inline as MigrationWorkflow post-migration if no 2nd workflow lands.
  2. Three duplicate gh.* retry loops (createPR, viewPR, fetchJobLog) — collapse to a withRetry<T> helper. Cosmetic refactor; ~40 LOC saved.
  3. --with-mcp flag (Playwright integration) never validated by any canary. Documented as experimental; enable for first Tier 2/3 component.
  4. Token-telemetry's cache-tier breakdown (5m vs 1h) is informational; could slim to { iteration, costUsd } per snapshot.

Verification — how to test this PR locally

Prerequisites

  • Node 20+, Yarn 1.22.x, gh CLI authenticated, Claude Code logged in (claude subprocess auth lives in ~/.claude/).
  • direnv with ~/Projects/.envrc exporting HAPPO_API_KEY + HAPPO_API_SECRET (or equivalent shell env). Orchestrator auto-loads from .envrc if direnv hook isn't active.

Smoke tests (no API spend)

# Dry-run plan output for any Tier 1 component
yarn orchestrate --component=Form --no-merge --dry-run

# Sweep with no awaiting_review items → no-op
yarn orchestrate --review-sweep

# `--max-items=N` flag
yarn orchestrate --tier=1 --batch --no-merge --max-items=2 --dry-run

# `withClasses` shim unit tests
yarn jest packages/base/Utils/src/utils/__tests__/with-classes

Real canary (~$0.30-0.50, ~12 min wall-clock)

# Pick a Tier 1 already-clean component and run end-to-end
yarn orchestrate --component=Note --no-merge --max-iterations=5 --ci-timeout-minutes=25

Watch the log for:

  • [loop] selected: Note (tier=1, status=queued, ...)
  • [loop] gates pass on iteration 1
  • [cost] iter 1: total $0.NN (in=N, out=N, cache_read=N)
  • [loop] polling CI on https://github.com/toptal/picasso/pull/<N>
  • All checks green except possibly Happo (Picasso/Cypress) — known flake on Tier 1; designer-accept in Happo UI to clear

After completion, check:

  • migration-runs/<date>/Note/cost.json — per-iter token + USD breakdown
  • migration-runs/<date>/Note/report.json — structured gate report
  • docs/migration/manifest.jsonNote.status should be awaiting_review

Verification of decision docs

Each decision doc is internally consistent and matches the in-code implementation:

  • decisions/backdrop-replacement.md ⟷ Backdrop's target_path: 'none' in manifest
  • decisions/popper-replacement.md ⟷ Popper's plan file in components/Popper.md
  • decisions/classes-shim.mdpackages/base/Utils/src/utils/with-classes.ts implementation
  • decisions/integration-branch.mdmigrationWorkflow.baseBranch in bin/migration-orchestrator.ts

Test plan

  • yarn workspace @toptal/picasso-utils build:package (compiles withClasses + tests)
  • yarn jest packages/base/Utils/src/utils/__tests__/with-classes.test.ts (8/8 passing)
  • yarn typecheck (full repo)
  • yarn eslint --ext=.ts bin/ (0 errors; warnings are pre-existing or acceptable)
  • yarn orchestrate --component=Note --dry-run (planned 14 steps print correctly)
  • yarn orchestrate --review-sweep (no-op when no awaiting_review items)
  • At least 1 reviewer reads decisions/*.md and confirms each decision matches the in-code implementation
  • At least 1 reviewer skims a Tier 1 plan file (e.g. Note.md) + a Tier 0 plan file (e.g. Button.md) to verify Slot keys + acceptance criteria are sensible
  • At least 1 reviewer skims bin/lib/orchestrator-core.ts:run() flow

Follow-ups (separate PRs after this lands)

  1. Day 2 Tier 1 batch (PF-1994) — process all 11 Tier 1 cleanup-only / type-only-fix components via yarn orchestrate --tier=1 --batch --no-merge.
  2. Day 3 Tier 0 batch (PF-1994) — 8 components in dependency order: Backdrop → Badge → Button → Slider → Switch → Tabs → Modal → Drawer.
  3. Day 4+ Tier 2 / Tier 3 (PF-2024 / PF-2025) — heavy migrations one at a time with operator review.
  4. Sweep cron*/30 * * * * yarn orchestrate --review-sweep (or operator-driven cadence).
  5. Critique simplifications (post-migration) — collapse gh.* retries, inline Workflow, slim telemetry.

Acknowledgments

  • Migration plan v4 + design decisions per docs/modernization/PI-4318-P1-MOD-01-migration-plan.md + PI-4318-PF-1992-design-decisions.md.
  • PR [TAPS-0000] Migrate Button and Switch to BASE UI #4906 (Button + Switch on @base-ui/react) — the calibration baseline for Tier 0 light path.
  • Canaries 18-31 series — each surfaced a distinct latent bug; full per-canary log in migration-runs/.

Refs: PF-1992

How to test

  • Temploy
  • FIXME: Add the steps describing how to verify your changes

Screenshots

Before. After.
Insert screenshots or screen recordings Insert screenshots or screen recordings

Development checks

  • Add changeset according to guidelines (if needed)
  • Double check if picasso-tailwind-merge requires major update (check its README.md)
  • Read CONTRIBUTING.md and Component API principles
  • Make sure that additions and changes on the design follow Toptal's BASE design, and it's been already discussed with designers at #-base-core
  • Annotate all props in component with documentation
  • Create examples for component
  • Ensure that deployed demo has expected results and good examples
  • Ensure the changed/created components have not caused accessibility issues. How to use accessibility plugin in storybook.
  • Self reviewed
  • Covered with tests (visual tests included)

Breaking change

  • codemod is created and showcased in the changeset
  • test alpha package of Picasso in StaffPortal

All development checks should be done and set checked to pass the
GitHub Bot: TODOLess action

Alpha packages

Manually trigger the publish.yml workflow to publish alpha packages. Specify pull request number as a parameter (only digits, e.g. 123).

PR Review Guidelines

When to approve? ✅

You are OK with merging this PR and

  1. You have no extra requests.
  2. You have optional requests.
    1. Add nit: to your comment. (ex. nit: I'd rename this variable from makeCircle to getCircle)

When to request changes? ❌

You are not OK with merging this PR because

  1. Something is broken after the changes.
  2. Acceptance criteria is not reached.
  3. Code is dirty.

When to comment (neither ✅ nor ❌)

You want your comments to be addressed before merging this PR in cases like:

  1. There are leftovers like unnecessary logs, comments, etc.
  2. You have an opinionated comment regarding the code that requires a discussion.
  3. You have questions.

How to handle the comments?

  1. An owner of a comment is the only one who can resolve it.
  2. An owner of a comment must resolve it when it's addressed.
  3. A PR owner must reply with ✅ when a comment is addressed.

@vedrani
vedrani requested a review from a team as a code owner May 8, 2026 06:25
@changeset-bot

changeset-bot Bot commented May 8, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 59295e2

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 85 packages
Name Type
@toptal/picasso-shared Minor
@toptal/picasso Patch
@toptal/picasso-autocomplete Patch
@toptal/picasso-avatar Patch
@toptal/picasso-avatar-upload Patch
@toptal/picasso-badge Patch
@toptal/picasso-breadcrumbs Patch
@toptal/picasso-button Patch
@toptal/picasso-calendar Patch
@toptal/picasso-carousel Patch
@toptal/picasso-checkbox Patch
@toptal/picasso-environment-banner Patch
@toptal/picasso-file-input Patch
@toptal/picasso-form Patch
@toptal/picasso-form-label Patch
@toptal/picasso-form-layout Patch
@toptal/picasso-grid Major
@toptal/picasso-input Patch
@toptal/picasso-input-adornment Patch
@toptal/picasso-list Patch
@toptal/picasso-menu Patch
@toptal/picasso-modal Patch
@toptal/picasso-notification Patch
@toptal/picasso-number-input Patch
@toptal/picasso-outlined-input Patch
@toptal/picasso-overview-block Patch
@toptal/picasso-page Patch
@toptal/picasso-popper Patch
@toptal/picasso-radio Patch
@toptal/picasso-select Patch
@toptal/picasso-skeleton-loader Patch
@toptal/picasso-slider Patch
@toptal/picasso-step Patch
@toptal/picasso-switch Patch
@toptal/picasso-table Patch
@toptal/picasso-tabs Patch
@toptal/picasso-tag Patch
@toptal/picasso-tagselector Patch
@toptal/picasso-test-utils Patch
@toptal/picasso-timeline Patch
@toptal/picasso-tooltip Patch
@toptal/picasso-utils Patch
@toptal/picasso-pictograms Patch
@toptal/picasso-forms Patch
@toptal/picasso-user-badge Patch
@toptal/picasso-query-builder Patch
@toptal/picasso-rich-text-editor Patch
@toptal/picasso-accordion Patch
@toptal/picasso-alert Patch
@toptal/picasso-application-update-notification Patch
@toptal/picasso-drawer Patch
@toptal/picasso-helpbox Patch
@toptal/picasso-pagination Patch
@toptal/picasso-password-input Patch
@toptal/picasso-prompt-modal Patch
@toptal/picasso-section Patch
@toptal/picasso-show-more Patch
@toptal/picasso-tree-view Patch
@toptal/picasso-date-picker Patch
@toptal/picasso-dropzone Patch
@toptal/picasso-timepicker Patch
@toptal/picasso-account-select Patch
@toptal/picasso-dropdown Patch
@toptal/picasso-date-select Patch
@toptal/picasso-tailwind-merge Patch
@topkit/analytics-charts Patch
@toptal/picasso-amount Patch
@toptal/picasso-backdrop Patch
@toptal/picasso-collapse Patch
@toptal/picasso-container Patch
@toptal/picasso-fade Patch
@toptal/picasso-link Patch
@toptal/picasso-note Patch
@toptal/picasso-paper Patch
@toptal/picasso-quote Patch
@toptal/picasso-rating Patch
@toptal/picasso-slide Patch
@toptal/picasso-typography Patch
@toptal/picasso-typography-overflow Patch
@toptal/picasso-charts Patch
@toptal/picasso-icons Patch
@toptal/picasso-image Patch
@toptal/picasso-loader Patch
@toptal/picasso-logo Patch
@toptal/picasso-empty-state Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vedrani
vedrani force-pushed the feature/pf-1992-migration-orchestrator branch from 6c32b4e to 8a68876 Compare May 12, 2026 12:05
@vedrani
vedrani changed the base branch from master to feature/picasso-modernization May 12, 2026 12:07
@github-actions

Copy link
Copy Markdown
Contributor

📖 Storybook Preview

🚀 Your Storybook preview is ready: View Storybook

📍 Preview URL: https://toptal.github.io/picasso/prs/4946/

This preview is updated automatically when you push changes to this PR.

github-actions Bot added a commit that referenced this pull request May 12, 2026
@vedrani vedrani self-assigned this May 12, 2026
@vedrani vedrani changed the title Feature/pf 1992 migration orchestrator [PF-1992] Migration orchestrator May 12, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📖 Storybook Preview

🚀 Your Storybook preview is ready: View Storybook

📍 Preview URL: https://toptal.github.io/picasso/prs/4946/

This preview is updated automatically when you push changes to this PR.

github-actions Bot added a commit that referenced this pull request May 12, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📖 Storybook Preview

🚀 Your Storybook preview is ready: View Storybook

📍 Preview URL: https://toptal.github.io/picasso/prs/4946/

This preview is updated automatically when you push changes to this PR.

github-actions Bot added a commit that referenced this pull request May 12, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📖 Storybook Preview

🚀 Your Storybook preview is ready: View Storybook

📍 Preview URL: https://toptal.github.io/picasso/prs/4946/

This preview is updated automatically when you push changes to this PR.

github-actions Bot added a commit that referenced this pull request May 12, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📖 Storybook Preview

🚀 Your Storybook preview is ready: View Storybook

📍 Preview URL: https://toptal.github.io/picasso/prs/4946/

This preview is updated automatically when you push changes to this PR.

github-actions Bot added a commit that referenced this pull request May 12, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📖 Storybook Preview

🚀 Your Storybook preview is ready: View Storybook

📍 Preview URL: https://toptal.github.io/picasso/prs/4946/

This preview is updated automatically when you push changes to this PR.

github-actions Bot added a commit that referenced this pull request May 13, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📖 Storybook Preview

🚀 Your Storybook preview is ready: View Storybook

📍 Preview URL: https://toptal.github.io/picasso/prs/4946/

This preview is updated automatically when you push changes to this PR.

github-actions Bot added a commit that referenced this pull request May 13, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📖 Storybook Preview

🚀 Your Storybook preview is ready: View Storybook

📍 Preview URL: https://toptal.github.io/picasso/prs/4946/

This preview is updated automatically when you push changes to this PR.

github-actions Bot added a commit that referenced this pull request May 13, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📖 Storybook Preview

🚀 Your Storybook preview is ready: View Storybook

📍 Preview URL: https://toptal.github.io/picasso/prs/4946/

This preview is updated automatically when you push changes to this PR.

github-actions Bot added a commit that referenced this pull request May 14, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📖 Storybook Preview

🚀 Your Storybook preview is ready: View Storybook

📍 Preview URL: https://toptal.github.io/picasso/prs/4946/

This preview is updated automatically when you push changes to this PR.

github-actions Bot added a commit that referenced this pull request May 18, 2026
@@ -0,0 +1,2568 @@
# Picasso code review rules

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review this document

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are some conflicts also:

  • Inline style framing - needs reframing and different priority

  • Idea is to use reasoning about why inline styles are needed (can we simplify DOM, does it look geometrical and visually correct) and to first exhaust base-ui customization mechanisms, also to ask for guidance in case of last solution

  • Slot-based styling section - only legacy and specific for Tier3.b components not new pattern

  • Design patterns R5 and R10 are in confict with existing codebase - be sure for migration we keep as is and leave for later refactor and for migration to not introduce new ones

  • Remove patch rules after master rebase

  • Resolving of versionBump is in conflict between manifest and rules


---

## 9. JSS → Tailwind translation (migration PRs)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Idk, this seems to me that it should be described as separate story on picasso like we had previously https://picasso.toptal.net/?path=/story/tutorials-styled-components--styled-components

To have single source of truth, who knows how it can change in the future.

I understand that tw is optional, but styles components are optional as well and still they have their own picasso page.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's just for migration and when migration finish we will rework it for tailwind.

Comment on lines +1391 to +1408
### Color

MUI palette → Picasso tokens (most common):

| JSS | Picasso Tailwind |
|---|---|
| `color: palette.text.primary` | `text-graphite-800` |
| `color: palette.text.secondary` | `text-graphite-700` |
| `color: palette.grey.dark` | `text-gray-700` |
| `color: palette.primary.main` | `text-blue-500` |
| `color: palette.primary.dark` | `text-blue-600` |
| `color: palette.success.main` | `text-green-500` |
| `color: palette.error.main` | `text-red-500` |
| `color: palette.warning.main` | `text-yellow-500` |
| `backgroundColor: palette.common.white` | `bg-white` |
| `backgroundColor: palette.grey.light` | `bg-gray-100` |
| `backgroundColor: palette.background.paper` | `bg-white` |
| `borderColor: palette.divider` | `border-gray-300` |

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My previous comment was specifically about this, since colors are the only things I can imagine can be changed. And they are picasso dependent, not common industry standards as for example spacings.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree, duplication. It should point to /tokens/picasso-tailwind-tokens.md

Comment thread docs/modernization/picasso-code-review-rules.md
Comment thread docs/modernization/picasso-code-review-rules.md

### R10 — Extend `BaseProps` · **RULE**

Every component's Props interface must extend `BaseProps`, which provides the shared root-element contract: `className?: string`, `style?: CSSProperties`, `'data-testid'?: string`. Do not redeclare these props locally.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Every component's Props interface must extend `BaseProps`, which provides the shared root-element contract: `className?: string`, `style?: CSSProperties`, `'data-testid'?: string`. Do not redeclare these props locally.
Every component's Props interface must extend `BaseProps`, which provides the shared root-element contract: `className?: string`, `style?: CSSProperties`, `'data-testid'?: string`.

nit: I think this is extra 🤔

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, this is R10 from PICASSO_COMPONENT_DESIGN_PATTERNS.md

}
```

**Migration exception** (see Appendix A): 20/28 components currently extend `BaseProps`; 3/28 still extend `StandardProps`; 5/28 mixed. **Do NOT preemptively rebuild prop interfaces** mid-migration — that's a separate refactor track.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍


**Internal translation**: Picasso's external `as` API is translated to Base UI's `render` mechanism internally — see §5 "Render prop". For button-default Base UI parts (Button, Menu.Trigger, Tabs.Tab, NumberField.Increment/Decrement, Toolbar.Button), the tag swap MUST pair with `nativeButton={false}`.

**No runtime guards**: do NOT add `typeof`/`isValidAs` checks for `as` — TypeScript constrains it; reviewers will ask for removal.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: I think this has to be a global rule of some sort, maybe in Claude.md

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree, will mvoe from code-standards.md to PICASSO_COMPONENT_DESIGN_PATTERNS.md

Comment on lines +623 to +627
**How to find which props to destructure**: open `node_modules/@base-ui/react/<group>/<part>/<Part>.d.ts` and diff its `*.Props` interface against your public `Props`. The intersection's NAME-OVERLAPS-WITH-DIFFERENT-TYPES set is what you destructure. Everything else is type-compatible and spreads.

**Empirical sanity check**: for Tier 0 components the destructure list is typically 1–3 props (onChange signature, occasional value/checked clamp, sometimes a removed prop). If you find yourself destructuring 6+ props, re-read the library's `.d.ts` — you're sliding into Anti-pattern B.

The migration-period oscillation observed on Switch review-iter 7 (2026-05-22) — allowlist → cast → allowlist across three consecutive iters — was the agent flipping between the two anti-patterns above because neither it nor the audit-agent named the canonical third option. Cite this section directly in PR replies when reviewers raise the question.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it seems those rules more confusing than helpful 🤔

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think they are extracted from lesson learned, I will ask agent how useful it is and can it be more generalized.

### Polymorphic + ref forwarding · **RULE**

- `forwardRef<HTMLButtonElement, Props>(...)` already types `ref` correctly. Don't cast `ref` at the JSX site.
- Spreading `{...rest}` with a cast (`{...(rest as BaseUIButton.Props)}`) is `// @ts-ignore` in disguise. If `rest` doesn't conform, drop the offending Picasso-only prop BEFORE spreading. NEVER fall back to `any`.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
- Spreading `{...rest}` with a cast (`{...(rest as BaseUIButton.Props)}`) is `// @ts-ignore` in disguise. If `rest` doesn't conform, drop the offending Picasso-only prop BEFORE spreading. NEVER fall back to `any`.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why, can you explain?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we need to give positive example of it. Something like "Drop Picasso-only props from rest before spreading to a @base-ui/react part."

import cx from 'classnames'
import { twMerge } from '@toptal/picasso-tailwind-merge'

twMerge(cx('px-4 text-sm', isLarge && 'px-6 text-base'), className)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think usage of cx is not needed with twMerge, example:

twMerge(
                'relative min-h-0 flex overflow-hidden',
                classesByOrientation[orientation].root,
                classesByVariant[variant].root,
                className
              )

className: twMerge(
'relative min-h-0 flex overflow-hidden',
classesByOrientation[orientation].root,
classesByVariant[variant].root,
className
),

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note for agent:

No cx needed. Picasso's twMerge is extendTailwindMerge from tailwind-merge (packages/picasso-tailwind-merge/src/twMerge.ts:35), which accepts the same input types as twJoin: strings, arrays, and falsy values (false, null, undefined, '') are filtered out.

So all of these work directly in twMerge(...):

condition && 'class' — Drawer.tsx:112, PaginationButton.tsx:20-22
condition ? 'a' : 'b' — Dropdown.tsx:271, DropdownArrow.tsx:21
Nested arrays — PageHeadBase.tsx:74 uses ['py-3', rightPadding && 'pr-8']
What's NOT supported is the clsx object syntax ({ active: isActive }). If you need that, you'd have to add clsx/cx — but the established Picasso pattern is the && / ternary forms above, which is what reviewers expect.

twJoin is also re-exported for the case where you just need to concatenate without conflict-resolution.

return (
<Checkbox.Root
className={twMerge(
cx(

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
cx(

```tsx
<Switch.Thumb
className={(state) =>
twMerge(cx(

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
twMerge(cx(
twMerge(

vedrani and others added 21 commits June 29, 2026 15:11
Merge local Tooltip run state into the manifest, preserving the existing
`vedrani` variant (needs_human, escalated after 10 iters) and adding:
  - v1: in_progress (interrupted local attempt, no PR)
  - v2: awaiting_review, PR #5005 (migrate-Tooltip-v2)

PR #5005's branch was rebased clean onto the migration target (the
14-commit worktree-fork pile was stripped), so v2 now tracks a
Tooltip-only diff. Append the three Tooltip audit sections to
lessons-learned.md (run was interrupted before the auto-append ran).

Refs: PF-1992
Add the Dropdown v2 variant to the orchestrator manifest so a
`--review-sweep` from this branch picks up PR #5008 (migrate-Dropdown-v2).

Status recorded as awaiting_review (the local run left it stale at
in_progress; PR #5008 is OPEN, CI-green, REVIEW_REQUIRED). The branch was
rebased clean onto the migration target (worktree-fork pile stripped), so
v2 tracks a Dropdown-only diff (7 files).

Refs: PF-1992
@vedrani
vedrani force-pushed the feature/pf-1992-migration-orchestrator branch from c2be873 to 8261b70 Compare June 29, 2026 14:36
@github-actions

github-actions Bot commented Jun 29, 2026

Copy link
Copy Markdown
Contributor
Fails
🚫

Commit ae070fe does not meet requirements of Commit Message Quality:

  • title is too long (max 79 characters)
🚫

Commit db17e35 does not meet requirements of Commit Message Quality:

  • body lines must not exceed 79 characters
🚫

Commit 41e9cae does not meet requirements of Commit Message Quality:

  • body lines must not exceed 79 characters

Generated by 🚫 dangerJS against 59295e2

@github-actions

Copy link
Copy Markdown
Contributor

Picasso design patterns check failed

Rule Status
2 — Reuse prop names across components
4 — Mirror native HTML prop names (event signature carve-out)
9 — Use variant-style string-literal unions for visual variations
11 — Use as to change the rendered element ❌ Link/Typography widen as from the canonical narrowed shape to bare ElementType/any

Base automatically changed from feature/picasso-modernization to master July 24, 2026 07:38
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.

4 participants