Repository navigation
fix(publish) + feat(published): public-page polish + cross-ref/citation strip - #414
Merged
Merged
Conversation
…shedOrigin wikis.publishedOrigin is stamped by the publish service with whichever origin Core saw on the publish request. When the publish toggle is invoked through a frontend that proxies /api/* to the Core server (Next.js rewrite, reverse proxy, etc), that origin resolves to Core's own hostname, not the user-facing frontend host, so the modal's Open/Copy buttons send users to a URL that does not serve /p/[nanoid]. Symptom (Railway 2-service deploy): publish toggle flips fine, but Open opens https://robincore-production-xxx.up.railway.app/p/<slug> instead of https://gatsby.up.railway.app/p/<slug>. The Core service does not serve the public wiki page; it lives in the frontend workspace. Repro: deploy core/ and wiki/ on separate hosts so that the wiki frontend proxies /api/* to the core service. Publish any wiki via the Settings tab. Click Open. URL points at the core host. Root cause: core/src/routes/wikis.ts:686 derives origin from new URL(c.req.url).origin (i.e. whatever URL Core itself sees). The frontend re-uses that field in AddWikiModal.tsx:943,961 to build the Open/Copy targets. The frontend already knows its own origin (window.location) and should use that for URL construction. The wikis.publishedOrigin column remains useful as audit but should not be treated as authoritative for user-facing URLs.
Reverse proxies (Next.js rewrites, Railway edge, nginx) forward the original user-facing host as X-Forwarded-Host. Without honouring it, multi-host Robin deployments store Core's hostname in wikis.publishedOrigin, which then leaks into MCP responses and any consumer that constructs /p/<slug> URLs from the stored origin. Fall back to the existing c.req.url derivation when the header is absent so same-origin setups keep working.
- Swap the abstract infinity glyph for Robin's logo (matches the onboarding screen and the rest of the brand surface). - Remove the 'Star on GitHub' button; the published page is a reader surface, not a developer landing page. - Wrap 'Powered by Robin Wiki' footer text in an anchor to www.withrobin.ai/knowledge so the footer line is itself a route back to the marketing page.
The infobox is an editor affordance, useful when working inside the wiki tool but visually loud on a read-only public page. Drop it from the published render path and trim the now-unused imports + type field.
Regen emits a leading {Type}: {Name} heading at the top of the wiki body. The published page already renders {wiki.name} as the page H1, so the body's title rendered a second time underneath. Strip the first heading- or bold-only line if its text contains the wiki name; leave arbitrary paragraphs alone to avoid clipping legitimate prose.
Wiki workspace's tsconfig sets target=ES2017 which predates the regex dotAll (/s) flag. Use [\\s\\S]*? in place of .* so the matcher still spans newlines without depending on a flag that fails the production build.
Public readers can't open /wiki/<id>, /fragments/<id>, /people/<id>, etc; those routes live in the authenticated app. Walk every [[kind:slug]] token in the wiki body and: - For [[wiki:slug]] whose target is itself published, emit an inline markdown link [Label](/p/<their-slug>). Otherwise drop the link and keep the label as plain text. - For [[person:slug]], drop the link and keep the label. Person names usually read as natural prose. - For [[fragment:slug]] and [[entry:slug]], remove the token entirely along with any leading whitespace. Fragment titles often carry a date prefix (260514 - ...) which reads as inline noise mid-sentence when rendered as plain text on the public page. Inline [N] citation markers are stripped outright since the source fragment isn't reachable from the public surface anyway. Wiki names are free-form user content (e.g. "Q4 [2026] Plan"), so the markdown link label is escaped to keep ']' / '[' / '\\' from breaking the surrounding link syntax. Covered by core/src/lib/published-content.test.ts.
koolamusic
force-pushed
the
pr-5/publish-origin-fix
branch
from
May 26, 2026 15:45
b75fe6f to
edc52a4
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Operator-reported bug → triage → root cause → fix → polish, all on the public-reader surface (
/p/[nanoid]).The trigger: published a wiki on a two-host deployment (Wiki at
gatsby.up.railway.app, Core atrobincore-production-xxx.up.railway.app). The Settings tab toggle flipped fine, but the Open button sent users tohttps://robincore-production-xxx.up.railway.app/p/<slug>— Core's hostname, which doesn't serve the public page. Investigating uncovered more issues on the reader surface (logo, GitHub star, duplicate title, dead cross-refs, citation markers leaking) so I rolled the polish into the same PR. Each commit is independently squashable.1.
wikis.publishedOrigincaptures the wrong URL on multi-host setupsRoot cause —
core/src/routes/wikis.ts:686derives origin fromnew URL(c.req.url).origin. When the frontend POSTs/api/wikis/<id>/publishvia Next.js'srewrites()(or any reverse proxy), Core receives the rewritten request andc.req.urlresolves to Core's hostname. The stored value then leaks into MCP responses, the Settings modal's Open/Copy buttons, and any consumer that builds/p/<slug>URLs from the field.Fix — two commits:
core/src/routes/wikis.ts— preferX-Forwarded-Host/X-Forwarded-Protowhen present (proxies pass these through). Fall back to existingc.req.urlderivation so same-origin setups keep working.wiki/src/components/layout/AddWikiModal.tsx— Open/Copy buttons construct the URL fromwindow.location.origin, ignoring the stored field. The browser always knows its own host, so trust that.DB rows published before this PR keep the stale value but no longer affect the UI. Republishing rewrites the field correctly.
2. Public-page polish
The published
/p/[nanoid]page is the read-only reader surface anyone-with-the-link can open. A few items felt off:/p/<slug>is a reader page, not a developer landing page.withrobin.ai/knowledgeso the footer line itself routes back to the marketing page.{Type}: {Name}heading at the top of the wiki body. The public page already renders{wiki.name}as the H1, so the body's title appeared twice. Strip rule: first heading- or bold-only line whose plaintext contains the wiki name. Narrow enough that arbitrary prose mentioning the title isn't clipped./s(dotAll) flag for spanning newlines; the wiki workspace's tsconfig targets ES2017, which predates dotAll, sonext buildrejected it. Swapped.*?for[\s\S]*?and dropped the flag. Same behaviour.3. Cross-refs and citations on the public reader
The wiki body contains
[[wiki:slug]],[[fragment:slug]],[[entry:slug]],[[person:slug]]cross-refs that the authenticated app resolves into clickable anchors to/wiki/<id>,/fragments/<id>, etc. The public page has no such routes — the links 404, and the inline citation markers[1],[2]point to source fragments that the reader can't open either.Fix —
core/src/lib/published-content.ts+core/src/routes/published.ts:[[wiki:slug]]token, look up whether the target is itself published. If yes, emit a native markdown link[Label](/p/<their-slug>). If no, drop the link and keep the label as plain text.[[fragment:slug]],[[entry:slug]],[[person:slug]]— always drop to plain label (no public route exists for these).[N]citation markers entirely.Content returned to the client is now self-contained markdown — every surviving cross-ref is a real anchor, the
refsfield is empty, andMarkdownContentrenders natively. No client-side change needed beyond the existing renderer.Test plan
/p/<slug>page: logo is Robin (not infinity), no GitHub button, footer line is a link, no infobox.## Belief: <name>or similar, the published page renders the H1 once (chrome) without a duplicate underneath.[1],[2]markers.[[wiki:public-target]]renders as a clickable link to/p/<their-slug>.[[wiki:private-target]]renders as plain text (the label, no link).[[fragment:foo]],[[entry:foo]],[[person:foo]]all render as plain text.🤖 Generated with Claude Code