Skip to content
All builds
Operations

SignetEstimate to Proposal v2

What it does for you

A rough estimate becomes a proposal clients can sign.

Paste a rough estimate and get a designed, signable proposal with packages, a gantt timeline, a payment schedule, and a private margin view.

Signet screenshot

Who it's for

Anyone who prices work at a site visit and then has to send a proper proposal. The proposal covers scope, packages, a timeline, a payment schedule, and a place to sign.

Screenshots

Signet screenshot 1
Signet screenshot 2

In more detail

Signet takes the rough numbers from a site visit and turns them into a proposal a client can read, adjust, and sign. You paste messy estimate text, it pulls out the line items, phases, tax, and deposit, and you get a multi-page document with a scope, a pricing table with packages, a timeline, a payment schedule, and a signature block. This was one of the featured builds, the one the project list calls my hero demo, refined.

Honest limits

Claude mode was never called against the live API during the build. The print-to-PDF dialogs and logo upload were not tested by hand in a real browser session. It runs, it passes its checks, and it's not a hammered production tool.

How I built it

This one came together in two sessions. An earlier Claude Code session wrote the core, proposal.js, which ended up around 103 KB, and then the connection died before any UI existed. On September 23, while Codex worked its own list in the same repo, Claude Code picked it back up with the tiny-writes brief: no file over about 4 KB, one tool call per step, grow bigger files by replacing a marker comment.

In that run we wrote 173 assertions across small test modules, then the whole UI: 11 modules, three stylesheets, a signature canvas, and an iframe smoke test that clicks through the real app (19 of 19 passing). I set the product rules: the local engine comes first, the margin view never prints, no em dashes, fictional clients only, and a featured-quality bar. Claude made the layout and wiring calls inside those rules, then checked every view with headless Chrome screenshots and fixed what it saw, like cramped unit fields and a cost input that ignored its width because of a selector clash.

What I learned

  • Rendering the proposal once, as one HTML string from pure code, means the preview, the print path, and the export can't drift. It also let me turn "margin never prints" and "the export has no cost data" into real checks in verify.mjs, not promises.
  • Live editing without a framework works if typing only updates the preview and a few data-live regions, and structural changes re-render the whole view. That keeps your cursor and the preview scroll where they were.
  • Money stays in integer cents, and the last payment row absorbs the rounding, so the schedule always matches the total.
  • A shell sed on an em dash escape quietly ate the backslash and turned three passing checks into false failures. For edits full of escapes, a short Node script is safer.
  • The API kept dropping long responses mid-stream. The fix was process, not code, and small modules turned out to be better design anyway.

What carried forward

The pattern here is the one from my first eleven projects: a pure core imported by both the page and the tests, plus a tiny serve.mjs on its own port (5141). The optional Claude rewrite follows the same shape as my earlier AI tools: a local engine that fully works, and a Claude layer whose JSON gets sanitized before it touches anything. The tiny-writes approach that finished this build is the same one that finished most of 41 to 50.

If I took it further

I'd store proposals in Supabase with a share link, so "sent" and "accepted" happen on the client's side instead of being recorded by hand. Then I'd test Claude mode with a real key and a small batch of proposals.

#041 Built with Claude Code September 23, 2026 173 automated checks

Want one like this?