{"slug": "woolly-gemma-proposes-the-tasks-only-the-client-can-pay-for-them", "title": "Woolly: Gemma proposes the tasks. Only the client can pay for them.", "summary": "A developer built Woolly, an open-source client-freelancer budget board in which a client sets a project budget, allocates it across cycles, and remains the sole approver of submitted work, with the server rejecting any attempt by a worker to approve their own task. A Mastra agent calls Google's open-weight Gemma model (gemma-4-26b-a4b-it) through the Gemini API to propose tasks from a pasted summary and to recommend credit or dispute on submitted proof, while the payment decision stays a separate client-only action. The board loads without a model call, and the demo runs on a Node API with a Flutter web client.", "body_md": "*This is a submission for the [Hacktoberfest Open-Source AI Challenge Week 2: Side Hustle](https://dev.to/challenges/hacktoberfest-week2-2026-10-12)*\n\nWoolly is a board for a client who pays a few freelancers against a project budget.\n\nThe client sets the budget, then puts an allocation on each cycle. Tasks on that project split the allocated money, and the shares always add up to 100%. A worker can edit a task and submit proof. Marking it complete requires a written summary of the proof, and that moves the task to **Under Review**. The client is the only person who can approve it (** Complete**) or send it back (** Revision Needed**). A worker who tries to approve their own work is refused by the server.\n\nThat is the thing someone would pay for. A client who already hires people by the piece wants the money to stay a percentage of a number they typed, with one approver. Woolly is not a marketplace and it does not send invoices. It is the agreement: budget, cycles, shares, proof, and a client who decides.\n\nIn the walkthrough, Field notes has an $800 allocation and four tasks. Each one sits at 25%, $200. Move one slider and the others give up the remainder so the total stays 100%.\n\nGemma does not touch that number. A client can paste a summary (\"add the landing page to Cycle 1\") and a Mastra agent asks Gemma to propose the tasks, on the cycle named in the summary. If that cycle is missing, the task comes back unplaced. The agent does not quietly pick a different cycle. A second agent can recommend credit or dispute on a proof. The recommendation is stored. The payment decision is a separate action, and only the client can take it.\n\nThe people in the demo are fictional: Cool Client, Worker One, and Worker Dos. There is no password.\n\nThe video is the client board, a task and its allocation, Manage (budgets and cycles), a new project, then the same app as Worker One. The worker has no project button and no Settings. The account menu is Sign out. Nothing in the recording is saved.\n\nTo run it locally, from a clone of the repo:\n\n```\ncd server && npm install && npm run migrate && npm run dev\ncd app && flutter run -d chrome\n```\n\nCopy `.env.example` to `.env` first. The API listens on `http://127.0.0.1:8787`, and the app is the Flutter web client. `npm run migrate` rebuilds the tables and the fictional rows. Sign in as **Cool Client** or **Worker One**.\n\nThe board loads without a model call. Gemma runs when someone asks for tasks from a summary, or for a recommendation on a proof.\n\nPay for an iteration, measured by the subtasks a client accepts.\n\nA project is the epic. An iteration is one sprint inside that project: it has an amount and an optional deadline. Tasks are the stories. Subtasks are the tickets. Pay for an iteration is its amount times the share of subtask effort the client has credited.\n\nA client has many workers. A worker has many projects. The client adds work and reviews it, filtered by worker or by project. The worker adds and edits tasks, and submits subtasks for review. The worker cannot credit their own work.\n\nThe people in the demo are fictional: Cool Client (client), Worker One (worker), and Worker Dos (worker).\n\nWalkthrough of the client board, task allocation, Manage, and the worker account: [https://youtu.be/IX1FJzK4vcU](https://youtu.be/IX1FJzK4vcU)\n\nDark brown, flat, closer to a Darcula editor than a paper form. Near-black background, warm brown panels, amber marks…\n\nThe open pieces are Gemma and Mastra. Gemma is `gemma-4-26b-a4b-it`, called through the Gemini API (` server/src/env.ts`). It is an open-weight model on a hosted endpoint. This build does not run the weights on the laptop, and I am not going to pretend it does. Proof text and summaries go to that API when an agent runs.\n\nMastra is the harness (`server/src/woolly.ts`):\n\n| Agent | Job | \n|---|---|\n| Place | Turn a summary into one task per piece of work, on the cycle the summary names | \n| Split | Break a brief into tasks and subtasks. Effort is the size, value is how much it matters | \n| Review | Read a proof against the description and recommend credit or dispute | \n\nPlace and split return JSON the server checks before anything is written. Review writes `model_outcome` on the proof. The client's decision is a different request. Shares are basis points on the task row, and the server normalizes them so a project sums to 10000. Cycle allocations that would pass the project budget are rejected. None of that is a sentence in the prompt.\n\n`search-past-work` is a Mastra tool the split and review agents call. It looks up this project's own accepted subtasks, with Postgres full text and a pgvector embedding, and hands a few titles back. The embedding call is Gemini's embedding model, not Gemma. The memory is the client's past work, not a general chat log.\n\n``` php\nflowchart LR\n  summary[Client summary] --> place[Mastra place agent]\n  place --> gemma[Gemma via the Gemini API]\n  gemma --> tasks[Tasks on the named cycle]\n  tasks --> board[Board and allocation]\n  proof[Worker proof] --> review[Mastra review agent]\n  review --> note[Recommendation only]\n  note --> client[Client approves or sends it back]\n  client --> paid[Share of the allocation]\n```\n\nStack around that: a Flutter web client, a TypeScript API, Postgres. The theme is a dark brown board. Projects, workers, cycle, and status are filters. The client and the worker are two workplaces in one app.\n\nA client will pay for this only if the money rule is boring and visible. An allocation is a number they set. A share is a percentage of that number. A worker cannot approve the proof. If those rules lived inside a closed chat product, the same vendor would propose the work and hold the ledger, and a prompt would be the only thing standing between a suggestion and a payment.\n\nOther people building agents next to money are drawing the same line. A spending limit written in a prompt is not a control ([Prompts don't enforce budgets. Signatures do.](https://dev.to/payload-tools/prompts-dont-enforce-budgets-signatures-do-2a1j)), and the approval has to sit outside the model ([Should this AI agent be allowed to pay?](https://dev.to/quinn_854b15f517d8632ed4f/should-this-ai-agent-be-allowed-to-pay-designing-the-approval-layer-between-agents-and-company-20k7)). Woolly keeps Gemma on the proposal. The budget check, the shares, and the approver check are ordinary server code.\n\nOpen is what makes the split possible. Mastra is the harness, so the three agents are a file I can read, and the model is a setting (`GEMINI_MODEL`, default `gemma-4-26b-a4b-it`) rather than the product. I can point Place at another Gemma endpoint without rewriting who is allowed to mark a task paid. The board also has a price of zero when nobody is asking the model anything. The calls a client would pay for are the ones that propose work or read a proof.\n\nWhat open does not do here: it does not keep the proof on the machine. Hosted Gemma means a summary leaves the laptop when an agent runs. Local weights would close that gap. They are not what this submission runs.\n\nFeatured\n\n`gemma-4-26b-a4b-it` through the Gemini API). Place turns a summary into tasks. Review recommends credit or dispute and does not get to apply it.\nPartner\n\n`Agent` s, and past work is a `createTool` the split and review agents call before they answer.\nSolo entry.", "url": "https://wpnews.pro/news/woolly-gemma-proposes-the-tasks-only-the-client-can-pay-for-them", "canonical_source": "https://dev.to/jedau/woolly-gemma-proposes-the-tasks-only-the-client-can-pay-for-them-1ip3", "published_at": "2026-10-18 03:02:44+00:00", "updated_at": "2026-10-18 03:09:56.913045+00:00", "lang": "en", "topics": ["ai-agents", "ai-tools", "large-language-models", "developer-tools"], "entities": ["Woolly", "Gemma", "Mastra", "Google", "Gemini API", "Flutter", "Hacktoberfest"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/woolly-gemma-proposes-the-tasks-only-the-client-can-pay-for-them", "markdown": "https://wpnews.pro/news/woolly-gemma-proposes-the-tasks-only-the-client-can-pay-for-them.md", "text": "https://wpnews.pro/news/woolly-gemma-proposes-the-tasks-only-the-client-can-pay-for-them.txt", "jsonld": "https://wpnews.pro/news/woolly-gemma-proposes-the-tasks-only-the-client-can-pay-for-them.jsonld"}}