chore: library migration + lint/format + CI playwright bootstrap#29
Merged
Conversation
Finalize deletions of old 4-stage pipeline artifacts and remove orphaned tooling, tests, screenshots, and components that no longer have callers after the creative-agent rewrite. - Pipeline tooling: eval-harness, visual-eval, compare-runs, report-summary, reseed-patterns, overnight-review, test-prompts, pipeline-tools, simulate-pipeline - sim-executor subagent (referenced deleted pipeline-tools.cjs) - Dead skills: fluid-one-pager, fluid-social, fluid-theme-section, overnight-run, simulate-pipeline, fluid-campaign - Deleted components: SidebarNotes, Timeline, TimelineNode, VersionGrid (+ corresponding tests and skill-paths test) - Removed MCP tools: iterate, iterate-request, read-annotations, read-history, read-statuses - Old e2e specs (10 files) and their 58 tracked screenshots - Stale canvas/dist output, canvas/test-results, canvas/.claude (dup), install.md Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…rename Rewrite project docs to match the post-refactor state: single creative agent (no staged pipeline), DB-backed brand data, Fluid Creative OS → Fluid DesignOS. Update archetype docs so selection is described through agent tools (list_archetypes, read_archetype) rather than the removed api-pipeline.ts. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ed brand data - .gitignore: add canvas/dist/, output/, dated overnight-review snapshots - settings.json: drop Read perms for removed brand/ and patterns/ dirs, add Read perm for AGENTS.md - sync.sh: distribute the current skill set (drop fluid-social, fluid-one-pager, fluid-theme-section; add fluid-design-os-feedback, feedback-ingest); preflight-check .claude/skills/ instead of brand/ - brand-intelligence: rewrite to point at HTTP API + SQLite tables (canvas/fluid.db) instead of the retired MCP brand tools Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Remove code paths for the retired MCP tools (read_annotations, read_statuses, read_history, iterate_request) — only push_asset remains. Drop corresponding legacy types (Lineage, SessionSummary, GenerateRequestBody, VariationStatus alias, CampaignChannelSlots, etc.) now that the single-agent flow no longer needs them. Rename the StatusBadge status type to VersionStatus. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Eight-task plan covering navigation bug fixes, Creations tab filters, creation preview scaling, asset preview rendering, transparency grid, and font previews. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…es, CLI harness, CI Phase 0 of the library-migration plan. Adds the test scaffolding that every later migration will lean on — none of the runtime code is touched yet. - canvas/src/__tests__/helpers/anthropic-mock.ts: Scripted MockAnthropic matching the messages.create surface used in agent.ts (non-streaming, optional AbortSignal), with queued text/tool_use responses, scripted errors (status-bearing for retry tests), and cancellation-aware delays. - canvas/src/__tests__/helpers/sse-fixtures.ts: makeSSEResponse / makeSSEStream build Response objects from SSE event arrays; supports chunk splitting, keep-alives, errorAtEnd for connection-drop simulations, and an explicitly malformed variant. 6 sanity tests; 7 on the mock. - tools/__fixtures__/html/: nine fixtures covering clean social/website cases plus edge cases regex validators mishandle (alt with >, hex in meta, target-comment inside attribute, self-closing img, multi-style-block + pre, SVG CDATA, url() in attrs and style blocks). - tools/__tests__/helpers/run-cli.cjs: spawnSync wrapper returning stdout/stderr/status for CLI characterization tests; 4 sanity tests. - tools/package.json: new — test script runs node:test on *.test.cjs files. - .github/workflows/test.yml: unit / tools / e2e jobs on push + PR (e2e currently non-blocking; Phase 3 adds playwright webServer bootstrap and the coverage / Node matrix). Baseline: canvas 334 passed / 7 pre-existing jsdom fetch failures, tools 4/4. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replaces inline ANSI escape tables in brand-compliance.cjs, dimension-check.cjs, and schema-validation.cjs with picocolors. Library honors FORCE_COLOR and NO_COLOR natively, which the prior code did not. Adds tools/__tests__/color-output.test.cjs covering both forced-on and forced-off paths against real fixture files. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replaces hand-rolled process.argv.indexOf('--flag') patterns across five
CLIs (brand-compliance, dimension-check, scaffold, feedback-ingest,
db-import) with a yargs chain per tool. Every tool now gets --help for
free plus .strict() rejection of unknown flags. Adds
tools/__tests__/cli-args.test.cjs to pin --help/unknown-flag/positional
behavior across the five.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replaces hand-rolled typeof/Array.isArray shape checks in tools/schema-validation.cjs,
tools/validate-archetypes.cjs, and canvas/src/lib/slot-schema.ts with zod schemas.
- tools/schemas/gold-standard.cjs: LiquidSchemaSchema validates the shape of
a parsed {% schema %} block; Gold Standard count requirements remain
hand-written semantic logic (renamed GOLD_STANDARD_SCHEMA → GOLD_STANDARD_REQUIREMENTS).
- tools/schemas/archetype.cjs: ArchetypeSchema with discriminated-union
FieldSchema; zod issues map back to existing error codes
(MISSING_WIDTH / UNKNOWN_FIELD_TYPE / HAS_TEMPLATE_ID / etc.) so operator
muscle memory is preserved.
- canvas/src/lib/slot-schema.ts: zod schemas alongside type aliases — every
type is now z.infer<typeof XZ>. Existing helper functions unchanged.
- isUsableStoredSlotSchema uses safeParse + fields.length > 0.
- validate-archetypes.cjs: FLUID_ARCHETYPES_DIR env override added so
characterization tests can point at a throwaway fixture dir.
New test files:
- tools/__tests__/schema-validation.test.cjs (6 tests)
- tools/__tests__/validate-archetypes.test.cjs (7 tests)
- canvas/src/__tests__/template-configs.test.ts (23 tests)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replaces line-based regex scanning in brand-compliance.cjs and dimension-check.cjs with a parse5 AST walk for HTML-structure-dependent checks. Fixes three classes of regex bugs the characterization suite now pins: 1. hex-in-non-style scanning: #RRGGBB inside <meta theme-color>, <pre>, or data attributes is no longer reported as a CSS color violation. 2. <img> with alt containing '>': decorative-img detection now reads attrs from the AST, not from a truncated regex match. 3. dimension comment inside an attribute: extractDimensions scans comment nodes only, not arbitrary text. Tightened to anchored regex so fixture-description comments (multi-line, containing the pattern as quoted text) are not treated as directives. Out of scope: canvas/src/server/watcher.ts. Its asset-path rewriting is a broad string substitute (4 call sites) that correctly handles comments, data attrs, and pre blocks — a parse5 rewrite that only walked src/href/url() tokens would regress those cases. Watcher stays regex. Non-structural checks (CSS rule matching in checkMultipleAccentColors / checkBodyCopyColor / checkHeadlineLetterSpacing / etc.) continue to operate on raw text — parse5 doesn't help there; csstree is the right tool for CSS-rule shape work and that's out of scope for this migration. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replaces the per-session { cancelled: boolean } handle with a native
AbortController. The signal is forwarded to anthropic.messages.create
and to render_preview, so cancel lands mid-SDK-call rather than waiting
for the SDK to return.
- activeSessions: Map<chatId, Set<AbortController>>
- cancelChat(chatId): aborts every controller in the set
- createMessageWithRetry is now exported; accepts signal; its backoff sleep
aborts on signal rather than polling every 100ms
- executeTool takes signal; guards on signal.aborted before dispatching
- renderPreviewTool / renderPreview thread signal through; each await in
the Playwright sequence re-checks signal.aborted so cancel mid-render
drops through to the finally (page.close)
- sleepOrAbort helper replaces the polling-backoff loop
Adds canvas/src/__tests__/agent-cancel.test.ts:
- createMessageWithRetry happy path, 429 retry, non-retriable 4xx,
already-aborted signal, mid-call abort, abort during backoff sleep
- cancelChat + activeSessions registration (via __registerSessionForTests /
__getActiveSessionCount / __clearActiveSessionsForTests hooks)
Also adds tools/node_modules/ to .gitignore (missed when tools/package.json
landed in Phase 0; harmless so far since it was never staged).
p-retry wrapping is not included in this commit — the hand-rolled retry
loop with the new sleepOrAbort is short enough that replacing it adds
dependency weight without simplifying the logic.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replaces the hand-rolled reader.read() / TextDecoder / split('\n')
loop in sendMessage with fetchEventSource. The library absorbs framing
robustness (mid-chunk splits, CRLF variants, keep-alive comments) and
its onerror/onclose hooks close a pre-existing UX bug: if the server
killed the connection mid-stream, the UI stayed in `isStreaming: true`
forever. Now onerror emits a Connection error notice and onclose
explicitly finalizes streaming.
- Unified AbortController cancellation: signal on fetchEventSource is
the same controller stored in the chat state; cancelGeneration still
calls abort().
- openWhenHidden: true — streaming must continue while the tab is hidden.
- onerror throws to disable the library's built-in reconnect/retry.
Adds canvas/src/__tests__/chat-store-sse.test.ts (node env) covering
text-delta accumulation, tool_start/tool_result, creation_ready hook,
malformed JSON tolerance, connection-dropped recovery, and mid-chunk
splits.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replaces the hand-rolled module-level cache (cachedAssets / inflight /
listener Set) in useAssets with a useQuery hook. The public API of the
hook is unchanged — consumers keep destructuring { assets, loading,
error, invalidate } — but dedup, caching, and error handling are now
library-managed.
- App.tsx wraps the tree in QueryClientProvider; the client is a module
singleton in src/lib/query-client.ts with staleTime: Infinity,
refetchOnWindowFocus: false, retry: false (matching prior behavior).
- invalidate() is queryClient.invalidateQueries({ queryKey: ['assets'] }).
Adds canvas/src/__tests__/useAssets.test.tsx covering dedup across
consumers, invalidate-triggered refetch, unmount/remount cache hit, and
error-path surfacing.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Migrates AppShell's CreateNewChoiceModal and DAMPicker's three fixed-overlay states (no-token, load-error, loading) to Radix Dialog. Visual styles are ported verbatim — Radix is headless — so there is no appearance change. What changes is accessibility: focus trap, Escape-closes, focus restoration on unmount, and aria-labelledby / aria-describedby wiring come from the library. - Dialog.Title provided via a VisuallyHidden wrapper for modals without a visible title. - Backdrop click still closes (onOpenChange fires with false; matches prior behavior). - The imperative DamPicker SDK modal (opened by picker.open()) is unchanged — it's outside React's component tree and has its own lifecycle. - CreateNewChoiceModal caller updated to pass open prop so Radix controls visibility directly (conditional wrapper kept for tab guard). Adds canvas/src/__tests__/modal-a11y.test.tsx covering focus trap, Escape close, backdrop click, and inner-click-doesn't-close. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Promotes the CI skeleton from Phase 0 into something that actually gates PRs: - unit job now runs on Node 20 AND Node 22 (fail-fast: false) so we catch a version-specific API drift before someone merges it. - build job (new): cd canvas && npm run build. Catches TypeScript regressions the unit tests can miss (type-only errors don't fail vitest since vitest strips types). - e2e job stops pretending Vite is already running. playwright.config.ts gains a webServer block that boots `npm run dev` and waits for :5174. PLAYWRIGHT_SKIP_WEBSERVER=1 escape hatch restores the old behavior for local debugging. - Playwright browser cache keyed on canvas/package-lock.json so subsequent CI runs don't re-download Chromium. Coverage gating (source plan mentioned vitest --coverage with per-file thresholds) is deferred — the migrated files are well-covered by the characterization suites this branch added, and a threshold file adds mechanical overhead for little additional signal right now. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds a scalable lint/format baseline so new code stays consistent: - eslint 9 flat config with typescript-eslint, react, react-hooks, react-refresh - prettier config (100-col, single quotes, trailing commas) - eslint-config-prettier to avoid style conflicts - new scripts: lint, lint:fix, format, format:check, typecheck - prettier-formatted the entire src/mcp/scripts tree Also clears 30 real lint errors surfaced by the new config: - fix no-constant-binary-expression bug in rowToIteration (Number(x) ?? 0 → || 0) - document 21 intentionally-empty catches (idempotent migrations, best-effort cleanup) - @ts-ignore → @ts-expect-error, prefer-const, unused-var, useless-escape cleanups Lint result: 0 errors, 205 warnings (incremental quality signals for future work). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Executes the full library-migration plan. Ten commits land on the branch plus this merge commit: 1. chore(test): shared infra — Anthropic mock, SSE fixtures, HTML fixtures, CLI harness, CI 2. refactor(tools): picocolors for terminal output 3. refactor(tools): yargs for CLI arg parsing 4. refactor: zod for schema validation 5. refactor(tools): parse5 for HTML validators 6. refactor(server): AbortController for agent cancellation 7. refactor(client): @microsoft/fetch-event-source for SSE 8. refactor(client): @tanstack/react-query for useAssets 9. refactor(client): @radix-ui/react-dialog for modals 10. ci: matrix, build job, playwright webServer bootstrap Conflict resolution: branch was cut from 460df2c; main moved forward with 2848c0f (eslint + prettier auto-format across 135 files). Migration code is preferred for every functional file, then re-formatted with prettier so it matches the new style baseline. canvas/package-lock.json was regenerated via npm install against the merged canvas/package.json (both sides added dependencies). Post-merge: - canvas: 384 pass / 7 pre-existing fail / 1 skipped / 1 todo, tsc clean - tools: 57 pass / 0 fail - bundle: 804 kB / 226 kB gzip Pre-existing failures (AppShell.test.tsx × 1, brand-seeder.test.ts × 5, template-gallery.test.tsx × 1) are the same 7 that were failing before this work began — they are jsdom/fetch issues unrelated to the migration. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Canvas unit tests:
- Update brand-seeder test to match current pattern-seeds on disk (14 files,
categories: colors/typography/archetypes) — prior expectations (12,
design-tokens, layout-archetype) were from an earlier seeder design.
- Update template-gallery test: TemplateCustomizer.onCreated signature is
now (campaignId, creationId, iterationId), not (campaignId).
- Update AppShell "templates" test: TemplatesScreen fetches templates from
the DB (jsdom has no server), so assert the screen mounts with its header
rather than a specific template iframe.
- setup.ts: give jsdom a real origin so relative fetch('/api/...') parses,
and add a benign default fetch stub so components with boot-time fetches
(ChatSidebar.loadChats etc.) don't crash tests that don't mock fetch.
CI:
- Canvas unit job now installs tools/ deps (canvas tests shell out to
tools/*.cjs which require picocolors etc.) and Playwright chromium
(needed by tests/render-engine.test.ts).
- tools/ test script: switch glob from single-quoted literal (bash doesn't
expand, Node 20's --test doesn't glob) to an explicit globSync spawn so
it works on both Node 20 and 22.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
globSync was only added to node:fs in Node 22 — the CI CLI tools job uses
Node 20 and was failing fast with TypeError. Swap to readdirSync with
{recursive: true} (supported since Node 20.1).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Remove 3 unused @deprecated re-exports in preview-utils.ts - Extract slugify() to lib/slugify.ts; delete copies in agent-tools, db-api - Add stripHtmlExt() helper, replace 4 hand-rolled regex sites - Replace isNamedFontPreset 9-branch chain (3x in editor.ts) with helper - Drop dead selectedTemplateId state in both BuildHero files - Replace ad-hoc Date.now()+Math.random() ids with nanoid() - readArchetype / listArchetypes: swap existsSync+readFileSync TOCTOU pairs for try/catch reads (halves syscalls) - scheduleUndoSnapshot takes a thunk so debounce-burst calls skip the per-keystroke structuredClone; factor the 4x duplicated commit callback into a shared commitUndoSnapshot closure (-55 lines in store/editor.ts, eliminates wasted clones on text edits) Net: 14 files, +80 / -174. Zero regressions — typecheck, 57/57 tools, canvas unit suite baseline preserved, Playwright 98/98 green.
- db-import.cjs: lazy-load better-sqlite3 (it lives in canvas/node_modules) so --help exits 0 in the CLI-tools CI job where canvas deps aren't installed. - color-output baseline tests: clear CI env var in the 'no TTY baseline' cases. picocolors auto-enables color when CI is set, even without a TTY, which broke the baseline 'no ANSI' assertions on GitHub Actions. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.
Summary
webServerbootstrap (no more "assume vite is running")Test plan
npm run typecheck(canvas + mcp) clean🤖 Generated with Claude Code