Skip to content

[Gastown] PR 6: tRPC Routes — Town & Rig Management #268

@jrf0110

Description

@jrf0110

Parent: #204 | Phase 1: Single Rig, Single Polecat

Note: This was previously part of #212, which has been repurposed as the Rig DO Alarm. The tRPC routes are now a separate issue.

Goal

Dashboard API for creating and managing towns and rigs. The sling mutation creates DO state and arms the alarm — the alarm handles dispatching to the container. All reads go through the Gastown worker HTTP API (DO SQLite), no Postgres.

New Router

src/server/routers/gastown.ts

export const gastownRouter = router({
  // -- Towns --
  createTown: protectedProcedure.input(z.object({
    name: z.string().min(1).max(64),
  })).mutation(async ({ ctx, input }) => { /* create town via gastown worker */ }),

  listTowns: protectedProcedure
    .query(async ({ ctx }) => { /* list towns for current user */ }),

  getTown: protectedProcedure.input(z.object({ townId: z.string().uuid() }))
    .query(async ({ ctx, input }) => { /* get town with rigs */ }),

  // -- Rigs --
  createRig: protectedProcedure.input(z.object({
    townId: z.string().uuid(),
    name: z.string().min(1).max(64),
    gitUrl: z.string().url(),
    defaultBranch: z.string().default('main'),
  })).mutation(async ({ ctx, input }) => { /* create rig, initialize Rig DO */ }),

  getRig: protectedProcedure.input(z.object({ rigId: z.string().uuid() }))
    .query(async ({ ctx, input }) => { /* get rig with agents, active beads */ }),

  // -- Beads (read from DO via worker API) --
  listBeads: protectedProcedure.input(z.object({
    rigId: z.string().uuid(),
    status: z.enum(['open', 'in_progress', 'closed', 'cancelled']).optional(),
  })).query(async ({ ctx, input }) => { /* list beads via gastown worker */ }),

  // -- Agents --
  listAgents: protectedProcedure.input(z.object({ rigId: z.string().uuid() }))
    .query(async ({ ctx, input }) => { /* list agents via gastown worker */ }),

  // -- Work Assignment --
  sling: protectedProcedure.input(z.object({
    rigId: z.string().uuid(),
    title: z.string(),
    body: z.string().optional(),
    model: z.string().default('kilo/auto'),
  })).mutation(async ({ ctx, input }) => {
    // 1. Create bead in Rig DO (via internal auth HTTP call)
    // 2. Register or pick an agent (Rig DO allocates name)
    // 3. Hook bead to agent (Rig DO updates state)
    // 4. Arm Rig DO alarm → alarm will dispatch agent to container
    // 5. Return agent info (no stream URL yet — comes from container)
  }),

  // -- Send message to Mayor --
  sendMessage: protectedProcedure.input(z.object({
    townId: z.string().uuid(),
    message: z.string(),
    model: z.string().default('kilo/auto'),
  })).mutation(async ({ ctx, input }) => {
    // 1. Create a message bead assigned to the Mayor agent
    // 2. Arm alarm → dispatches to container
  }),

  // -- Agent Streams --
  getAgentStreamUrl: protectedProcedure.input(z.object({
    agentId: z.string().uuid(),
    townId: z.string().uuid(),
  })).query(async ({ ctx, input }) => {
    // Fetch stream ticket from container via TownContainer.fetch()
    // Return WebSocket URL for dashboard to connect to
  }),
});

Key difference from original #212: The sling mutation no longer creates a cloud-agent-next session. It creates state in the DO and arms the alarm. The alarm handles dispatching to the container. This decouples the API response time from container cold starts.

Dependencies

  • PR 1 (Rig DO)
  • PR 2 (HTTP API Layer)
  • PR 4 (Town Container)
  • PR 5 (Rig DO Alarm)

Acceptance Criteria

  • gastownRouter added to tRPC app router
  • Town CRUD (create, list, get)
  • Rig CRUD (create, get)
  • sling mutation: creates bead → assigns agent → hooks bead → arms alarm
  • sendMessage mutation for Mayor communication
  • Bead and agent list queries via Gastown worker API
  • Agent stream URL endpoint (fetches ticket from container)
  • Authorization checks (user owns the town)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions