Skip to content

docs(deployment): retire the Vercel and ObjectQL-migration pages — delete, re-point their inbound redirects, and fix the prose links into them #8904

Closed

Description

@os-zhuang

Card 01 of the deployment-section restructure (maintainer ruling, 2026-08-15). Two pages come out of content/docs/deployment/. They are one card, not two — both edit content/docs/deployment/meta.json and apps/docs/redirects.mjs, so splitting them only serialises the same two files across two PRs.

Rulings (settled — do not re-litigate)

  • 「https://objectstack.ai/docs/deployment/vercel 已经不需要了。平台通常用 docker 发布。」 → delete vercel.mdx, redirect to self-hosting.
  • 「migration-from-objectql 作废」 → delete migration-from-objectql.mdx.

Why these two, measured

vercel.mdx (195 lines) documents a path no CI covers — vercel does not appear anywhere in .github/workflows. (The "Vercel Preview Comments" check on PRs is the docs site's own hosting, unrelated to deploying a user app.) Meanwhile Docker is the real shipping path: docker/Dockerfile plus .github/workflows/docker-publish.yml. The page is also already visibly decaying — it carries a Removed in 11 callout for the MSW mode and the Next.js adapter.

migration-from-objectql.mdx (179 lines) documents a one-time @objectql/core → @objectstack/objectql port. It belongs to neither the platform-runtime nor the metadata-app lifecycle the section is being restructured around, and every published page is a standing maintenance obligation.

⚠️ The trap: both pages are already redirect TARGETS

apps/docs/redirects.mjs currently ends two older URLs at these very pages:

['/docs/guides/deployment-vercel',  '/docs/deployment/vercel'],              // line 95
['/docs/guides/objectql-migration', '/docs/deployment/migration-from-objectql'], // line 101

Deleting the destinations without touching these leaves two live URLs redirecting into a 404. Next.js serves one redirect hop — it does not follow chains — so adding a second entry does not rescue them. Both existing entries must be re-pointed at the final destination, not merely supplemented.

Prose links that must be fixed in the same PR

content/docs/deployment/self-hosting.mdx links to the Vercel page twice:

  • line 11 — [Vercel guide](/docs/deployment/vercel) and assumes you have read …
  • line 424 — - [Deploy to Vercel](/docs/deployment/vercel) — the serverless alternative

Line 424 is a Related-links entry (delete it). Line 11 is mid-sentence and needs the sentence rewritten, not just the link stripped. migration-from-objectql has no prose inbound links — only meta.json and the redirect above.

The Check Documentation Links gate (lint.yml) will catch a missed one, but do not use it as the search: find them first, then let the gate confirm.

Scope

  • Delete content/docs/deployment/vercel.mdx and content/docs/deployment/migration-from-objectql.mdx.
  • Drop both entries from content/docs/deployment/meta.json.
  • In apps/docs/redirects.mjs: re-point the two existing entries, and add /docs/deployment/vercel → /docs/deployment/self-hosting. Choose the migration-from-objectql destination and argue it in the PR — /docs/deployment is the neutral default; there is no successor page.
  • Respect the file's own documented ordering rule: "Exact entries come first; wildcard catch-alls for retired sections must stay last (first match wins)."

Docs-only, skip-changeset. Do not touch any other deployment page — the restructure that moves them is a later card.

Verify

git grep -rn "deployment/vercel\|migration-from-objectql" -- content/ apps/   # expect: only redirects.mjs
pnpm check:docs-audit-scope && pnpm check:release-notes && pnpm check:role-word

Then confirm both retired URLs and both older /docs/guides/* URLs each resolve in one hop to a page that exists.

Context

Full IA proposal and the other four cards: this is card 01 of 5, sequenced first because it is the cheapest irreversible move and nothing else depends on it. Cards 02–05 (new Upgrading page, publish-and-preview rewrite, Docker promotion in self-hosting, index rewrite) follow separately.

Activity

  1. added theissue type on Aug 15, 2026
  2. self-assigned this
    on Aug 16, 2026
  3. os-zhuang commented on Aug 16, 2026

    @os-zhuang
    ContributorAuthor

    Claim: PM loop round 2
    Session: session_013gLCsJ1sDoJZZruDKMsHH3
    Branch: claude/issue-8904-retire-vercel-objectql-docs
    Worktree: objectstack-issue-8904
    Domain: domain:devx
    File surface: content/docs/deployment/vercel.mdx (delete), content/docs/deployment/migration-from-objectql.mdx (delete), content/docs/deployment/meta.json, content/docs/deployment/self-hosting.mdx, apps/docs/redirects.mjs (stop on breach; explain in the report)
    Container & model: S but not mechanical, mode:subagent, model: opus
    Serial constraints cleared: no open PR touches content/docs/deployment/** or apps/docs/redirects.mjs. #8911 is the only other card on self-hosting.mdx and is pm:blocked behind this one — it must not start until this merges.

    Model tier raised from the initially proposed sonnet to opus, measured. The gate farm does not cover this card's hardest part: Check Documentation Links runs lychee --offline over content files, and nothing in scripts/ or .github/workflows/ references redirects at all. So the two prose links in self-hosting.mdx are gate-caught, but the two dangling redirect entries are not — a wrong answer there ships two silently-404ing live URLs with CI fully green. Judgement outside the gate farm takes the higher tier.

    Routed under the maintainer's direct-dispatch channel. Authorizing instruction, verbatim: 「你先派 8904」


    Generated by Claude Code

  4. os-zhuang commented on Aug 16, 2026

    @os-zhuang
    ContributorAuthor

    Claim amendment — declared file surface (session session_013gLCsJ1sDoJZZruDKMsHH3, branch claude/issue-8904-retire-vercel-objectql-docs).

    Three files beyond the surface declared in my claim above are touched by PR #8947. Amending the declaration so it still serializes correctly against parallel work:

    Added to surface Why it is not optional
    .claude/workflows/docs-accuracy-audit.js Generated scope list. Deleting the two pages leaves two entries pointing at files that no longer exist, and check:docs-audit-scope — a gate this card names — fails on exactly that. Regenerated with the script's own --write remedy.
    scripts/role-word-baseline.json migration-from-objectql.mdx was baselined at 2 occurrences; check:role-word — also named by this card — fails when a baselined file vanishes. Ratcheted down with the remedy the gate prints.
    packages/objectql/README.md Links into the deleted migration guide by relative path. Same defect class as the card's prose links, but outside lychee's scan surface, so no gate would have caught it.

    The first two are mechanically forced by the deletion and add no gate surface — they are the two named gates. The third is the bounded in-place exemption: same defect class, mechanical form pinned by the sibling fix in self-hosting.mdx, no other claim on the file (no open PR touches it), and no new verification surface. All three are named with their evidence in the PR body.

    Anyone claiming work on these three files should treat them as held until #8947 merges.

    Generated by Claude Code


    Generated by Claude Code

  5. os-zhuang commented on Aug 16, 2026

    @os-zhuang
    ContributorAuthor
    {
      "issue": 8904,
      "status": "done",
      "branch": "claude/issue-8904-retire-vercel-objectql-docs",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/8947",
      "premise_still_valid": true,
      "summary": "Both pages deleted, dropped from deployment/meta.json, and their inbound URLs re-pointed in apps/docs/redirects.mjs (two existing entries re-pointed, two new exact entries added before the wildcards per the file's ordering rule). The two Vercel prose links in self-hosting.mdx are gone — line 424 deleted outright, line 11's sentence rewritten. migration-from-objectql lands on /docs/deployment: /docs/protocol/objectql/ was weighed and rejected because those pages specify the ObjectQL protocol, not a package port, so they promise a successor that does not exist. THREE PREMISE CORRECTIONS. (1) The 'zero prose links to migration-from-objectql' claim is wrong: packages/objectql/README.md:100 linked into it by relative path. The PM sweep was scoped to content/ apps/; a repo-wide grep finds it, and it sits outside lychee's scan surface (content/**, root README.md, ARCHITECTURE.md), so nothing would have caught it — fixed here under the bounded in-place exemption. (2) Two gate-owned artifacts are mechanically broken by the deletion and were NOT in the scope ceiling: .claude/workflows/docs-accuracy-audit.js (check:docs-audit-scope fails on the 2 stale paths) and scripts/role-word-baseline.json (check:role-word fails when a baselined file vanishes). Both regenerated with the remedy each gate itself prints; both are named gates on this card, so this adds no new gate surface. (3) The Next.js one-hop mechanism is half right — see tests. Claim file surface amended in a comment above.",
      "tests": "No build or test run — docs-only change, no package source touched, so the shared heavy-verify lock was never needed. All gates run FROM THE FINAL COMMIT a34b9e8df. PM-named four: check:docs-audit-scope OK ('scope is in sync with content/docs/: 177 hand-written doc(s)'); check:release-notes OK ('every released major has a curated, navigable release page'); check:role-word OK ('43 baselined file(s), no new occurrences'); check:nul-bytes OK ('scanned 5932 text file(s) ... no raw ASCII control bytes'). Re-derived with `node scripts/pm/dispatch-gates.mjs` against the real diff, which ADDED four families the prompt did not name: check:doc-authoring OK ('375 files clean'), check:skill-frame-sync OK ('12 cases pass', 4 frame copies isomorphic), check-engine-split-ratio OK (matched only via the packages/objectql/README.md edit), and check:doc-formula-expressions — NOT RUN, the one family needing a workspace install. Skipping it is evidence-backed, not assumed: a repo-wide grep shows no baseline or fixture under packages/ names either deleted page, so it cannot be keyed on them, and a docs deletion only shrinks its input. BEFORE regenerating I ran both stale-artifact gates and watched them go RED with the exact expected messages ('2 listed path(s) do not exist'; 'baselined file is clean/gone (was 2) — ratchet DOWN'), then confirmed each regeneration diff was exactly one removed line per file — important for check-role-word --update, which rewrites the WHOLE baseline from the tree and could silently re-baseline other files; it did not. THE DELIVERABLE CI CANNOT GIVE: I resolved every one of the 92 redirect destinations against the tree the way Fumadocs routes them (/docs/x to content/docs/x.mdx|.md|/index.mdx). All four owned URLs resolve in exactly ONE hop to a real page: /docs/deployment/vercel and /docs/guides/deployment-vercel to self-hosting.mdx; /docs/deployment/migration-from-objectql and /docs/guides/objectql-migration to deployment/index.mdx. Zero entries in the table now chain (a destination that is itself a source) — checked explicitly. MECHANISM CORRECTION: the prompt's claim that supplementing without re-pointing 'does not rescue' the old URLs is half right. next.config.mjs is a plain redirects() via toNextRedirects() with no chain resolution, and middleware.ts is i18n-only and never touches these paths, so Next does match the table once per request — correct. But the response is a 308 whose Location an HTTP client re-requests, and that new request re-enters the table, so supplementing alone would have produced a TWO-HOP CHAIN, not a 404. The conclusion is unchanged (re-pointing is what the ruling asks and what I did, and one hop is strictly better), but the failure mode is a wasted round trip and diluted redirect signal, not a break. PM sweep otherwise confirmed: exactly two prose links to the Vercel page, both in self-hosting.mdx, nothing beyond them; the only surviving mentions of either page anywhere in the tree are the four intentional redirect entries and two archival lines in docs/audits/2026-06-handwritten-docs-accuracy-followups.md, which are a dated historical record of a June 2026 state and were deliberately left alone.",
      "open_questions": [],
      "out_of_scope_findings": [
        "filed as #8948: three entries in apps/docs/redirects.mjs point at pages that do not exist — /docs/guides/solutions/role-based-interfaces, /docs/guides/plugin-chatbot-integration and /docs/concepts/cloud-artifact-api each 308 into a 404. Pre-existing, unrelated to the two retired pages, found by the same 92-entry resolver; ui/audience-based-interfaces.mdx exists, so the first looks like a rename whose redirect was never updated. Unassigned and unlabeled for triage; flagged there that it edits the same file as #8947 and should be scheduled after it merges."
      ]
    }

    Label read-back on #8947 (after bots settled): size/m, skip-changeset — written as a union so the bot's size/m survived the whole-set PUT.

    Generated by Claude Code


    Generated by Claude Code

  6. os-zhuang commented on Aug 16, 2026

    @os-zhuang
    ContributorAuthor

    ACCEPT — PR #8947. Reviewed against the tree, not the report. Both corrections to my dispatch are upheld.

    Where I was wrong — twice

    1. "Zero prose links to migration-from-objectql" was false. packages/objectql/README.md linked into it by relative path (../../content/docs/deployment/migration-from-objectql.mdx). My sweep was scoped to content/ apps/ and I reported its result as if it were repo-wide.

    The part that makes this worth recording: I verified the link-checker's scan surface, and it is content/**/*.md, content/**/*.mdx, root README.md and ARCHITECTURE.md. packages/**/README.md is outside it. So this would have merged green and left a dangling relative link — my narrow sweep was the only thing standing between that and production, and it was narrow. Finding it required ignoring the file list I supplied.

    2. My redirect mechanism was half wrong. I wrote that supplementing without re-pointing "does not rescue" the old URLs, implying a 404. The correction is right: Next matches the table once per request (that half of my claim holds), but the 308's Location is re-requested by the client and that request re-enters the table — so supplementing alone yields a two-hop chain, not a break. The remedy is unchanged and the PR does the right thing; my stated failure mode was wrong, and a card that had trusted my wording would have hunted the wrong symptom.

    Verified independently

    • Redirect table — two entries re-pointed (not supplemented), two exact entries added ahead of the wildcards per the file's own ordering rule. All four owned URLs resolve in one hop to files that exist. Spot-checked the resolver's method against the tree.
    • .claude/workflows/docs-accuracy-audit.js — diff is exactly the two stale paths removed, zero additions.
    • scripts/role-word-baseline.json — exactly one entry removed, nothing else re-baselined.

    On --update, which deserves saying plainly, because it looks like gate-weakening at a glance: the script marks that flag ⛔ MAINTAINER-ONLY for the expand path — accommodating a new occurrence. The ratchet-down path is a different act, documented in the same header as the normal remedy: "a baselined file's count DECREASED / file vanished (improvement!) — run with --update to ratchet the baseline down and commit it." The file here was deleted, so down was the only direction available, and the diff confirms the baseline only shrank. No maintainer-only action was taken. Checking the whole-file rewrite for silent re-baselining of unrelated files was exactly the right paranoia.

    Landing path

    Path surface: content/docs/**, apps/docs/redirects.mjs, scripts/role-word-baseline.json, packages/objectql/README.md, .claude/workflows/docs-accuracy-audit.js. The ACCEPT fork triggers on docs/adr/**, .claude/skills/** and skills/** — .claude/workflows/ is none of those, so this lands through the merge queue normally rather than waiting on a human. Recording that as a decision rather than leaving it silent, since the path shares a prefix with a fork trigger.

    CI is still converging (ESLint, TypeScript Type Check, Test Core and Dogfood shards in_progress) — honest reading at report time. Holding the ready-flip until the lint.yml job reports completed: success.

    Out-of-scope finding, spot-verified

    #8948 is real. I confirmed two of its three: content/docs/deployment/cloud-artifact-api.mdx and content/docs/guides/solutions/role-based-interfaces.mdx do not exist, while content/docs/ui/audience-based-interfaces.mdx does — consistent with the rename theory. It edits apps/docs/redirects.mjs, the same file as this PR, so it is correctly flagged to be scheduled after this merges.

    Downstream

    #8911 (Docker promotion in self-hosting.mdx) is blocked on this and must re-read the file on the merged ref — self-hosting.mdx line numbers moved here.


    Generated by Claude Code

  7. os-zhuang commented on Aug 16, 2026

    @os-zhuang
    ContributorAuthor

    Landed. PR #8947 merged.

    Verified on origin/main as a second reading — "merged" and "the content is correct" are separate facts:

    • content/docs/deployment/vercel.mdx and migration-from-objectql.mdx — both gone.
    • content/docs/deployment/meta.json — down to 11 pages, neither entry present.
    • Repo-wide grep across content/ apps/ packages/ returns only the two intentional redirect entries (redirects.mjs:110-111). The two /docs/guides/* entries no longer match because they were re-pointed away from the deleted pages, which is exactly the outcome this card existed to secure.

    PR subscription released on merge. #8911 unblocked — pm:blocked removed, back to pm:queue.

    Follow-up still open: #8948 (three pre-existing redirect destinations that do not exist). It edits apps/docs/redirects.mjs, which this PR also touched, so it is now clear to schedule.


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions