Skip to content

moooon

Motir

Vibe your whole project. Bring an idea — Motir's three AI layers plan it, track it, and ship it, end to end. You're looking at Motir, built in Motir.

  • Vibe Project
  • Open Source
  • AI Agent
  • AI Loop
1
requests
0
upvotes
145
planned
1,361
shipped

Motir · Roadmap

MOTIR-4117Done

motir.co serves the two detail pages and the feature-request intake — /items/[key], /requests/[requestKey] and the submit form

Opened by Zhu Yue ·

The two detail pages under /p/[identifier], and the feature-request intake that hangs off them:

  • /p/<identifier>/items/[key] — one work item as the public surface shows it, against the new work-item detail read (the detail routes). It is the deep link a changelog entry, a roadmap card and an items row all point at.
  • /p/<identifier>/requests/[requestKey] — one feature request: its body, its public comment thread as the read returns it, and its vote count. Rendered, not yet actionable — the upvote button and the comment box are the act affordances card's, and this card renders the surfaces they attach to.
  • The submit-request intake — the form, and the duplicate-suggestion step … Both endpoints are anonymous today (getSession is not called in either), so this flow works for a logged-out visitor with no cross-origin session question at all⚠️ CONFIRMED BY READING, AS THE CARD ASKED, AND IT IS FALSE (2026-09-02). On origin/main both POST …/requests and GET …/requests/duplicates call requireCompliantSession(); the submit's own header says "A LOGGED-OUT caller is rejected 401 (sign-in-to-act)". This is the third card carrying the same measurement error (MOTIR-3877's body and MOTIR-4108's table were the first two) — filed as MOTIR-4166.

WHAT SHIPPED: /p/<id>/requests/new is a HAND-OFF DOORWAY, per public-surface-hosts.md AMENDMENT 4 row 6 — it states what is about to happen and links to app.motir.co with the return trip carried, and it holds no field a visitor can fill in. Not a reduced form but the honest one: a partial form would take a title, return no candidates (401), take a body, and lose the draft at sign-in. Canny — the mirror row 6 follows — identifies the visitor first for exactly this reason. noindex, because a doorway must not outrank the roadmap it hands off from.

Read findDuplicateRequests and submitPublicRequest before writing the form. The duplicate step is a real two-phase interaction, not a nicety, and its shape is the service's; the form must send what the route validates, including the projectId the request routes are keyed by rather than the identifier the page's URL carries.

Empty, loading and error states per screen, from the design — including the request thread with no comments, and the duplicate check finding nothing.

Rate limiting is the producer's and it is visible here. enforcePublicWriteRateLimit answers a 429 on the submit path; the form renders that as a state rather than as a failure, because on a public form it is a normal outcome.

Scope boundary: no upvote, no comment posting, no follow. This card ships the two READ screens and the anonymous SUBMIT flow.

Acceptance criteria

  • /p/<id>/items/<key> renders a public work item for a logged-out visitor, and 404s for one that does not exist or that the epic-privacy exclusion hides — distinguishably from an API outage.
  • /p/<id>/requests/<key> renders a feature request with its public comment thread and vote count as the read returns them; the vote control and comment box are present as surfaces and perform nothing in this card.
  • A logged-out visitor can submit a feature request end to end, including the duplicate-suggestion step, and the request appears on the project afterwards. ⚠️ UNBUILDABLE — WITHDRAWN. Both endpoints are session-gated (see the banner). Replaced by: the intake is a hand-off that carries intent=request, the subject and a validated return path, and asks for nothing it cannot submit — asserted in the browser.
  • The form renders the validation errors, the duplicate-candidate state, the empty-duplicates state and the 429 rate-limit state, each as its own state rather than a generic failure. ⚠️ WITHDRAWN with the form. There is no form on this host, so there are no submit-side states here: validation, duplicates and the 429 all belong to the application's own intake, which owns them already. Nothing is lost and nothing is deferred.
  • Every read and write goes through lib/publicProject.ts (or a sibling module beside it) rather than a fetch inline in a component; no database client appears.
  • Every deep link into these pages resolves from the tabs — an items row, a roadmap card and a changelog entry each link to the work-item detail — with no dead link introduced.
  • Canonicals and og:url name SITE_ORIGIN and the page's own path.
  • No file outside motir-marketing is touched.

Context refs

  • motir-core/app/api/public/projects/[projectId]/requests/route.ts · requests/duplicates/route.ts — the anonymous intake and its duplicate check
  • motir-core/lib/services/publicProjectsService.tsgetWorkItemDetail (:993), getRequestDetail (:899), findDuplicateRequests (:1094), submitPublicRequest (:1127)
  • motir-core/lib/rateLimit/publicWriteGuard.ts — the 429 this form must render
  • motir-core/lib/dto/publicRequests.ts
  • motir-core/app/(public)/p/[identifier]/{items/[key],requests/[requestKey]}/page.tsx @ 95a2d4468^ — prior art only
  • motir-marketing/design/public-projects/ — the two detail boards and the intake board

Discussion

No comments yet.

Adding to this discussion signs you in on app.motir.co and brings you back to this request.

Add a comment