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
144
planned
1,365
shipped

Motir · Roadmap

MOTIR-3945Done

The public project SUBJECT gets a route — app/api/public/p/[identifier], the one endpoint the page's own subject has never had

Opened by Zhu Yue ·

Type: code · Executor: coding_agent · Repo: motir-core · ONE PR.

The page's own subject has no public endpoint. Every LIST a public project page renders has one — app/api/public/p/[identifier]/{tree,items,roadmap,changelog} — and the project itself does not: there is no app/api/public/p/[identifier]/route.ts on origin/main. A second renderer can fetch a project's work items and never learn its name.

This is the single hard gap the 2026-08-29 re-measurement found (MOTIR-3877 carries it in full), and it is one route.

What it returns

The subject of /p/<identifier> as the page renders it today: the display name, the public overview / README markdown, the tags, the owning workspace's public identity, and whatever the hero reads. Read app/(public)/p/[identifier]/page.tsx for the field set rather than inventing one — the contract is "what the page needs", and the page is the specification.

Conventions

  • Anonymous, like its four sibling READS. Those take an optional actorUserId = session?.user.id ?? null for viewer-awareness and are explicitly "NOT session-gated on READ"; this route follows them exactly. It is not a new access posture.
  • A framework boundary calls a service. publicProjectsService.getOverview already exists and is what the page uses; this route is a thin caller, not a second read path (the 4-layer rule).
  • The project-access gate is the service's, unchanged. A non-public identifier answers exactly as the page does today — do not invent a second notion of "public".

Boundary

  • It renders nothing. The consumer is MOTIR-3877, later and elsewhere.
  • It does not version anything. Promoting /api/public/* into a contract a second repository may depend on is the sibling card, blocked_by this one.
  • It adds no capability. The data is already public and already served to this repository's own pages; this exposes the same read over HTTP.

Acceptance criteria

  • GET /api/public/p/<identifier> returns the subject fields app/(public)/p/[identifier]/page.tsx renders, for a public project, with no session.
  • A non-public or unknown identifier answers with the same status and shape the existing sibling routes use for that case — asserted, so the two cannot drift.
  • The route calls publicProjectsService; no Prisma client is imported into app/api/public/, asserted in the shape the repository already uses for framework-boundary rules.
  • The response field set is derived from the page's own read — a test fails if the page starts rendering a subject field this route does not return.
  • ≥90% coverage on the files this PR touches.

Context refs

  • motir-core/app/api/public/p/[identifier]/ — the four sibling reads whose shape and access posture this follows
  • motir-core/app/(public)/p/[identifier]/page.tsx — the specification for the field set
  • motir-core/lib/services/publicProjectsService.tsgetOverview, the service this calls
  • MOTIR-3877 — the consumer, and the card carrying the measurement that found this gap

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