A hackathon fork of giltine/cc exploring a typed-chat surface inside the cc app.
This experiment replaces the Claude.ai workflow for planning and triaging giltine-stack work with first-class chats backed by Convex. Each chat has a type (planning, general) that determines which docs are injected into the system prompt, which MCP tools the agent gets, and whether the agent can propose vairas batchRuns. Round-trips run through stampas on the same batchRuns queue the rest of the workflow uses. The chat itself becomes the planning and triage surface — no external Claude.ai project required.
For the broader autonomous workflow this plugs into, see giltine/cc/docs/claude.ai/autonomous-workflow-spec.md.
The typed-chat architecture landed across nine PRs between 2026-05-02 and 2026-05-03. Here's what exists today:
Schema (convex/schema.ts): Four new tables power the chat surface:
chatTypes— defines chat types with their system prompt templates, default docs to inject, MCP servers, and whether they can spawn agentschats— individual chat instances, each linked to achatType, with session state, runner preference, and message metadatamessages— Anthropic-shaped content blocks (text, tool_use, tool_result) plus token counts and status trackinginjectedDocs— cached doc content fetched from Forgejo, keyed by(repoSlug, path, ref)with content hashing so re-fetches are no-ops when the file hasn't changed
The existing batchRuns table was extended with runner (claude|bob), spawnedFromChatId, the "chat_message" kind, and chatId/messageId params for chat-dispatched runs.
A new batchProposals table implements the propose-then-approve flow: when the planning agent identifies a batch worth running, it emits a PROPOSE_BATCH_START … PROPOSE_BATCH_END JSON block. Stampas parses it into a batchProposals row; the chat UI renders a ProposalCard next to the message; the user clicks approve to enqueue the actual kind: "run" batchRun. The agent never spawns directly — every batch goes through human approval.
Seeds (convex/chats/seed.ts): Two chat types ship out of the box:
planning— compass icon, can spawn agents, injects the giltine workflow docs (autonomous-workflow-spec.md,issue-process-giltine.md,giltine-project-context.md), emitsPROPOSE_BATCHmarkers when it identifies a coherent batchgeneral— message-square icon, open-ended, no injected docs, no agent spawning
Run bunx convex run chats/seed:seedChatTypes to populate or update them.
Doc fetcher: A Convex action pulls doc files from Forgejo and caches them in injectedDocs keyed by (repoSlug, path, ref). Content hashing ensures re-fetches are no-ops when the file hasn't changed. The system prompt assembly logic reads from this cache and injects the docs into the agent's context.
Chat dispatch: The chat composer calls chats.send, which inserts a user message and enqueues a kind: "chat_message" batchRun. Stampas claims it, GETs /api/stampas/chats/dispatch?chatId=... to fetch the assembled system prompt + latest user message, runs the agent (default runner: bob), and POSTs the assistant message back to /api/stampas/chats/assistant-message. Both endpoints are gated by X-CC-Secret matching STAMPAS_API_SECRET.
Propose-then-approve: When the planning agent identifies a batch worth running, it emits a PROPOSE_BATCH_START … PROPOSE_BATCH_END JSON block. Stampas parses it into a batchProposals row; the chat UI renders a ProposalCard next to the message; the user clicks approve to enqueue the actual kind: "run" batchRun. The agent never spawns directly — every batch goes through human approval.
UI: Chat list page (app/in/chats/page.tsx) + new-chat dialog wired into the sidebar, plus ChatDetailClient (lib/components/chats/ChatDetailClient.tsx) rendering the message stream and the proposal cards inline. The ProposalCard component (lib/components/chats/ProposalCard.tsx) displays the batch title, rationale, and approve/reject buttons.
- #8 — Schema additions for typed conversations + chatTypes seed
- #12 — Forgejo doc fetcher action +
injectedDocscache + system prompt assembly - #18 — Chat list page, new chat dialog, sidebar entry,
ChatDetailClient - #32 — Fix
chats/dispatchto readchatIdfrom POST body - #34 —
chatsGetDispatchincludeschatTypeSlugin dispatch response - #39 —
batchProposalstable + queries/mutations - #46 —
ProposalCardcomponent +ChatDetailClientintegration - #49 — Append
PROPOSE_BATCHinstructions to the seeded planning prompt - #54 — Cosmetic fixes + http endpoint adjustments
Org-scoped actions (listOrgRepos, listOpenIssues, reconcileOrg,
etc.) gate access through assertOrgMember, which resolves the
caller's Forgejo username from their session email via
/api/v1/users/search.
FORGEJO_TOKEN must be an admin-scoped (site admin) token.
Forgejo only includes user email fields in the search response when
the requester is a site admin. A non-admin token returns results
without emails, email-based user resolution always fails, and every
org-gated action returns NOT_ORG_MEMBER — effectively locking out
the entire user base.
To create the token:
- Sign in to Forgejo as a site admin.
- Go to
Settings → Applications → Generate New Token. - Grant the token the scopes needed to read users, orgs, repos,
issues, and pull requests (at minimum
read:admin,read:user,read:organization,read:repository,read:issue). - Set it on the Convex deployment:
bunx convex env set FORGEJO_TOKEN=<token>.
If you see NOT_ORG_MEMBER for users you expect to have access, check
the Convex logs for the one-shot warning emitted by
fetchForgejoUsernameByEmail — it surfaces a mis-scoped token.
The bob/ folder contains artifacts from bob, the alternative agent runner used by stampas for chat dispatch (default runner for new chats — see runner: "bob" in the dispatch flow).
bob/custom_modes.yaml— the custom modes bob runs against the giltine pipeline (giltine-reviewer,giltine-triager, planner modes, etc.). These define role definitions, tool groups, and per-mode instructions used by the autonomous workflow.bob/tmp/<session-hash>/chats/session-*.json— raw chat session transcripts captured while bob was driving the typed-chat surface end-to-end. Useful for inspecting how the agent handled real planning rounds, tool calls, andPROPOSE_BATCHemissions.
These are checked in for hackathon reviewers and are not consumed by the running app.
Standard cycle:
bun installbunx convex dev(andbunx convex env set FORGEJO_TOKEN=…per the section above)bunx convex run chats/seed:seedChatTypesto populate the two chat typesbun run devfor the Next.js front