---
name: feature-request-board
when: feature request board, feedback board, roadmap voting, idea board — a persistent app where users add requests and upvote them, each with a status. Needs a database → runtime → local edition.
needs: database
deploy: runtime
editions: local
capabilities_note: persistent — needs a database (Mongo). Runtime app, async build, local edition only. Declare the canonical `needs: { database: true }`; the deployer injects DATABASE_MONGODB_URL (+legacy MONGODB_URL).
summary: Build a persistent Next.js + Mongo feature request board on the grid — a `requests` collection (title, description, votes:number, status of open/planned/done) with list / upvote (atomic increment) / add. Edition-gate first, scaffold, put the app under services/web/, wire process.env.DATABASE_MONGODB_URL (legacy MONGODB_URL fallback) lazily, declare needs:{database:true} (not requires:), deploy async, poll to a live URL.
---

# Workflow: feature-request-board

The user wants a feature request / feedback board — a page where people add
requests, **upvote** the ones they care about, and each request carries a status
(open / planned / done). Votes and requests must **persist** and be shared across
users, so this is a **runtime app** backed by the grid's shared Mongo, not a
static page. It is the same proven shape as `app-with-data`, with a `requests`
domain (title, description, votes:number, status) and a voting board view.

Follow this recipe. Be honest that a runtime deploy is async (not instant like a
static drop) and that it needs the local edition.

## 1. Edition check FIRST (hard gate)

A persistent app is a built + deployed container. It requires the **local
edition** (Claude Desktop / Claude Code) or the CLI.

- **Hosted edition (Claude Web / hosted MCP):** you CANNOT build a runtime app —
  hosted is inline-only and can only publish static pages. Tell the user
  plainly, offer a **static version** instead (a voting board whose state lives
  in memory only, lost on refresh), and STOP the runtime path here.
- **Local edition:** continue.

## 2. Auth + grid

1. Ensure signed in: `grid_login_status`; if not, `grid_login`.
2. A grid is required. Respect the grid picker: if the user has more than one
   grid, ask which to use; do not assume a target.

## 3. Scaffold

`grid new <name>`. It scaffolds the project folder and writes a `cloudgrid.yaml` with an EMPTY
`services: {}`. No server entity exists yet — the first `grid plug` auto-creates
it from the manifest (honoring its `name:`) and writes `.cloudgrid/link.json`.
Then (a) write the app under **`services/web/`**,
and (b) fill `cloudgrid.yaml` to the shape below (`services.web` + `needs:
{ database: true }`).

## 4. Add the datastore + wire Mongo

1. Set `cloudgrid.yaml`. **App code MUST live under `services/<name>/`** —
   `path:` is the URL mount, NOT the filesystem path.
   ```yaml
   name: my-board
   services:
     web:
       type: nextjs
       path: /
   needs:
     database: true
   ```
   **Declare the datastore with `needs: { database: true }`** — the canonical
   shape. The deployer provisions Mongo and injects `DATABASE_MONGODB_URL` (plus
   the legacy `MONGODB_URL` alias). `requires:` is the deprecated v1 alias; don't
   author new yaml with it, and never set `needs:` and `requires:` together (the
   validator rejects it).
2. Fetch the template for the Mongo wiring + CRUD shape:
   `grid_get_template("template", "feature-request-board")`. It is a minimal, real
   Next.js + `mongodb`-driver board under `services/web/`: a lazy client in
   `services/web/lib/db.js`, an App-Router GET/POST/PATCH/DELETE route on a
   `requests` collection (PATCH does an atomic `$inc` upvote), a server-component
   page, and a client voting board.
3. Adapt it (rename the collection, change fields, adjust the UI). The domain is
   `requests { title, description, votes:number, status(open/planned/done) }`
   with list (votes desc) + upvote (increment) + add. **Read the DB from
   `process.env.DATABASE_MONGODB_URL`** (legacy `process.env.MONGODB_URL`
   fallback) — the grid injects it at dev-time and runtime.
   - **Put the DB connection behind a lazy getter — never read the connection
     string at module top level, or `next build` fails.**
   - Keep the vote increment atomic on the server (`$inc`), so concurrent upvotes
     don't clobber each other.

## 5. (Optional) Run locally

Mention that the user can `grid dev` to run locally against the injected Mongo
before deploying. Don't require it.

## 6. Config

- API keys / secrets → `grid secrets set`. Non-secret config → `grid env set`.
- Do **NOT** set the DB connection vars yourself (`DATABASE_MONGODB_URL` or the
  legacy `MONGODB_URL`) — the grid injects them.

## 7. Deploy (async)

Deploy the folder with `grid_plug`. A **runtime deploy is ASYNC**: the first
response is `status: "building"`, NOT a live URL yet.
- Poll `grid_check_deploy` (or the returned poll_url) until the entity is live.
- Surface a liveness signal while it builds — never a bare silent wait.
- Only once it is live, return the deployed app URL (NOT the build/log link).

## 8. Return the live URL + iterate

Give the user the live app URL — that is the deliverable. To iterate, re-plug the
SAME entity so it updates the same URL. Keep it honest: async build, local-edition
only, credentials injected by the grid.
