# Undersign — Build Log A timestamped, prompt-by-prompt record of building **Undersign**, a standalone e-sign proof-of-concept, in a single day with an AI agent (Claude Code) under human direction. > **On the name:** built under the working name **Signet**, renamed to **SreeSign** at > entry #4, then to **Undersign** at entry #7. Earlier entries below keep the name that was > true at the time (and prompts are verbatim) — that naming arc is part of the record. This is intentionally kept as a primary artifact — to show *how* it was built, not just what shipped. It captures, for each step: **the time**, **the exact human prompt**, and **the artifacts that came out of it** (files, commits, decisions, screenshots). > _Built by human voice · refined by prompts · made with AI agents · human reviewed · then shared._ **Conventions** - Times shown as **IST** (the working day) with **UTC** in parentheses (container clock = UTC). - Prompts are quoted verbatim (lightly trimmed only where noted with […]). - "Artifacts" = anything produced after the prompt: files, commits, decisions, plans, assets. - Newest entries are appended at the bottom (read top-to-bottom as a story). --- ## Session 1 — 2026-06-05 (IST) / 2026-06-04 (UTC) > Back-filled entries below (#1–#3) are reconstructed from the start of the session; > their clock times are approximate. Timestamps are precise from entry #4 onward. ### #1 — ~04:5x IST (~23:2x UTC) · Human prompt — the brief > "Am actually going to meet the Signeasy team. They are the Signeasy.com and iPhone > app. They are award-winning. I have never built an app. But I did built a team that > built apps. But I am very familiar with Claude code. What does it take to build a clone > of Signeasy? […] Can you propose? Remember they are award-winning, so every pixel > matters. Can we, as a proof of concept, build a standalone e-sign app? Plan first." **Artifacts** - Researched repo conventions (static, no-build, token-driven CSS, vanilla ES modules; per-app folder pattern like `antara/`, `teardown/`). - Produced a **proposal + plan** for "Signet": a 100% client-side e-sign PoC (pdf.js + pdf-lib + WebCrypto), original brand in the house design system, 6 build phases. - Surfaced the honest "what's real vs. faked" map (document engine + signing real; cross-device workflow simulated; no legal CA / identity verification). ### #2 — ~04:5x IST (~23:2x UTC) · Human decisions (clarifying questions answered) Decisions locked via 3 questions: - **Scope:** _Full mock incl. "send to others"_ (simulated, in-browser, no real email). - **Brand:** _Original brand_ in the house design system (not a Signeasy look-alike). - **Visibility:** _Unlisted PoC_ — `noindex,nofollow`, **not** linked from the hub. **Artifacts** - Finalized scope. Working product name **Signet** (signet ring = sealing documents); folder `signet/`. Branch: `claude/signeasy-clone-poc-CiILP`. ### #3 — ~04:5x IST (~23:2x UTC) · Human prompt — keep a build log > "Do want to keep a release log. Since it all happens with in a day time is imprtant to > log as well. Remember the prompts as well. Any artefacts that came after my prompt also > remember. Which I would love to show later to the [team]." **Artifacts** - Created `signet/` folder. - Created **this file** (`signet/BUILDLOG.md`) and back-filled entries #1–#3. - Build-log discipline added to the plan: every prompt from here gets a timestamped entry with its resulting artifacts; every phase ship gets a release-log entry below. --- ### #4 — 05:14 IST (23:44 UTC) · Human prompt — name it, build it all, while asleep > "1. sreesign 2. phase 0-6 please. commit per phase and push […] am going to sleep now > for next 2 hours, so you are empowered, think like a senior designer or senior engineer > or senior product owner or business owner. keep a log as request" **Decisions taken (empowered, senior calls — rationale logged so they're defensible):** - **Name:** SreeSign. Folder `signet/` kept (provisional; rename is trivial since unlisted). - **Libraries:** CDN egress is blocked from the build container, but **npm registry + GitHub are allowlisted** — so I pulled `pdfjs-dist@4.10.38` and `pdf-lib@1.17.1` from npm tarballs and **vendored them locally**. This makes the "nothing leaves the device" claim literally true and the demo work offline. _Senior-engineer call: own the dependency._ - **Samples:** generated two realistic PDFs (Mutual NDA, Service Engagement Letter) with pdf-lib so the demo works with zero setup. Generator kept in-repo (`samples/_generate.mjs`). - **Verification:** no headless browser in the sandbox (Chromium host blocked). Strategy = rigorous code review + **node tests of the highest-risk logic** (pdf-lib flatten + coordinate mapping, which run natively in node). Browser click-through is left for the human to confirm on wake — noted, not hidden. **Artifacts (Phase 0 — Scaffold + brand):** - `index.html` (shell, `noindex,nofollow`, theme-color, vendored pdf-lib), `styles.css` (full token-driven design system — light + dark, editorial-app type pairing), `app.js` (DOM helper, icons, toast, hash router, dashboard: hero + dropzone + sample loader + document library), `state.js` (IndexedDB: docs/sigs/meta). - `prepare.js` / `finalize.js` route stubs (filled in later phases). - `vendor/` (pdf.min.mjs, pdf.worker.min.mjs, pdf-lib.min.js — sourcemap refs stripped). - `samples/mutual-nda.pdf`, `samples/service-agreement.pdf`, `_generate.mjs`. - `CLAUDE.md` per-app memory. - Verified: all assets serve 200; JS parses; pdf-lib loads in node. **Not linked from the hub.** --- ## Release log (phase ships) | Version | When (IST) | Phase | What shipped | Commit | |---|---|---|---|---| | v0.1 | 05:15 | Phase 0 | Scaffold, brand/design system, IndexedDB, vendored libs, sample PDFs | 062cbce | | v0.2 | 05:16 | Phase 1 | Import & render PDF (pdf.js) — `viewer.js` page column + per-page overlay; editor shell (bar/stage/side) | fafa960 | | v0.3 | 05:19 | Phase 2 | Field editor: palette (signature/initials/date/text/checkbox), click-to-place, drag-move, corner-resize, delete, keyboard del/esc, recipient assignment; positions stored page-relative (%) | fa52b2a | | v0.4 | 05:21 | Phase 3 | Signature pad (`sign.js`): Draw (smoothed canvas), Type (4 fonts), Upload (image); ink colours; transparent PNG output; reusable saved signatures (IndexedDB). Modal UI. | 37383ee | | v0.5 | 05:24 | Phase 4 | Recipients (add/remove, colours, field counts) + **simulated send** (status→sent, audit logged, no email) + **signing hub**: role-switch between signers, tap fields to sign/date/fill/check, per-signer progress, "all parties signed" → finalize. | 97514d5 | | v0.6 | 05:28 | Phase 5 | **Finalize** (`finalize.js`): pdf-lib flattens signatures/dates/text/checkboxes into the page (page-% → PDF points, y-flip), SHA-256 of original + signed content (WebCrypto), appended **audit-certificate page** (parties, trail, hashes), Blob download. **Node-tested** the flatten (7/7 checks). | 9eca96c | **Verification artifact (Phase 5):** built a real signed sample in Node using the actual `buildSignedPdf` — `SreeSign-NDA-signed-sample.pdf` (2 pages: signed NDA + certificate). Node test asserts: valid PDF, +1 cert page, both hashes 64-hex, `hashOriginal` matches `sha256(original)`, signed≠original. _(Browser click-through still pending human verify.)_ | v1.0 | 05:31 | Phase 6 | Polish: SVG favicon, OG tags, **delete-document** on cards (demo reset), keyboard-activatable cards, hover/focus states, reduced-motion guards, **cache bump v=1→v=2** across all assets. Final syntax + flatten test + serve all green. | 132f0b3 | | v1.1 | 05:35 | Rename | Renamed **SreeSign → Undersign**; folder `signet/ → undersign/` (git mv); updated UI, meta, favicon wordmark, certificate text, IndexedDB name, CLAUDE.md; cache bump v=2→v=3; samples regenerated; making-of viz + hub listing. | 0b34dd7 | --- ## Status at end of autonomous session (while human asleep) **All 6 phases shipped, each its own commit + push to `claude/signeasy-clone-poc-CiILP`.** The PoC is a complete on-device e-sign flow: import → place fields → sign (draw/type/upload) → role-switch through signers → finalize (flatten + SHA-256 + audit certificate) → download. **Verified by me:** all JS parses; flatten logic node-tested 7/7; all assets serve 200; a real signed 2-page sample PDF generated from the production code. **Left for human verify on wake (no sandbox browser):** click-through in a real browser — load `/signet/`, run a sample end-to-end, eyeball light/dark + a phone width, confirm zero console errors. (Open `python3 -m http.server` from repo root, visit `/signet/`.) **Honest gaps to mention in the meeting:** cross-device send is simulated (no server); page rotation not handled in flatten; not a regulated eIDAS/ESIGN certificate; no identity/KYC. --- ## Session 2 — 2026-06-05 (IST) ### #7 — 05:35 IST (00:05 UTC) · Human prompt — a better name > "come up with a better name for this app?" → (chose from 4 options) **"Undersign"** **Artifacts** - Proposed 4 names with rationale (Signet / Paraph / Inkwell / Undersign). Human picked **Undersign** ("to undersign" = sign at the bottom; clean, action-forward, product-grade). - Renamed across the app: page title + meta + OG, brand wordmark, signature certificate text ("Undersign · generated on this device"), IndexedDB name (`sreesign`→`undersign`), `CLAUDE.md`. Folder `signet/ → undersign/` via `git mv`. Cache bumped v=2→v=3. Samples regenerated. Release row **v1.1** added above. - Naming arc for the record: **Signet → SreeSign → Undersign**. ### #8 — 05:4x IST (00:1x UTC) · Human prompts — URL, a viz, and list it > "what the URL" · "based on buildlog.md … can we use that data and create a viz to > showcase what happened behind the scene?" · "add to listing page / it was unlisted but > its okay" **Artifacts** - **URL:** the app lives at **`https://apps.sreeb.dev/undersign/`** once this branch merges to the deployed site (locally: `python3 -m http.server` from the repo root → `/undersign/`). - **Visualization:** `making-of.html` — a self-contained, on-brand page that turns this log into a visual story: headline stats, a 16-minute phase **gantt**, a colour-coded **timeline** (human prompts vs build phases vs rename), the naming arc, and the honest "real vs staged" note. Linked from the app and the hub. - **Hub listing:** added an `undersign` bench-card to the root `index.html` (first item). Reversed the earlier "unlisted" call per request; **kept `noindex`** so it stays search-invisible (it's a competitor clone) — flip on request. - **Timing correction:** replaced earlier estimated IST times with **real commit timestamps** (git log). The true record: **Phases 0–6 in 16 minutes (05:15 → 05:31 IST)**; full arc incl. build-log + rename ≈ 38 minutes. ### #9 — 07:1x IST (01:4x UTC) · Human bug report — live site error > (screenshot) "Something went wrong: Failed to fetch dynamically imported module: > https://apps.sreeb.dev/undersign/prepare.js?v=3" **Diagnosis** - The dashboard loaded (so `app.js` served fine on prod). `prepare.js` is a sibling `.js` in the same deploy, so it can't be the real failure — the only thing unique to its dependency chain is the two **`.mjs`** vendor files (pdf.js, pulled via `viewer.js`). The production host serves `.mjs` with a non-JS MIME type (or 404s it); **strict module MIME checking** rejects it, and the browser reports the failure on the top-level dynamic `import()`. - Local Python serves `.mjs` as `text/javascript`, which is why a headless check never caught it — a real-host MIME quirk. **Fix (v1.2)** - Renamed vendored modules **`.mjs → .js`** (`pdf.min.js`, `pdf.worker.min.js`) — host-agnostic; `.js` is proven to serve correctly. Updated the `viewer.js` import + `workerSrc`, CLAUDE.md, cache bump v=3→v=4. Verified all modules + vendor files serve `text/javascript`. | v1.2 | 07:1x | Hotfix | Live `.mjs` MIME failure → renamed vendored pdf.js modules to `.js`; updated import + workerSrc; cache v=3→v=4 | _this commit_ | ### #10 — 07:2x IST · Human prompt — show the bug-fix in the making-of > "making-of.html should be updated included how we fix the bug / sree upload the error > and claude fixing it" **Artifacts** - Added colour-coded **Live bug** + **Fix** event types to `making-of.html` and put the `.mjs` MIME hotfix on the timeline (Sree's screenshot report → diagnosis → PR #23). ### #11 — 07:3x IST · Human bug report — signature missing from the download > (screenshot, finished doc) "the pdf i downloaded it dont have the esign attached — is > that normal?" + "signet?" (stale name spotted in a test path) **Diagnosis** - The basic flatten embeds the image correctly (verified: page-1 `/Image` XObject present), so simple PDFs sign fine. The failing document (a real school election notice) had a **rotated page** — the signature was flattened into unrotated coordinates and landed **off the visible area**. The known "rotation not handled" gap, hit by real input. **Fix (v1.4)** - Rewrote the flatten coordinate mapping to be **rotation- and crop-box-aware**: derived the transform, **unit-tested all four rotations** (0/90/180/270) + shifted crop-boxes. Applies to signatures, dates/text and checkboxes. - Added a **“Re-generate”** button on the done screen so an already-completed document can be re-exported with the new engine (no re-signing needed). - Cleaned the last stray “signet” path reference (the original working name) in `samples/_generate.mjs`. (`making-of` + `BUILDLOG` keep “Signet” intentionally — naming arc.) - Cache bump v=4→v=5. | v1.3 | 07:2x | Viz | `making-of.html`: bug-fix episode added to the timeline (new Live-bug / Fix event types) | _this commit_ | | v1.4 | 07:4x | Hotfix | Rotation- & crop-box-aware flatten (signature/date/text/checkbox), unit-tested 0/90/180/270; "Re-generate" on done screen; cache v=5 | _this commit_ |