Lead Web Engineer application for Genki

Liam Trubshaw

I'm a hands-on engineering lead in London. I've owned technical direction at TwoTen Studio for 8 years while building every week, and this year I took Frame Foundry from idea to paying customers in 6 months.

I built this page the way I'd work at Genki, with Claude Code, clear guardrails and a safe deploy pipeline.

What I've built

  • Frame Foundry

    Co-Founder, 2026 to now

    A multi-tenant SaaS membership platform, taken from idea to paying customers in 6 months. I built it end to end in Next.js, React and TypeScript, with 81 tables and 70 migrations in Prisma, a preview deployment for every change and weekly releases behind feature flags. Tenants are isolated with row-level security in Postgres.

    • Next.js
    • TypeScript
    • PostgreSQL
    • Prisma
    • Vercel
  • Events platform on Amazon EKS

    TwoTen Studio, during Covid

    A WordPress Multisite virtual events platform for a large London events agency. I architected it, containerised it with Docker and load-balanced it on Amazon EKS.

    • Docker
    • Amazon EKS
    • WordPress Multisite
  • Shared starter codebases and block libraries

    TwoTen Studio, 2018 to now

    Starter codebases, component libraries and plugins shared across many client sites, so every new project started from the same foundations. Libraries of React blocks with guardrails let non-technical marketing teams build and publish pages safely without a developer.

    • React
    • TypeScript
    • GitHub Actions
    • Playwright

An architectural bet I lived with

At Frame Foundry I put every customer organisation in one shared Postgres database, with a tenant ID on every row. For a small team shipping weekly, one schema meant one set of migrations, one backup and one place to look when something broke. A database per tenant would have multiplied that work before we had the customers to justify it.

The cost is that isolation depends on every query remembering its tenant filter, and one missed filter could show one organisation another's data. So once the data model settled, I rolled out row-level security. Now the database itself blocks access to other organisations' rows, and the filter in the application is the first check rather than the only one.

How I work with AI agents

I use Claude Code every day, and over the last 2 years I rebuilt my team's workflow around it. I give it clear rules and a plan to work to, then check what it produces through review and automated tests before anything ships.

These are the real prompts from building this page, with what worked and what I changed.

  1. 01 Establish Project Standards

    The prompt

    Generate a CLAUDE.md containing the project's architecture, coding conventions, technical constraints, and development standards.
    
    Stack:
    - Astro 7
    - TypeScript (strict mode)
    - SCSS Modules
    - React (isolated interactive components)
    - Cloudflare Pages
    
    Requirements:
    - Enforce strict TypeScript typing. Avoid `any`.
    - Use component-scoped SCSS Modules, with global CSS restricted to resets and design tokens.
    - Prioritise semantic HTML, WCAG 2.2 AA accessibility, keyboard navigation, and progressive enhancement.
    - Minimise client-side JavaScript and unnecessary hydration.
    - Require linting, type checking, and Playwright smoke tests before considering a task complete.
    - Do not introduce dependencies without approval.
    
    Define explicit acceptance criteria and validation commands.

    What worked

    Generated a centralised development specification with enforceable coding standards, validation commands, and a consistent definition of done. This provided a repeatable quality gate for subsequent implementation tasks.

    What I changed

    Introduced strict content-integrity requirements to prevent AI-generated copy modifications. Added a restriction on em dashes and required potentially incorrect content to be flagged rather than automatically rewritten.

  2. 02 Architecture & Implementation Planning

    The prompt

    Review CLAUDE.md and produce a technical implementation plan before generating application code.
    
    Include:
    - Proposed directory structure
    - Component architecture and responsibilities
    - Design token strategy
    - Client/server rendering boundaries
    - Accessibility considerations
    - CI/CD pipeline configuration
    - Potential implementation risks
    
    Keep the plan concise. Identify uncertainties and architectural trade-offs. Do not implement anything yet.

    What worked

    Identified two significant architectural considerations: Content Security Policy compatibility with Astro's hydration scripts, and cumulative layout shift caused by transitioning server-rendered content into interactive tabs.

    What I changed

    Rejected a proposed custom post-build script for generating CSP script hashes. Astro 7 provides native CSP support through security.csp, eliminating the need for custom hashing infrastructure. Retained Cloudflare's _headers configuration for directives unsupported by meta-based CSP.

  3. 03 Project Scaffolding & CI Configuration

    The prompt

    Initialise the Astro application using the approved architecture.
    
    Configure:
    - Strict TypeScript configuration
    - ESLint flat configuration
    - Prettier formatting
    - Playwright browser testing
    - GitHub Actions CI
    - Lighthouse performance auditing
    
    Create a baseline Playwright smoke test that verifies successful page rendering and the presence of primary document headings.
    
    Configure CI to execute linting, type checking, browser tests, and Lighthouse auditing on pull requests.
    
    Do not implement the application content at this stage.

    What worked

    Established a fully configured development environment with strict compiler settings, consistent static analysis, and an automated CI pipeline aligned with the project's acceptance criteria.

    What I changed

    Resolved several upstream compatibility constraints:

    • Standardised on Node.js 24 LTS to satisfy Astro 7 runtime requirements.
    • Pinned TypeScript 6.0 due to compatibility limitations in typescript-eslint and Astro's type-checking tooling.
    • Replaced the original JSX accessibility ESLint package with a maintained fork supporting ESLint 10.
    • Configured Astro's preview server with --ignore-lock to prevent orphaned server processes during agent-driven Playwright execution.
  4. 04 Component Implementation

    The prompt

    Implement the page sections defined in the approved architecture.
    
    Requirements:
    - Use Astro components for static content.
    - Use SCSS Modules for component-level styling.
    - Preserve all content from content.md verbatim.
    - Follow a mobile-first responsive implementation.
    - Implement tablet and desktop layouts through progressive enhancement.
    - Use semantic HTML and accessible document structure.
    - Do not introduce client-side JavaScript.
    
    Maintain clear component boundaries and avoid unnecessary abstraction.

    What worked

    Created a modular component architecture, with each section encapsulated in its own Astro component and associated SCSS Module. Content was preserved without modification, and responsive behaviour was implemented through CSS.

    What I changed

    Removed an automatically generated skip-navigation link. As the document had no preceding navigation region, the link provided no meaningful navigation shortcut and introduced unnecessary interface complexity.

  5. 05 Interactive React Island

    The prompt

    Implement an isolated React island for the "How I work with AI agents" section.
    
    Data requirements:
    - Load content from a typed JSON data source.
    - Each entry must contain the original prompt, implementation outcome, and subsequent changes.
    
    Interaction requirements:
    - Implement the WAI-ARIA tabs pattern.
    - Support keyboard navigation using arrow keys, Home, and End.
    - Maintain appropriate focus management and screen reader semantics.
    - Server-render all content as a readable list before hydration.
    - Preserve complete functionality and content access when JavaScript is disabled.
    - Minimise the client-side bundle and hydration overhead.

    What worked

    Built a progressively enhanced interface that initially renders all entries as static HTML. Following hydration, React enhances the content into an accessible tabbed interface with keyboard interaction and appropriate ARIA relationships.

    What I changed

    Corrected responsive tab navigation after the seven-tab layout wrapped across multiple rows at smaller viewport widths. Consolidated the tabs into a single horizontal row below desktop breakpoints. Also normalised <code> typography to use the project's monospace design token rather than browser defaults.

  6. 06 Independent Engineering Review

    The prompt

    Perform an independent pre-release code review against CLAUDE.md.
    
    Audit:
    - Component architecture and maintainability
    - Accessibility and keyboard interaction
    - Hydration behaviour and layout stability
    - Runtime performance and JavaScript payload
    - Cloudflare Pages security configuration
    - Unused code and unnecessary dependencies
    - Test coverage and potential regressions
    
    Report findings before applying changes.
    
    Prioritise issues by severity and address them individually, documenting the rationale for each fix.

    What worked

    Executed the review using a separate agent to reduce implementation bias. The audit identified 11 issues, including two significant interaction defects:

    • Previous/Next navigation could unmount the active panel while keyboard focus remained within it, resulting in lost focus.
    • Hydration caused approximately 3,700px of vertical layout collapse on mobile devices as the static list transitioned into the tab interface.

    What I changed

    Resolved 10 of the 11 findings, with some solutions differing from the agent's recommendations.

    Changed the React island's hydration directive from client:visible to client:idle. This ensured the interactive layout initialised shortly after page load rather than introducing substantial layout shifts when the section entered the viewport.

    Replaced a proposed content-specific nowrap utility with a reusable heading-level treatment for hyphenated words, preserving the original copy.

    Retained PLAN.md as an immutable record of the initial architecture, documenting deviations rather than retroactively modifying the plan.

    The remaining repository-link issue was classified as a deployment/publication task rather than an application defect.

  7. 07 Security Hardening & Deployment

    The prompt

    Configure production security headers for Cloudflare Pages.
    
    Implement:
    - Content Security Policy (CSP)
    - HTTP Strict Transport Security (HSTS)
    - X-Content-Type-Options
    - Referrer-Policy
    - Permissions-Policy
    
    Ensure Astro's generated scripts and the React island remain compatible with the CSP.
    
    Create a concise README documenting:
    - Local development
    - Available validation commands
    - CI pipeline behaviour
    - Production build and deployment process
    
    Validate the production build rather than relying exclusively on development-server behaviour.

    What worked

    Added Cloudflare Pages security configuration and deployment documentation. Used Astro's native CSP handling for generated inline scripts, with _headers providing directives that cannot be enforced through a CSP meta element.

    What I changed

    Introduced negative testing to verify that CSP enforcement was functional rather than simply present.

    Removed a valid script hash from the generated output and confirmed that the associated browser test failed as expected. Added a separate assertion verifying that the production document includes the required CSP configuration.

    This established regression coverage for both CSP presence and script execution restrictions.

  8. 08 Interactive Canvas Navigation

    The prompt

    Extend the existing application with a game-like navigation experience.
    
    Concept:
    Create an animated world consisting of multiple islands, each representing a section of the CV. Users should be able to fly between islands and open the corresponding content.
    
    Technical requirements:
    - Use the Canvas API with vanilla TypeScript.
    - Avoid additional runtime dependencies.
    - Preserve the existing Astro page as the accessible baseline.
    - Treat the interactive world as a progressive enhancement layer.
    - Provide equivalent keyboard and screen reader navigation.
    - Respect prefers-reduced-motion.
    - Ensure content remains accessible without JavaScript.
    - Maintain existing performance and accessibility targets.

    What worked

    Introduced a lightweight canvas-based navigation layer without replacing the underlying document architecture.

    The implementation uses native TypeScript and browser rendering APIs, avoiding additional frameworks or rendering dependencies.

    Each island maps to an existing content section, which opens in an associated panel. A parallel island-navigation list provides equivalent access for keyboard and assistive technology users.

    The application falls back to the standard page when JavaScript is unavailable or reduced motion is preferred. Lighthouse continued to report a score of 100.

    What I changed

    Addressed a CSP limitation involving the inline bootstrap script responsible for selecting the initial rendering mode.

    Astro automatically hashes scripts managed by its build pipeline, but manually inlined scripts require explicit handling. The bootstrap script's hash is now generated from the same source used to inline it, ensuring consistency between the executable content and CSP declaration.

    The bootstrap script is positioned after the CSP meta element to ensure the policy applies during execution.

    Also corrected the initial navigation state: the proximity detection previously triggered a "Land on Home" action immediately after departure. The navigation controller now excludes the island the player has just left until the departure state has completed.

Letting non-engineers ship safely

At TwoTen I built libraries of React blocks with guardrails, so marketing teams could build and publish pages without a developer. At Genki I'd set it up the same way.

  1. Starter templates. New pages and components start from a shared starter with the design tokens, accessibility defaults and tests already in place.
  2. Required review. Every change goes through a pull request, and an engineer reviews anything that touches shared components.
  3. Automated checks. CI runs lint, type checks, Playwright tests and Lighthouse on every pull request, so the obvious problems are caught before a person has to look.
  4. Preview deploys. Every pull request gets its own preview URL, so whoever made the change can check it on a real phone before anything reaches production.

This page is built and deployed the same way.