Manage · Request intake
Capture a request from the tab it came from
RepoOps captures a request from the dashboard topbar with the tab, subtab and page URL it came from, holds it in a local append-only queue, and lets the admin promote it, dismiss it or mark it shipped. Build now opens a pull request that carries the request brief, with auto-merge off. Nothing builds until a person promotes a request to the queue.
For: anyone on the dashboard who spots something worth fixing, and the admin who triages the Requests tab
What it does, and why it helps
The Submit request button sits in the topbar on every tab. The popover asks for a Type (Feature, Fix, Cleanup or Other), a Title of up to 200 characters and optional Details of up to 5000, and shows a Captured line with the active tab or subtab and the viewer URL, read from the same state the nav tracks. Submitting writes one line to the dashboard brain's requests/<yyyy-mm>.jsonl with status New and the submitter kind local, then forwards a copy to repoops.ai best-effort so the hosted team page and the operator console see it. A forward that fails never fails the capture.
The Requests tab (Manage, Requests) lists the fold of the current month and the previous two. Each card shows the type, the title, the body, when it was submitted, who submitted it and the captured tab, subtab and URL. The admin moves it: Promote to queue, Mark shipped, Dismiss (with an optional reason that shows on the card as Rejected) or Return to inbox. Being built and Delivered are builder-owned, so the tab offers no button on them. Build now, offered only on a queued request, opens a pull request from a throwaway worktree that carries the brief file docs/requests/<id>.md, without --auto and without gh pr merge. Every five minutes the dashboard asks gh whether each open pull request merged and flips the row to Delivered.
The pain. Someone notices a wrong filter or a slow tab, says so in chat, and moves on. By the time anyone looks, the tab, the page and the reason are gone, and the note has turned into an argument about what was meant.
The point of view. Capture and build are different acts. A request should carry where it came from, and a person should decide what enters the queue. RepoOps records the tab, subtab and URL at submit time and refuses to build anything that is not queued.
What gets easier. Triage. The card carries the context, the Source filter separates what this install captured from what was pulled down from repoops.ai, and every status change is one button with the transition table enforced on the server.
When it helps. A dashboard user who wants a feature, fix or cleanup tracked without leaving the tab; an admin who wants one queue for local, hosted and public submissions; a team that wants one pull request per request with a person at the merge.
Its limits. Build now opens a branch that carries the brief, not the implementation; an agent or a person pushes the code onto that branch. The tab reads three months of files; older requests are not listed and not deleted. A local submit forwards its text to repoops.ai by default. The advisory audit sends the request text to Anthropic on your key when one is set, and its scores appear on the operator console, not on the desktop tab.
Understand it in 30 seconds
Read the narration
- 0:00 A bug gets mentioned in chat.
- 0:02 By Friday nobody remembers which tab.
- 0:06 Submit from the tab you are on.
- 0:08 RepoOps captures the tab, subtab and URL.
- 0:13 You promote it to the queue.
- 0:14 Build now opens a pull request that carries the brief, not the code, with auto-merge off.
- 0:23 Nothing builds until a person promotes it.
- 0:25 The guide covers the rest.
Synthetic example. Read the guide
Where to find it
Where to find it
- Desktop:
localhost:4000, then Local settings in the sidebar, then Requests under Workspace utilities. - Hosted:
repoops.ai/team/requests, from Team & settings in the sidebar, then Requests under Workspace utilities. - Keyboard: ⌘ K, then type “Requests”.
When to use it
A fix spotted on a tab
Situation. On the Costs tab a filter resets after refresh. The user presses Submit request, picks Fix, types a one-line title, reads the Captured line and submits.
What you do. The admin opens Manage, Requests with the Source filter at Local. The card reads fix, the title, when it was submitted, by local and the captured tab. Press Promote to queue, then Build now.
What you see. The status pill moves from New to In queue, then to Being built as #N with a link to the pull request. The pull request title reads fix(request): followed by the title, and its body says the branch carries the brief, not the implementation.
What it establishes. A branch requests/req_<id> exists on origin with docs/requests/req_<id>.md on it, and a pull request with no auto-merge. When that pull request merges, a later five-minute tick reads Delivered as #N. The merge is a person's act on GitHub.
A public submission pulled down
Situation. Someone files a request on repoops.ai/requests/submit. It lands in the hosted public bucket with the page URL and a hashed IP, and no tab.
What you do. On a desktop bound to the operator's intake team, press Pull hosted requests or wait for the pull, once an hour on the hour. Switch the Source filter to Hosted-pulled. Read the card, then Dismiss with a reason or Promote to queue.
What you see. The status line reads Pulled N, imported M new. The card reads by hosted with the URL alone. A dismissal with a reason renders Rejected: and the reason on the card.
What it establishes. A public request has been read by a person before anything else happened to it. The pull imports rows as New only; a public submission cannot arm a build.
Before you start
- Supported versions
- RepoOps desktop v0.3.1, the release this guide was read against. Build now needs git and an authenticated GitHub CLI (gh auth login) on the machine running the dashboard.
- Where it runs
- Local: the Requests tab under Manage, and the Submit request button on every tab. Hosted: /team/requests reads the team's own rows (counts by status and type, each card with tab, subtab and URL) and offers one write, Send to RepoOps admin; /requests/submit is the public form; the hosted dashboard topbar has its own Submit request. The operator console at /admin/requests belongs to the RepoOps operator, not to a team.
- Permissions
- The local tab has no sign-in of its own; anyone who can reach the dashboard can submit and triage. The hosted page needs the manage role on the team. Send to RepoOps admin is allowed for a team owner or admin, or the request's own submitter, and only while the row is New or Accepted and not yet promoted.
- Connections
- None to capture locally. A device binding to a hosted team for Pull hosted requests, Sync to cloud and the status round trip. ANTHROPIC_API_KEY on this machine for the advisory audit tick. The public bucket on repoops.ai reaches a desktop only when the operator names an intake team in REPOOPS_PUBLIC_INTAKE_TEAM_ID on the server.
- Plan
- No plan gate: neither the intake routes nor the pricing tier list names requests. Seeing /team/requests follows the hosted dashboard's role check, not a tier.
Configure it
- Submit from the tab where you saw it.
Press Submit request in the topbar. Pick a Type, write a Title (the placeholder reads One line: what do you want?), add Details if they help, and read the Captured line: the subtab or tab and the viewer URL. Press Submit. The form answers Thanks. Your request was captured. Triage it on the Requests tab. The server accepts 10 submissions a minute and 100 a day per instance, and the button rests for three seconds after each try.
- Triage on Manage, Requests.
The Source filter starts at Local. A New card offers Promote to queue, Mark shipped and Dismiss; an Accepted card (set in the operator console and pulled down) adds Return to inbox. Dismiss asks Why is this being dismissed? and shows the answer on the card as Rejected. The server refuses any move not in its table, so a Being built or Delivered row cannot be moved by hand.
- Build now on a queued request.
Only a queued request is buildable; the route answers 409 for anything else. The backend probes the target checkout (the dashboard's own repository by default, or the row's repoId when it names a tracked repository), its origin remote and gh auth status before writing anything. A failed probe returns Build backend not configured and the request stays queued with nothing written. On success the row reads Being built as #N.
- Let the scheduled ticks do the round trip.
When bound, the dashboard pulls new hosted requests (200 a pull) and status changes down once an hour, asks gh whether each open pull request merged (100 rows a tick) and flips merged ones to Delivered, and, with ANTHROPIC_API_KEY set, scores up to 5 pending unaudited requests. An hourly pass re-reads the oldest 200 hosted rows so a request the cursor passed is not lost.
- Pull or sync by hand when you need it now.
Pull hosted requests runs the puller once; unbound it answers Not bound to a hosted team. Sync to cloud re-forwards every open local request (not Delivered, Rejected or Shipped; at most 500, five at a time) under the same id, so the hosted side dedupes and nothing doubles.
- Close what already shipped.
Mark shipped records that the work landed without a build for this request. It is offered on New, Accepted and In queue, and Return to inbox undoes it.
| Setting | Where | A sensible choice | Why it matters |
|---|---|---|---|
Type | Submit request popover, Type | fix for something wrong, feature for something new, cleanup for something to remove, other otherwise | The type sets the pull request prefix Build now uses (fix(request), feat(request) or chore(request)) and colours the card. |
Source | Requests tab, the Source filter | Local (the default) for what this install captured; Hosted-pulled after a pull; All to triage both | Display scoping only: a hosted-pulled row is promotable and buildable like a local one. |
Status | Requests tab, the Status filter | All while triaging, In queue before pressing Build now, Shipped to find what you closed | One option per stored status. In queue is stored as queued, Being built as pr-open, Delivered as merged, Rejected as dismissed. |
ANTHROPIC_API_KEY | the data directory's .env | unset unless you want the advisory audit | With a key, each five-minute tick sends up to 5 pending unaudited requests (type, title, body, context) to Anthropic through the router's synthesize tier and pushes the scores to the hosted store when bound. Without one the tick is a no-op and makes no call. The desktop tab does not show the scores. |
REPOOPS_NO_FORWARD | the dashboard process environment; not listed in .env.example | unset in normal use; 1 for a test or verify run | 1 disables every outbound push: the submit forward, Sync to cloud, the status push, the audit push and the build-intent dispatch. It does not stop the pull, the audit call or the git and gh calls. |
REPOOPS_PUBLIC_INTAKE_TEAM_ID | the hosted server environment (website/.env.example), operator only | the id of the one team whose desktops may pull the public bucket | Unset, the public bucket is pullable by no team and /requests/submit submissions accumulate unseen. Team-stamped rows reach their own team either way. |
REPOOPS_DEVICE_TOKEN | .env, the cloud request-builder routine only | empty on a desktop | The cloud builder has no local binding, so this token is how it reads GET /api/requests/build-queue and pushes pr-open and merged up. A bound desktop uses its binding instead. |
What you should see
The normal case
Configuration. A local install, no binding, git and gh authenticated, ANTHROPIC_API_KEY unset.
Expect. A submit lands as New on the next load of the tab. Promote to queue, then Build now, opens a pull request inside the 60-second command timeout and the row reads Being built as #N. The forward to repoops.ai goes to the public ingest with the same id, best-effort.
Verify. GET /api/requests?source=local lists the row with status queued, then pr-open with a prNumber. The branch requests/req_<id> is on origin with one commit adding docs/requests/req_<id>.md. The pull request has no auto-merge set. The status line under the toolbar reads Building as pull request #N.
Bound to a team
Configuration. The desktop is bound; the operator has named this team as the intake team.
Expect. Pull hosted requests imports public and team-stamped rows as New. Status changes made in the operator console (Accepted, or Rejected with a reason) arrive on the same pull. A local Build now or a later merge pushes Being built and Delivered up, so the hosted team page reads Being built as #N and Delivered as #N on the same id.
Verify. The status line reads Pulled N, imported M new. A pulled card reads by hosted or by member, with the URL and, for a hosted dashboard submission, the tab and subtab. /team/requests shows the same status on the same id.
Build now refuses
Configuration. Any of the above, with gh signed out, or a request that is not queued.
Expect. The tab reads Build backend not configured with the probe that failed, for example the GitHub CLI is not installed or not authenticated (run: gh auth login). The request stays queued and no state is written. Build now on a non-queued row is refused with only a queued request can be built.
Verify. GET /api/requests shows the row unchanged. No branch requests/req_<id> exists on origin. The button re-enables after the message.
Data and cost
- What is captured
- One created line per request in the dashboard brain's requests/<yyyy-mm>.jsonl: id, type, title, body, context (tab, subtab, url, userAgent, each cut at 2048 characters) and the submitter kind. One status line per transition with its note, and one audited line per audit with the scores and the advisory blob. The directory is in .gitignore, so the queue is never committed.
- Who can see it
- Reads are local. A local submit is forwarded to repoops.ai at once (bound: the team's ingest with the device token; unbound: the public ingest), carrying id, type, title, body and context. On the hosted side a team-stamped row is visible to that team's managers on /team/requests and to the RepoOps operator on the console; a public row is visible to the operator and pullable by the one intake team. The hosted ingest stores the submitter IP as a hash (keyed when REPOOPS_INTAKE_IP_PEPPER is set), never raw.
- How long it is kept
- Reads fold the current month and the previous two; a request whose created line is older than that window is not listed and is not resurfaced by a later status line. Nothing deletes the files and no retention knob exists; see Delete below.
- What leaves the machine
- The submit forward and Sync to cloud (request content, to repoops.ai). The status push and the audit push (id, status, pull request reference, scores; bound only). The audit call itself (type, title, body, context, to Anthropic on ANTHROPIC_API_KEY, up to 5 requests per five-minute tick, none without a key). Build now (a branch, one commit with the brief, a pull request, to your GitHub origin through gh). The reconcile reads gh pr view for each open pull request. REPOOPS_NO_FORWARD=1 stops the pushes; it does not stop the audit call or the git and gh calls.
- What it costs
- The audit is the only model call: one call per audited request on your own key, at most 5 per tick and never twice for the same row, because the audited line marks it. Capture, triage, pull, sync, reconcile and Build now make no model call. RepoOps holds no meter for this feature, and the per-tick limit is the only cap in the code.
When the result differs
| Symptom | Likely cause | Next action |
|---|---|---|
| The tab reads No requests yet. after a submit. | The Source filter is at Local and the row came from the hosted pull, or the created line is older than the three-month window. | Switch Source to All. A row outside the window is not listed; the file is still requests/<yyyy-mm>.jsonl under the dashboard brain. |
| Submit answers too many requests, please wait a moment before submitting again. | The per-instance guard: 10 a minute or 100 a day. | Wait a minute. The guard fails open on an internal error, so volume is the only reason it blocks. |
| Build now reads Build backend not configured. | No target checkout resolved, no origin remote, or gh is not authenticated. A row whose repoId names an unknown repository is refused rather than built in the dashboard's own checkout. | Run gh auth login on the dashboard machine, confirm the repository has an origin remote, and press Build now again. The request stayed queued. |
| A merged pull request still reads Being built. | The reconcile runs every five minutes and reads at most 100 open rows a tick; or the pull request was closed without merging, which stays Being built on purpose. | Wait a tick. For a closed pull request there is no admin move off Being built; Return to inbox is not offered on it. |
| Pull hosted requests reads Not bound to a hosted team. | No device binding on this install. | Connect the desktop app to your team, then pull again. Public bucket rows also need the operator to set REPOOPS_PUBLIC_INTAKE_TEAM_ID. |
| The operator console shows a request as unscored. | No ANTHROPIC_API_KEY on the desktop that holds the row, or the audit response carried no score. | Set the key in the data directory's .env if you want scores; the next tick audits up to 5 rows. An unscored row is an honest null, never a made-up number. |
| A request submitted locally is on repoops.ai. | The forward runs on every local submit, bound or not. | Set REPOOPS_NO_FORWARD=1 before submitting if the text must stay on the machine. Nothing on the desktop removes a hosted row; see Delete below. |
- Disable
- Not provided. No setting turns intake off or hides the Submit request button, and the ticks ride the server's schedule (the pull once an hour, the reconcile and the audit every five minutes) with no flag of their own. REPOOPS_NO_FORWARD=1 stops the outbound writes; disconnecting the device binding stops the pull and every push.
- Roll back
- Not provided for a build. Build now opens a branch and a pull request; closing or reverting them is a GitHub action, and the row stays Being built. Triage moves are reversible where the table allows: Return to inbox from Rejected, Shipped or In queue. The table is ADMIN_TRANSITIONS in lib/routes/requests.mjs.
- Revoke access
- Disconnect in the desktop app forgets the binding and revokes the device token, which ends the pull, the sync, the status push and the audit push. gh auth logout ends Build now and the reconcile. Send to RepoOps admin cannot be withdrawn: no route sets a promoted row back to unpromoted.
- Delete
- Not provided on the desktop. No route removes a request; the data is requests/<yyyy-mm>.jsonl under the dashboard brain, and the read window hides old rows without deleting them. On the hosted side an operator-only bulk delete exists (website/app/api/operator/requests/bulk-delete); a team cannot delete its own rows.
Related tasks
Maintenance evidence
- Feature id
request-intake(spine leafrequests)- Owner
- Request-intake program (K7 followups; the automation program is ledger LDG-0162). Guide: LDG-0717.
- Supported product version
- RepoOps v0.3.1
- Last verified
- 2026-09-15, read against origin/main at 52366bb6d; labels read from the served tab source (public/requests.html, public/dashboard.html) and the hosted page source (website/app/team/(home)/requests/page.tsx). No live instance was run for this guide.
- Example fixtures
- No fixture file. The transition table, the submit caps and the build route's fail-closed path are inline in lib/routes/requests.test.mjs; the bounded window in lib/requests-store.test.mjs; the pull, forward, status push, reconcile and audit in lib/requests-puller.test.mjs, lib/requests-forwarder.test.mjs, lib/requests-status-forward.test.mjs, lib/requests-reconcile.test.mjs, lib/request-audit.test.mjs, lib/requests-audit-tick.test.mjs and lib/requests-audit-forward.test.mjs. lib/request-build.mjs has no test file beside it.
- Source references
lib/routes/requests.mjs,lib/requests-store.mjs,lib/requests-forwarder.mjs,lib/requests-puller.mjs,lib/requests-reconcile.mjs,lib/requests-status-forward.mjs,lib/request-build.mjs,lib/request-audit.mjs,lib/requests-audit-tick.mjs,lib/requests-audit-forward.mjs,lib/build-dispatch.mjs,public/requests.html,public/dashboard.html,website/app/team/(home)/requests/page.tsx,website/app/api/requests/ingest/route.ts- Documentation review
- Independent review requested on the slice pull request; not yet recorded.
- Video review
- Story script written 2026-09-15; render and review pending in the same slice.
Last updated