This is a submission for the Hacktoberfest Open-Source AI Challenge Week 3: Slow and Steady
Every maintainer knows the morning dread. Forty notifications, and before writing a line of code you answer the same four questions in the same order: Is this a duplicate? Is it even a bug? Which version are you on? Can you give me a minimal reproduction?
Hammock is an overnight triage sloth. A durable Temporal workflow fetches new and updated issues idempotently, classifies them with a local Gemma 4 model, recalls duplicate reports with embeddings, asks Gemma to adjudicate whether two issues share the same technical root cause, and drafts the reply that asks for exactly what's missing. Then it stops.
It applies allowlisted, reversible labels on its own. Everything public : comments, closes, locks, assignments waits in a local queue for one maintainer tap.
Hammock can propose anything. It can only do the reversible things alone.
Labels on an allowlist: automatic.
Comments, closes, locks, and assignments: your tap.
| Action | Who decides |
|---|---|
bug ,needs-info ,feature ,duplicate ,question |
Automatic — additive, allowlisted in.hammock.yml |
| Comments, closes, locks, assignments | Your tap — always, with no config that disables it |
The person who gets to be a little lazier is a maintainer at 7am: instead of forty tabs, they get a short queue of drafts to approve, edit, or discard.
The person who gets to be a little lazier is a maintainer at 7am: instead of forty tabs, they get a short queue of drafts to approve, edit, or discard.
video
live demo
To run on Local Device
60-second reviewer path — no Ollama, no Temporal, no tokens:
git clone https://github.com/Lakshay-Deol/Hammock.git
cd Hammock
pnpm install && pnpm build
pnpm hammock eval-dedupe --replay # precision / recall vs ground truth
pnpm hammock test-fixtures --replay # 8 fixtures incl. 3 prompt injections
pnpm hammock dashboard # http://localhost:3000
Hammock triages your GitHub issues overnight on a local Gemma 4 model. It labels, spots duplicates and drafts replies — and touches nothing public without your tap.
Reversible alone. Visible never.
Submission for the Hacktoberfest Open-Source AI Challenge (Week 3: Slow and Steady).
Hammock can propose anything. It can only do the reversible things alone.
Labels on an allowlist: automatic Comments, closes, locks, and assignments: your tap.
Most AI triage bots call hosted models on public webhooks and post directly to repositories without supervision. Hammock runs 100% locally, overnight, durably via Temporal, and strictly refuses to speak in public without maintainer approval — proving every action with an immutable receipt.
GitHub REST API
│ since + ETag (conditional, idempotent)
▼
Temporal schedule "nightly-triage"
│
▼
workflow: fetch → per-issue activity fan-out → digest
│
├─► Mastra agent ──►
… Gemma 4 via Ollama does three genuinely different jobs, each shaped by a different failure mode:
type, severity, confidence, missing_info[], suggested_labels[], summary. The thinking channel (<|channel>thought … <channel|>) is stripped before parsing, so a rambling model can't corrupt the object. A verdict that fails validation is an error, not a guess.nomic-embed-text embeddings plus cosine similarity over SQLite recall the top-3 candidates; Gemma then answers the narrower question : Temporal carries the overnight pass: a deterministic workflow with per-issue activity fan out, defined timeouts, and a registered cron schedule, so a laptop asleep at 2am resumes instead of double labelling.
Mastra defines the triage agent and its four typed tools (classifyIssue, embedIssue, findDuplicates, draftReply) over the local Ollama model a provider-agnostic contract for the loop, while the deterministic activities call the same primitives directly so workflow replay stays pure.
SQLite (better-sqlite3) stores issues, verdicts, proposals, embeddings, and append-only receipts in WAL mode. Hono serves the cockpit: an editable draft per pending proposal, a live character-count diff indicator, approve/reject, and the receipt log.
Two design decisions I'd defend:
<untrusted_issue_data> and the model's only job is to emit JSON, which is validated before anything reads it. Adversarial fixtures come back injection_suspected: true and produce There is also a replay adapter a real ModelClient implementation that swaps the transport and reads recorded verdicts through the same Zod schema. Every code path after the model call is the production path. It exists so a judge with no GPU, no Ollama, and no tokens can still run the whole thing in one minute.
Because the alternative is shipping your bug reports to someone else's API.
Private issue trackers contain internal hostnames, database schemas, customer identifiers, and proprietary stack traces. Running Gemma locally via Ollama makes "triage 500 issues overnight" cost $0.00 in tokens and zero bytes of exfiltration. It also means the tool works on a plane, in an air-gapped environment, and after the maintainer stops paying someone's subscription.
Open weights made the trust model possible, not just the economics. Because I can run the model locally, and read the SDKs it is wrapped in, I can make a real promise about what never leaves the machine — and I can show the receipt for every decision, including the ones that never touched GitHub. A closed API could give me a better classifier; it could not give me that guarantee. And because everything is open, the interesting part — the approval gate, the sandbox assertion, the reversibility split — is auditable by anyone who disagrees with me.
Measured on the fixtures in the repo, all reproducible offline:
| Metric | Result |
|---|---|
| Deduplication precision | 100.0% (TP 3, FP 0) |
| Deduplication recall | 100.0% (TN 5, FN 0) |
| F1 | 1.000 |
| Avg cosine similarity — true duplicates | 0.971 |
| Avg cosine similarity — distinct pairs | 0.236 |
| Prompt injections neutralized | 3 / 3 |
| Fixture acceptance | 8 / 8 |
| Test suite | 26 / 26 across 7 files |
On a sandbox pass of 8 issues: 6 allowlisted labels applied automatically, 5 drafts queued, 1 duplicate flagged — one tap per public action instead of writing each reply from scratch.
Two footnotes, because benchmarks without them are marketing:
pnpm hammock verdict fixtures/issue-001.json and confirm the client name does not say [replay]
Best Use of Gemma — Gemma 4 (gemma4:e4b) does three distinct jobs locally: schema-validated classification, root-cause duplicate adjudication with an evidence string, and tone-conditioned reply drafting. Issue text never leaves the machine, and the pipeline survives a full network disconnect.
Best Use of Temporal — the nightly pass is a deterministic workflow with per-issue activity fan-out, defined timeouts, and a registered cron schedule, so interruptions resume rather than duplicate work.
Best Use of Mastra — the triage agent and its four typed tools are defined in @mastra/core, giving the loop a typed, provider-agnostic contract over the local model.
Best Use of Auth0 for AI Agents — approval is modelled as asynchronous authorization: a pending proposal carries an authorization request, and the write path is gated on an approval identity recorded on the receipt. The adapter is swappable; the demo runs its local-session fallback.
Apache-2.0 © 2026 Lakshay Deol · Hammock triages while you sleep, and never speaks without your permission.