> Vault ← portfolio
← back
AI Interview: Resume Q&A · ~5,119 words · 26 min read

AI interview: resume question tree

Built for a Mercor-style AI screen, works for any resume-driven interview. Every question here comes from a line on my resume — if it’s not on the resume, it’s not here, because the interviewer never sees it.

Covers: StashBase, SamurAI Studios, fiddle-factory (contractor), fiddle-factory (intern), the skills block, trove/cn, and a B.Tech in Mechanical Engineering from NIT Durgapur.


How the screen works

20–30 minutes of video with an AI interviewer that’s already parsed my resume. No human watches live, a transcript gets scored against a rubric.

It adapts, and the ramp only goes one way. The first two or three answers set a baseline — strong answers pull the next question deeper, weak ones keep it shallow and cap the score. Recovering late is harder than starting well.

It punishes silence too. Long pauses read as a pacing problem, so I think out loud instead of thinking quietly.

What it’s really testing: whether the person who wrote this resume and the person answering are the same person. Every bullet is a claim, and a follow-up is the cheapest way to check it — do I own the decision, can I explain the mechanism one level below the bullet, do I know what it cost.

“You said 56 primitives, what was in them?” isn’t a trick, it’s just testing whether a number is real. Any number I say has to have a real breakdown behind it, not just a metric I threw out to sound thorough.


Resume audit

What each line invites, before the questions themselves. Green means I can go three questions deep without effort. Amber means it needs rehearsal.

Resume lineRiskWhy
Milkdown Crepe editor, without losing source fidelityAmberstrong claim, need one concrete hard example
Hardened rendering + nav featuresGreenspecifics live in the answer
Shared Agent Contract, parity testsAmberbest architecture story, most probeable
Playwright E2E, cross-platform CIAmber”how do you stop visual tests being flaky” has a real answer
Creative-brief pipeline, golden evaluationsAmber”golden evaluations” is jargon, be ready to define it
Multi-scene storyboards, migrationGreenclean end-to-end story
Multi-provider generationAmbernaming providers means naming how they differ
Shared primitives, image format optimizationGreenroles and format live in the answer
Canvas hydration race, Storybook, PKCEGreenthree solid stories, know the details
react-flow to tldraw migrationGreenbest story on the resume, follow-up is always the data conversion
StackBlitz to e2bGreenclean tradeoff, real constraint
Languages (TS, JS)Greenstrong everywhere, expect a type-system question
trove/cnGreenmine end to end, recent
B.Tech Mechanical EngineeringAmberalways asked, needs a confident answer, not an apology

Opening ninety seconds

Highest-value answer in the session, because the ramp only goes one way.

I’m a frontend-leaning full-stack engineer, mostly TypeScript and React. The thread through my work is building difficult interactive surfaces — canvases, editors, agent panels. At fiddle, an AI component design tool, I migrated the drawing canvas from react-flow to tldraw and later traced a hydration race that left every new user with a blank canvas. At SamurAI Studios I rebuilt storyboards from project-owned to scene-owned, data model through to the workspace, without breaking existing projects. Right now I contribute to StashBase, a local-first knowledge base, where I own the Markdown editor and wrote the contract that lets Claude and Codex sit behind one interface. I also maintain trove/cn, a registry of copyable React components.

Four hooks in there — tldraw migration, hydration race, storyboard schema, agent contract — every one a place I can go three questions deep.


StashBase

What do I own in StashBase?

It’s a local-first Electron app that turns files on disk into searchable context for AI coding agents. Notes stay on the machine, the app indexes them, agents reach them over MCP.

I own three areas:

Local-first, why?

Nothing leaves the machine unless the user sends it. That’s the product promise, and it constrains engineering — no server to hold state, so the Electron main process owns the filesystem and the renderer owns nothing durable. It also means the agent adapters use whatever’s installed locally rather than calling a hosted API.

Why a desktop app?

It needs real filesystem access and it needs to launch local agent binaries. Neither is possible in a browser tab.

Walk me through the Milkdown Crepe editor — what does “without losing source fidelity” mean?

Crepe is Milkdown’s batteries-included editor, similar in feel to Notion — it renders Markdown as formatted content you type directly into, so there’s no separate preview pane. The document you see is the document.

The catch: underneath, it’s a ProseMirror document, not text. Every Markdown construct has to survive a round trip — parse into nodes, edit as nodes, serialise back to Markdown that’s byte-comparable to what a plain text editor would produce. Source fidelity means that round trip doesn’t quietly rewrite your file. The ones that actually broke: links, code fences, lists, blockquotes, paste, and Find.

Which one was hardest?

Lists and blockquotes on exit. You’re inside a list item, press Enter twice — the editor has to decide whether you’re adding an item, exiting a nesting level, or leaving the list. Get it wrong and the serialiser emits different indentation than the file had. I shipped it as its own change, because the behaviour is small but the blast radius is every list in the library.

Why does paste matter?

Paste is where two representations collide. Copying inside the editor copies ProseMirror nodes, copying from a browser brings HTML, copying from a terminal brings plain text. Accept the HTML path naively and you inherit whatever markup the source page had — a rendering problem and a security one. Same reason the rendering bullet says sanitized HTML.

And Find?

In a virtualised, node-based editor the match might not be mounted. Find has to locate the match in the document, then scroll the actual rendered position into view — I shipped the scroll-into-view part separately from the matching.

Hardened Markdown rendering — against what?

Against untrusted input, mostly. A knowledge base renders files the user didn’t necessarily write — anything an agent generated, anything pasted from the web. Markdown allows raw HTML, so rendering it directly means script injection inside an Electron renderer, worse than a browser tab. HTML goes through sanitisation before it renders. Beyond that, hardening meant using package-native implementations for footnotes and heading anchors instead of the hand-rolled ones that were there.

What’s “hidden frontmatter”?

YAML metadata at the top of the file. Real, part of the document, but not prose — a reader shouldn’t see it as a code block on every note. It stays in the file and doesn’t render. Stripping it would lose data; rendering it makes every note ugly.

Outline and Quick Open?

Quick Open is navigation across files, the outline is navigation inside one. I moved the outline into the Files sidebar rather than giving it its own panel — one place to go find things.

Explain the Shared Agent Contract

StashBase drives more than one coding agent. Claude Code and Codex run as local processes with different protocols, session models, permission systems, and different ideas about what deleting a chat means. Before the contract, the app knew about both in the UI layer, so every feature had two code paths. The contract is one interface both runtimes implement — start a session, stream deltas, expose skills, apply an access mode, delete a chat. The panel talks to the contract, not to a specific runtime.

What did you leave out of the interface?

Anything only one of them can do. Widen the interface to fit every Codex capability and the Claude adapter grows methods that throw, and callers start branching on runtime again — the abstraction’s bought nothing. So the contract holds what both can honour, and runtime-specific things are exposed as capabilities the panel can query rather than methods it can call blindly.

What are parity tests, why do you need them?

A shared test suite that runs against both adapters and asserts they behave the same. Without it, the contract’s just a comment — adapters drift the moment someone fixes a bug in one and not the other, invisibly, because each adapter’s own tests still pass. The parity suite is what catches that. Wired into CI, colocated with the adapters.

Where did the adapters actually diverge?

Deletion and access. Codex needed chats permanently deleted rather than marked, and access-mode validation was implemented twice with slightly different rules, so I pulled it into the shared layer.

A runtime that’s not installed?

Don’t silently fail. If a built-in runtime is unavailable, the panel says which one and how to install it. An agent panel that just shows nothing is the worst outcome — the user can’t tell if it’s broken or empty.

The Playwright suite — how do you keep Electron E2E from being flaky?

Every spec launches a real Electron app through a dedicated entry file against a disposable fixture directory, so a test never touches actual folders. Two things drove most of the early flake — launch readiness (the window exists before the app’s usable, so tests need an explicit readiness signal, not a wait) and port release (the next test starts before the previous app’s let go, so shutdown had to be serialised).

Visual baselines aren’t permanently flaky?

Only if you let CI regenerate them. Projects are split into smoke, functional, and visual; visual baselines are reviewed and committed per platform, refreshed through a manual workflow. A baseline that updates itself isn’t a test — the tradeoff is reviewing modified baselines every time a legitimate visual change lands.

How do you test the agent panel if it spawns a real agent?

You don’t spawn a real one. There’s a deterministic fake that speaks the Codex app-server protocol, so the panel exercises the real code path against a scripted peer.

You refactored the renderer architecture — what was wrong with it?

The directory layout implied layering — app on features on store on common — but nothing enforced it, so lower layers imported back upward. Store reached into the agent panel for the agent catalog, common imported from four different features, common and store imported each other — a cycle. Ordinary cause: shared vocabulary got written wherever it was needed first, and nobody promoted it when a second feature needed it too.

Moving files isn’t cosmetic?

Moving files alone lasts one pull request. What matters is I added linting with per-layer import restrictions — one rule block per feature naming the siblings it can’t reach, each pointing at where shared code should go instead. There was no lint tooling at all before that.

Did you have to break the rules anywhere?

Tests are exempt — a renderer test legitimately imports a feature component to render it. I calibrated the production rules so the codebase lands clean rather than shipping a config with hundreds of warnings nobody reads.


SamurAI Studios

Three months is short — what did you actually get done?

If you want one thing to judge me on, it’s the storyboard migration.

Why doesn’t your GitHub graph show this?

Private repos under a company account, commits attributed to a work identity rather than my personal one. I’d rather explain that up front than have someone quietly assume I did nothing for three months.

Walk me through the creative-brief pipeline

A creative deck goes in, a structured brief comes out:

Why persist every stage — that’s a lot of storage.

A pipeline that behaves like one opaque model call is unmaintainable. When a brief comes out wrong, the question is always which stage was wrong — no per-stage artifacts, no answer. Also why I built the operator console: job search, live job detail, artifact previews, brief export. Someone non-technical can look at the captions and see the model was fed the wrong three images. Storage’s cheap compared to that.

What are golden evaluations?

A fixed set of decks with known-good expected briefs, checked into the repo. The pipeline runs against them, we diff. Regression testing for a non-deterministic system — you can’t assert exact string equality against a model, so the assertions target structure and required facts. Catches a prompt or model change quietly making output worse everywhere, which unit tests can’t.

How did you secure it?

API key protection on the parser endpoints, authentication on the console. Internal service, so the threat model’s mostly accidental exposure rather than a targeted attacker.

The storyboard work — walk me through it end to end

Storyboards started project-owned — one project, one flat list of frames. Then it needed scenes: a project holds scenes, a scene holds the storyboard. A data-model change, not a feature, so it ran through the whole stack.

On the frontend: a multi-scene workspace, scene-aware loading states, final-storyboard and candidate-selection interfaces.

How did you keep old projects working?

Default-scene resolution plus legacy route compatibility. Old projects resolve to a single implicit scene rather than being migrated destructively, and old project-level routes keep answering. Migration guards and shared fixtures so tests cover both shapes, plus a regression suite asserting one scene can’t leak state into another.

What would leak?

Version history and snapshots. Wrong scoping anywhere in that chain, and restoring a snapshot in scene two overwrites frames in scene one — no error, just lost work. That’s the failure I was most afraid of, so it’s what the tests target hardest.

Multi-provider generation — what’s actually hard about that?

Calling the APIs is the easy part. We supported Runway, Veo, and Seedance, plus Kling for final image-to-video clips. The hard parts were consistency, visibility, and cost.

How do providers differ in ways that hit your code?

Different input contracts, different async models, different failure vocabulary. Some take a reference image, some don’t. Some return a job you poll, some hold the connection. The abstraction has to be over the lifecycle — submit, observe, retrieve — not over the parameters, because the parameters genuinely differ.

What’s real-time sync doing here?

Multiple people work in the same workspace, and a generation run started by one of them has to appear for the others.

Media delivery — why did that need attention?

One image URL was doing three jobs — a tile preview, a gallery image, and the original generation, each with different quality and cost requirements. I gave media URLs distinct roles, generated sharper WebP thumbnails, exposed preview derivatives through the API, made the frontend preview-aware so it knows when the small one’s all it needs.

Tailwind v4 migration — what changed?

v4 moves configuration into CSS — theme values live as custom properties in a CSS file rather than a JS config object, so the design system’s CSS-first. I migrated our theme onto v4 tokens and built a design-system module around it.


Fiddle (contractor)

What is fiddle?

A component design tool — you come with rough ideas about what you want to build or experiment with, and get a working prototype shipped directly to your codebase as a PR. No worrying about code generation or the security of running that code, because everything runs inside a cloud sandbox you can see into.

Tell me about the canvas hydration race

New users opened the app to a blank canvas. Returning users were fine, which is why nobody on the team saw it — we all had warm caches. Root cause: tldraw’s onMount fires exactly once, and early. Returning users have React Query data already cached, so onMount runs with data available. New users hit a cold cache — the fetch hasn’t resolved, onMount runs with projectData undefined, the canvas initialises empty, and because onMount only fires once, the data arriving a moment later never re-triggers hydration.

How did you fix it?

A readiness gate. The tldraw editor doesn’t mount until React Query reports the data ready, so onMount always has something to hydrate from. Plus a fallback that detects empty canvas state and retries hydration if data lands late.

Why not just hydrate in a useEffect on the data?

Tempting, and worse. The editor mounts empty then gets populated — a visible flash of an empty canvas, and a window where user input can land before the document arrives. Gating the mount removes the intermediate state instead of patching over it.

How would you have caught this earlier?

An integration test that mounts with a cold cache. The whole bug class is “works for everyone who already has state.” We were early-stage though, e2e testing wasn’t in scope yet.

Storybook across four repositories — why was that hard?

None of the four agreed on anything. shadcn-ui was Tailwind v4 with CSS variables, so mostly volume — stories for every component and its variants. design-engineer was still on Tailwind v3 and wouldn’t render correctly until I migrated it. eleven-labs-ui had a theme bug where dark-mode variables weren’t reaching the Storybook iframe. fiddle itself needed a custom decorator providing a QueryClient, because its components expect Remix loader context that doesn’t exist inside Storybook.

You mentioned a MutationObserver — what for?

Screenshot timing. The naive approach waits a fixed number of milliseconds — too short and flaky, or too long and slow. Instead I observe the DOM for the actual rendered content and treat its appearance as the ready signal.

PKCE — explain the flow and why you need it

Proof Key for Code Exchange. The plain authorisation code flow assumes the client can hold a secret, which a browser app or desktop client can’t. The client generates a random verifier, hashes it, sends the hash as a challenge with the authorisation request. When it exchanges the returned code for tokens, it sends the original verifier, and the server checks it against the stored challenge. An attacker who intercepts the authorisation code can’t use it — no verifier, and can’t derive it from the hash.

What does non-blocking chat mean?

The original send flow waited on everything before the UI moved — write to the database, invalidate the cache, wait for the refetch, then render. Several round trips before the user sees their own message. I restructured it so the message goes into the local cache optimistically and renders immediately, the database write goes out async, the model call fires in parallel, and the response streams back token by token.

What happens when the write fails after you’ve already shown it?

That’s the cost of optimism, and you handle it explicitly rather than hope. Retry, and on final failure surface an error toast and mark the message unsent. The rare bad case is worth the common good case here — chat’s the primary interaction, and latency in it is the whole feel of the product.

You also fixed double submission — same area?

Same input, different problem. Two fast Enter presses sent the message twice — a straightforward race. A submitting flag disables the input on first submit, clears when the response starts streaming, plus a debounce guard.


Fiddle (intern)

Walk me through the react-flow to tldraw migration

We moved the canvas off react-flow onto tldraw. react-flow models everything as nodes and edges — good for flowcharts — but fiddle needed freeform drawing and custom component shapes, which meant building a new shape system rather than styling the old one.

WebContainers to e2b — why?

WebContainers run the whole sandbox in the browser, elegant until it isn’t. We hit memory limits, package installation failures, browser compatibility problems — all landing on the user as “the preview is broken,” nothing we could do. e2b runs real Linux sandboxes in the cloud, so installs behave like installs. Cost is network latency and a two-hour session limit; I built custom templates and session management around that.

Isn’t moving compute to the cloud a step backwards for a design tool?

For latency, yes. For reliability, no — and reliability was the thing failing. If I were optimising now I’d look at a warm sandbox pool so the first preview doesn’t pay cold start, but that’s a cost decision as much as an engineering one.

What’s the “make real” flow?

Draw a rough component on the canvas, get generated code back. The drawing becomes an attachment on the chat message, so the model gets the image alongside the text prompt.

How do you handle a bad generation?

Keep it cheap to retry, never destroy the drawing. Generation’s additive, not a replacement of the user’s input — the difference between a tool you experiment with and one you’re afraid of.


Skills block

What’s new in React 19 that you’re actually using?

Actions and the form integration around them, the use hook for reading promises and context during render, ref as an ordinary prop so forwardRef stops being boilerplate. The compiler’s the bigger shift — removes most manual memoisation, though I wouldn’t claim to have tuned it in production.

Zustand over Context or Redux — defend it.

Context isn’t a state manager, it’s a dependency injection mechanism — using it for frequently-changing state re-renders every consumer regardless of what they read. Zustand gives selector-level subscriptions, so a component reading one field re-renders only when that field changes. Compared to Redux it’s far less ceremony for the same unidirectional model. Tradeoff’s less structure, so a large store needs discipline — in StashBase I split the store into typed slices and moved feature logic out of components into the slices that own it.

shadcn/ui isn’t a library — why’s it on your list?

Correct, that’s the point. It’s a distribution model — components copied into your repo as source rather than installed as a dependency, so you own and modify them. I’ve built on it and trove/cn ships in the same registry format.

What’s a TypeScript feature you use that most people don’t?

Discriminated unions to make invalid states impossible. In the agent contract, a session is a union over its states rather than an object with optional fields — you can’t hold a reference to a session that’s both starting and streaming.

PostgreSQL — tell me about a schema you designed.

The storyboard model at SamurAI. A Scene table sits between project and frames, everything downstream keys off scene, old projects resolve to a default scene rather than being migrated destructively. Final storyboards stopped being stored and became an aggregation across scenes, because storing them meant two sources of truth that could disagree.

Describe a CI pipeline you built.

The StashBase one — Electron, cross-platform. Required and manual checks: typecheck, lint, unit, and a Playwright suite split into smoke, functional, visual. Visual baselines committed per platform, refreshed manually. Release packaging and development both on Node 24, plus an initial-JS budget so bundle growth fails the build instead of getting noticed a year later.

Why Bun?

Install and script speed, runs TypeScript directly. Risk’s compatibility gaps against a Node-shaped ecosystem — CI at StashBase validates on Node rather than assuming parity.


trove/cn

What problem does it solve?

You see a nice interaction on a real product and can’t read the code behind it. A component library hands you a package with a design system attached you didn’t ask for. trove/cn rebuilds those patterns from scratch and ships them as source in the shadcn registry format — one add command copies the files into your project. Live preview next to the implementation, no runtime dependency.

How’s that different from publishing an npm package?

Ownership. A package is a black box you configure through props — need behaviour the author didn’t anticipate, you’re stuck. Copied source is yours to edit. Cost is you don’t get updates, which is the right tradeoff for interface patterns specifically, since they’re things you want to customise rather than consume.

What’s hard about the registry format?

A JSON manifest describing each component — its files, dependencies, registry dependencies. Getting one add command to pull the correct files plus transitive dependencies was the fiddly part. I generate the registry JSON from component metadata rather than maintaining it by hand.

Dozens of animated previews on one page — does that not crawl?

That was the main performance problem. Every preview’s a live component with real motion, so naively mounting them all means dozens of animations running for content nobody’s looking at. Don’t run what’s off screen, keep each preview isolated.


Education

You studied Mechanical Engineering — how’d you end up here?

Got into programming wanting to know how things worked underneath, and that pulled me into building interactive surfaces — canvases, editors, agent tooling. NIT Durgapur gave me the engineering fundamentals; the software’s self-taught, tested against production work since 2025. I didn’t learn one framework and stop — I built at different levels of the stack, and that shows in the range on the page.

Do you feel gaps from not having a CS degree?

Specific ones. No formal algorithms course, so a genuinely algorithmic problem takes me longer to recognise. What I have instead is time spent below the framework layer — renderer architecture, agent protocol contracts, not just React.


Behavioral


Traps


Numbers, if asked cold

ItemDetail
Storybook repos4
Model providers4 — Runway, Veo, Seedance, Kling
StashBaseopen source, commits public under PPRAMANIK62
shadcn-ui primitives56
tldraw migration~84 files, May–Aug 2025
SamurAI4 codebases, Apr–Jul 2026
fiddle contractor5 repositories, Dec 2025–Apr 2026
e2b sandbox lifetime2 hours, silent expiry

Openers that get the ramp going: lead with the tldraw migration or the hydration race — both concrete, both non-obvious.

Closers, if asked for questions: ask about the code, not the company. What’s a first task look like. How much of the frontend is component work versus system work. What’s the thing in the codebase everyone’s quietly afraid of.