DEV Community

Tirta
Tirta

Posted on

The Client Said “A Simple Website.” What Did They Actually Mean?

Hacktoberfest Open-Source AI Challenge Week 2: Side Hustle Submission 💸

This is a submission for the Hacktoberfest Open-Source AI Challenge Week 2: Side Hustle

What I Built

“A simple website. Four pages. Maybe bookings too.”

Those last three words can change a quote completely.

A link to an existing booking service is one task. A reservation system with payments, cancellation rules, and availability is another project. If the proposal treats them as the same thing, the freelancer inherits the ambiguity.

I built ScopeNote to help turn that conversation into something both sides can review: deliverables, estimated hours, unanswered questions, and work that is explicitly outside the scope.

It is for independent designers, developers, and small studios preparing client proposals. You can start with a written brief or upload a recording of a client briefing, with their permission.

Here is the document editor:

ScopeNote's actual scope editor showing editable deliverables and an estimate for a fictional studio website

The hours, descriptions, rate, and contingency are editable. The output is a working draft, not a client agreement.

Who would pay for it?

A freelancer handling confidential client work has a reason to pay for private saved projects. ScopeNote Premium is US$1 once for 30 days, with no subscription or automatic renewal. It includes private drafts by default, optional public sharing, and 10 voice imports per access period, up to 12 MB each.

Free includes one voice import per month, but newly saved Free client names, briefs, scopes, and estimates are public. Both plans include PDF and Markdown export, five saved projects, and three AI scopes per quota period. Free quotas use a calendar month; Premium quotas follow the 30-day access period.

That is the business hypothesis, not proof of demand. I have exercised the paid flow using my own account; I am not counting that as an independent paying customer.

Demo

Open ScopeNote · Browse public drafts without signing in

Watch the narrated walkthrough:

The screenshots and walkthrough use the real frontend with a fictional Tanah Studio brief and a hand-authored example scope. This is an interface walkthrough, not a timed Gemma run, customer session, or claim about model accuracy. The PDF shown was exported through the app.

1. Start with what the client actually said

Paste the message, meeting notes, or transcript into the brief editor. The example asks for four pages, mobile support, and an uncertain booking feature.

The client brief editor with a fictional ceramics studio message and an existing audio recording upload control

If the conversation is in a recording, upload it through the voice import control. ElevenLabs Scribe transcribes it into text. You can review that text before creating a scope.

The app imports recordings; it does not record meetings directly.

2. Review the proposed work

The working scope keeps the overview and deliverables beside the estimate. Change a title, rewrite a detail, or adjust the hours. You decide the hourly rate and contingency.

In this illustrative document, 42 hours at US$65/hour plus 15% contingency is US$3,139.50, displayed as US$3,140. That number comes from arithmetic, not a model's guess at the final price. The hours are still estimates.

3. Resolve what is missing

The most useful part of a scope can be a question:

Which booking service will the studio use, and is an external link enough for launch?

That question belongs beside an exclusion such as “Custom booking or payment system.” Together, they make the unfinished decision visible.

Questions about booking, content and approval, alongside exclusions for a custom booking system and other unquoted work

The screenshot is an editorial example. In the AI path, the prompt asks Gemma to put missing requirements into questions rather than inventing commitments. Whether it asks the right questions still needs human review.

4. Export the document you reviewed

The proposal page combines the scope, estimate, open questions, and exclusions. Both plans can download PDF or Markdown.

Proposal preview with PDF and Markdown export controls

The A4 PDF includes the project, client, scope, estimate, and “Made with ScopeNote” credit:

First page of the actual A4 PDF exported from the fictional studio example

To test it yourself, create an account and verify your email. Registration automatically sends the verification email; the in-app verification action can request another. Use fictional or non-confidential material on Free because saving publishes it.

Code

ScopeNote

A scope-of-work app for independent designers and developers. Bring a client brief or voice note, generate a draft with Gemma through Mastra, edit the document and hours, save the project, and export a PDF or Markdown proposal.

Status: account migration: email/password accounts, public Free drafts/scopes, server-side usage limits, password reset and email verification. There is no demo or simulated entry point. Premium is prepaid: US$1 once for 30 days through AllScale, with no subscription or automatic renewal. Checkout requires server configuration. Resend sender domain is verified; verification and reset email delivery plus old-session revocation passed against the production API. See CONFIGURATION.md.

Run locally

Node.js 22.13+ is required. From this directory:

npm install
npm run dev
Enter fullscreen mode Exit fullscreen mode

This starts Vite on http://127.0.0.1:5178 and the Express backend on port 3001. Vite forwards /api to the backend. Copy .env.example to .env when ready to configure credentials. Real accounts require MongoDB. Public drafts and…

Repository and setup · MIT license · Media provenance

The repository contains the frontend, Express API, AI workflow, account and billing logic, tests, and Render configuration. Credentials are supplied on the server.

How I Built It

The frontend is React + Vite. Express on Node.js serves the built application and its API. The AI portion is a Gemma agent inside a two-step Mastra workflow.

ScopeNote architecture: text or ElevenLabs transcription enters Express, Mastra invokes Gemma, Zod validates, and the human reviews before saving or exporting

The two workflow steps are small enough to explain:

  1. extract-scope sends the brief to Gemma and requests a JSON document.
  2. validate-scope parses the result and validates its structure with Zod.

The configured model is gemma-4-26b-a4b-it, accessed through the Google Generative AI adapter. Inference runs at the model provider; the Render instance does not load Gemma's weights.

The contract requires an overview, deliverables with bounded estimated hours, clarification questions, and exclusions. The brief is treated as untrusted data. The prompt asks for missing information to become a question and avoids promising delivery dates or client approval.

A response that fails parsing or validation produces an error. There is no fallback that quietly substitutes an example scope.

What the model does not calculate

The model proposes hours. Application code calculates the estimate:

export function estimate(items, rate, buffer) {
  const hours = items.reduce((sum, item) => sum + Number(item.hours), 0);
  return {
    hours,
    total: Math.round(hours * rate * (1 + buffer / 100) * 100) / 100
  };
}
Enter fullscreen mode Exit fullscreen mode

The scope schema can reject a negative number or a malformed deliverable. It cannot establish whether 18 hours is realistic for a particular freelancer. That distinction is why the editor is central to the product.

Michael Maurice's “Schema-Valid Isn't Correct: 4 Checks Before You Trust LLM Extraction” discusses the same gap between structured output and trustworthy data. ScopeNote's answer is validation plus an explicit human review step, not a claim that JSON makes an estimate correct.

Account boundaries are part of the product

Atlas stores account-owned projects, sessions, usage counters, and up to ten previous revision snapshots. The backend derives ownership and plan access from the session. It does not trust a plan or owner sent by the browser.

A saved revision number prevents two tabs from silently overwriting each other's changes.

Public and private access are separate backend paths:

Free newly saved drafts are public; Premium defaults private with optional public sharing; email, owner identifiers and revision history are omitted from public responses

Premium visibility changes take effect on save. Existing private projects remain private after Premium expires. Public pages show a masked author label, such as A***r, rather than the account email.

Payment is verified before access changes

AllScale handles the one-time Premium checkout using supported USDT/USDC crypto networks.

The backend checks the provider's authoritative payment status and matches the amount, order, and user before updating Premium access. Returning from checkout alone does not unlock anything.

AllScale payment is verified server-side before granting access; a MongoDB receipt outbox delivers a receipt through Resend

Granting access and recording that the order has already been applied happen in one atomic user update. If a later ledger write fails, a retry can repair the ledger without adding another 30 days.

Resend sends the payment receipt. A MongoDB outbox preserves the message and its order-specific idempotency key so retries do not casually create duplicate emails. Ambiguous retries stop before the provider's idempotency window expires and need review.

What I checked

The latest application test run passed 62 tests. They cover account isolation, reset-token expiry and session revocation, quotas, payment reconciliation, receipt retries, public/private access, masked authors, scope validation, estimate arithmetic, and PDF pagination.

The PDF is generated in the browser with pdf-lib, fontkit, and embedded Lato fonts. Exported content is not sent to a separate PDF service.

These checks test application behavior. They do not measure scope quality, time saved by freelancers, or production uptime.

Why Does Open Innovation Matter?

ScopeNote's core transformation uses Gemma, an open-weight model, and Mastra, an open-source agent framework. The application is MIT licensed.

For this project, openness gives me three practical things.

I can inspect the planning workflow. The prompt, output contract, validation, and pricing arithmetic are visible in the repository. Someone can see where the model's suggestion ends and ordinary application logic begins.

I can adapt the planner to a niche. A freelancer building booking sites may need different questions from someone preparing an illustration commission. The workflow and schema can change with that domain.

The model's weights provide a future serving option. I can investigate self-hosted Gemma under its license rather than depending permanently on one hosted endpoint. That option would not exist for a model available only through a closed API.

I have not fine-tuned Gemma or built local inference into ScopeNote. I also have not demonstrated that this arrangement is cheaper than a closed-model alternative.

The hosted app sends text to Google, audio to ElevenLabs, and saved projects to Atlas. Open source makes the implementation inspectable; it does not make those requests local. The distinction matters when the documents contain client information.

Prize Categories

I am entering six categories. Here is the job each integration performs, the implementation that supports it, and what I can claim from it.

Best Use of Render

Render runs the frontend and Express API in one web service. Its Blueprint builds the Vite app, starts Node, and uses /api/health as the health check.

This deployment exposed a practical problem: on Free compute, the first visitor after an idle period could see Render's waking-up page. A scheduled GitHub health ping was too irregular to make that reliable. I moved the service to one paid Starter compute instance (0.5 CPU, 512 MB RAM) while keeping the workspace on Hobby, then removed the keep-alive schedule.

This fixes idle spindown, not every possible outage. Gemma inference still happens at the provider.

Inspect: Render Blueprint · Live application

Best Use of Gemma

Gemma performs the task the app is built around: translating an unstructured brief into a structured scope.

The prompt and schema ask for deliverables, estimated hours, questions, and exclusions. The goal is to expose uncertainty early. An incomplete booking requirement should lead to a clarification question rather than a confident promise to build a payment system.

I use hosted Gemma inference with the model ID set explicitly. There is no silent substitution of Gemini or another model family.

The screenshots above illustrate how the editor handles a scope; they are not evaluation results. I have not established an accuracy rate or a baseline comparison.

Inspect: Agent prompt and model invocation · Scope contract and estimate code

Best Use of Mastra

Mastra owns the brief-to-scope workflow, rather than being an unused dependency.

The extraction step invokes the agent. The validation step checks its result. A failed run becomes an explicit API error, and the application returns the run ID, model, and measured duration on success.

This is a bounded workflow with one model request. It does not need an autonomous research loop to turn a client's paragraph into a draft document.

The automated workflow test uses a stubbed model response to check the wiring and validation path. That is a workflow test, not proof that a remote model understood a real brief.

Inspect: Two-step workflow · Backend tests

Best Use of MongoDB Atlas

Atlas is the application's persistent data layer for accounts, project documents, revision snapshots, quotas, and billing state.

The important behavior is account isolation. Listing, saving, deleting, and reading history use the authenticated account's identity. Public endpoints return a deliberate subset of fields, and a private project cannot be read through its former public link after the visibility change is saved.

Atlas also supports the payment grant's atomic update and the receipt outbox. That makes it part of both the document workflow and the paid product.

This is ordinary document storage and access control; I am not claiming Vector Search or RAG.

Inspect: Project storage and visibility rules · Account isolation tests · Billing reconciliation

Best Use of Sentry Agent Tracing

A failed scope request can fail during orchestration, the model request, or output validation. Sentry spans separate those operations.

The implementation traces:

Operation What it follows
gen_ai.invoke_agent The complete brief-to-scope run
gen_ai.chat The Gemma request, with model identity and token counts when returned
workflow.step Parsing and validating the generated scope
gen_ai.execute_tool ElevenLabs transcription

The agent span wraps the Mastra run:

return traced('gen_ai.invoke_agent', 'ScopeNote / brief-to-scope', async (span) => {
  span?.setAttribute('gen_ai.operation.name', 'invoke_agent');
  span?.setAttribute('gen_ai.agent.name', 'scope-planner');
  span?.setAttribute('gen_ai.request.model', config.model);
  // The Mastra workflow is started within this span.
});
Enter fullscreen mode Exit fullscreen mode

Excerpt from the instrumentation; the linked source contains the complete run.

Telemetry deliberately omits raw prompts and responses, disables default PII collection, and sanitizes error events. The code does not invent an inference cost when the provider supplies only token usage.

I am showing the instrumentation, not a fabricated Sentry dashboard. I have not published a trace-based performance comparison or a measured cost improvement.

Inspect: Telemetry configuration · Agent spans · Transcription span

Best Use of ElevenLabs

ElevenLabs is used inside the product, not only to narrate its promotional video.

Scribe turns an uploaded client recording into editable brief text. The user reviews the transcript, then sends that text through the same Gemma/Mastra path as a written brief.

The backend checks file format, enforces the 12 MB upload limit, and rejects an empty transcript or a transcript exceeding the brief limit. Audio stays in request memory and is not stored by ScopeNote. Provider policies still apply.

Free includes one voice import per month; Premium includes ten per 30-day access period. A failed provider call produces an error instead of sample transcription text.

Inspect: Transcription endpoint and validation · Per-account usage enforcement


Built and written with AI assistance under my direction. The point of ScopeNote is to make the unresolved parts of a brief easier to discuss before they become commitments.

Top comments (0)