{"slug": "hammock-the-gentle-ai-sloth-that-triages-your-github-issues-overnight-and-never", "title": "Hammock: The Gentle AI \"Sloth\" That Triages Your GitHub Issues Overnight (And Never Speaks Without Permission) 🦥💤", "summary": "A developer built Hammock, an open-source GitHub issue triage tool that runs overnight on a local Gemma 4 model via Ollama and orchestrates work through durable Temporal workflows. Hammock automatically applies allowlisted, reversible labels and flags likely duplicates using nomic-embed-text embeddings and cosine similarity, but queues all public actions—comments, closes, locks, and assignments—for a single maintainer approval. The project ships with replayable eval and fixture commands, including three prompt-injection test cases, and logs every action with an immutable receipt.", "body_md": "*This is a submission for the [Hacktoberfest Open-Source AI Challenge Week 3: Slow and Steady](https://dev.to/challenges/hacktoberfest-week3-2026-10-19)*\n\nEvery 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?*\n\n**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.\n\nIt applies allowlisted, reversible labels on its own. Everything public : comments, closes, locks, assignments  waits in a local queue for one maintainer tap.\n\n**Hammock can propose anything. It can only *do* the reversible things alone.**\n\nLabels on an allowlist: **automatic**.\n\nComments, closes, locks, and assignments: **your tap**.\n\n| Action | Who decides | \n|---|---|\n| `bug` ,`needs-info` ,`feature` ,`duplicate` ,`question` | **Automatic** — additive, allowlisted in`.hammock.yml` | \n| Comments, closes, locks, assignments | **Your tap** — always, with no config that disables it | \n\nThe 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.\n\nThe 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.\n\n*video*\n\n*live demo*\n\n*To run on Local Device*\n\n**60-second reviewer path** — no Ollama, no Temporal, no tokens:\n\n```\ngit clone https://github.com/Lakshay-Deol/Hammock.git\ncd Hammock\npnpm install && pnpm build\n\npnpm hammock eval-dedupe --replay     # precision / recall vs ground truth\npnpm hammock test-fixtures --replay   # 8 fixtures incl. 3 prompt injections\npnpm hammock dashboard                # http://localhost:3000\n```\n\n**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.**\n\n*Reversible alone. Visible never.*\n\nSubmission for the **Hacktoberfest Open-Source AI Challenge (Week 3: Slow and Steady)**.\n\n**Hammock can propose anything. It can only *do* the reversible things alone.**\n\nLabels on an allowlist: **automatic**\nComments, closes, locks, and assignments: **your tap**.\n\nMost 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**.\n\n```\nGitHub REST API\n      │  since + ETag (conditional, idempotent)\n      ▼\nTemporal schedule \"nightly-triage\"\n      │\n      ▼\nworkflow: fetch → per-issue activity fan-out → digest\n      │\n      ├─► Mastra agent ──►\n```\n\n…\n**Gemma 4 via Ollama** does three genuinely different jobs, each shaped by a different failure mode:\n\n`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.\n\n**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.\n\n**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.\n\nTwo design decisions I'd defend:\n\n`<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.\n\nBecause the alternative is shipping your bug reports to someone else's API.\n\nPrivate 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.\n\nOpen 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.\n\nMeasured on the fixtures in the repo, all reproducible offline:\n\n| Metric | Result | \n|---|---|\n| Deduplication precision | **100.0%** (TP 3, FP 0) | \n| Deduplication recall | **100.0%** (TN 5, FN 0) | \n| F1 | **1.000** | \n| Avg cosine similarity — true duplicates | **0.971** | \n| Avg cosine similarity — distinct pairs | **0.236** | \n| Prompt injections neutralized | **3 / 3** | \n| Fixture acceptance | **8 / 8** | \n| Test suite | **26 / 26** across 7 files | \n\nOn 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.\n\nTwo footnotes, because benchmarks without them are marketing:\n\n`pnpm hammock verdict fixtures/issue-001.json` and confirm the client name does not say `[replay]`\n**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.\n\n**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.\n\n**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.\n\n**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.\n\n*Apache-2.0 © 2026 Lakshay Deol · Hammock triages while you sleep, and never speaks without your permission.*", "url": "https://wpnews.pro/news/hammock-the-gentle-ai-sloth-that-triages-your-github-issues-overnight-and-never", "canonical_source": "https://dev.to/lakshay_deol_3f21b00bccca/hammock-the-gentle-ai-sloth-that-triages-your-github-issues-overnight-and-never-speaks-without-3f77", "published_at": "2026-10-23 16:28:57+00:00", "updated_at": "2026-10-23 16:42:13.496146+00:00", "lang": "en", "topics": ["ai-agents", "ai-tools", "developer-tools", "large-language-models", "ai-safety"], "entities": ["Hammock", "GitHub", "Temporal", "Gemma 4", "Ollama", "Mastra", "nomic-embed-text", "Lakshay-Deol"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/hammock-the-gentle-ai-sloth-that-triages-your-github-issues-overnight-and-never", "markdown": "https://wpnews.pro/news/hammock-the-gentle-ai-sloth-that-triages-your-github-issues-overnight-and-never.md", "text": "https://wpnews.pro/news/hammock-the-gentle-ai-sloth-that-triages-your-github-issues-overnight-and-never.txt", "jsonld": "https://wpnews.pro/news/hammock-the-gentle-ai-sloth-that-triages-your-github-issues-overnight-and-never.jsonld"}}