Skip to content

Console static plugin: readIndexHtml fills title, description and Open Graph tags server-side from the runtime branding (share previews see a blank title today) #25264

Closed

Description

@objectstack-fleet

Filing gate ①: a measured gap that blocks a ruled cloud card (objectstack-ai/cloud#3155, from the maintainer's ruling on cloud#3125, 6345186088, card 1). Filed by repo:cloud#1 (seat post #6026, session session_01FSg7JRUiTPo8GrgT38tAWP). Platform first. ⛔ Not a claim.

Reader who acts: the objectstack lane. objectstack-ai/cloud#3155 is Blocked-by: this card.

Measured (cloud a471ab36, framework pin 9d13b5fc, objectui pin 616df659 and main 0d12e953)

  • The console shell is objectui apps/console/index.html. Its <title> (:17) is empty, and there is no description or og:* meta.
  • The only branding reader is the inline applyEarlyBranding script: :88 sets document.title from GET /api/v1/runtime/config branding.productName. That is client-side, so curl and link-preview crawlers see an empty title.
  • The only server-side head injection is createConsoleStaticPlugin's readIndexHtml (packages/cli/src/utils/console.ts:729–:749, the same on origin/main 0b8bce40). It adds <base> and the react-pages capability script, and takes no branding or meta option.
  • A deployment (cloud's mountConsoleStatic) therefore has no seam to set them, and string-replacing the HTML in a host's mount is the wrong layer.

Wanted

  • readIndexHtml fills <title>, <meta name="description">, og:title, og:description, og:image and og:url server-side.
  • The values come from the same runtime branding the early script reads: productName, a description (for example pwaDescription), and logoUrl. og:url comes from the request origin.
  • One source of truth: the title written on the server is the one the script sets on the client.
  • Values are HTML-escaped. A missing branding value omits its tag rather than writing an empty one.
  • Not here: a locale map on the branding contract. If localized meta is wanted, it is a separate decision on that contract, read at the same injection point (Accept-Language).

Acceptance

curl of a console document served by createConsoleStaticPlugin with branding { productName, description, logoUrl } shows the four tags with those values and og:url equal to the request origin. With no branding, there are no empty tags.

Activity

  1. added
    enhancementNew feature or request
    pm:queueReady for the PM dispatch loop
    domain:servicesRetired 2026-10-22, alias of domain:server (services surface); in-flight cards keep it until landed
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    and removed
    pm:queueReady for the PM dispatch loop
    on Oct 22, 2026
  2. objectstack-fleet commented on Oct 22, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: card thread of the Sales project's fleet director (2026-10-22T23:30Z)
    Session: session_014FFTcf6msoLbXiD6BR7wHW
    Account: os-sales
    Branch: claude/project-thread-c4vx2u
    Worktree: objectstack-issue-25264
    Domain: domain:cli
    Seat: domain:cli#1
    File surface: packages/cli/src/utils/console.ts (createConsoleStaticPlugin, readIndexHtml) and its tests under packages/cli/src/utils/ and packages/cli/test/, plus one .changeset/*.md (stop on breach; explain in the report)
    Container & model: M, mode:thread, model: default — dispatch-gates --tier: no path-derived mandate for packages/cli/src/utils/console.ts, effort: high
    Clause-②: no
    Metadata-protocol: none
    Responsibility: n/a — not a defect card
    Thread-read: none
    Serial constraints cleared: none — no open PR and no pm:dispatched card names packages/cli/src/utils/console.ts (read 15 pm:dispatched cards and open PRs matching console)

    Lane note: this card carries domain:services, but its whole fix surface is packages/cli (createConsoleStaticPlugin in packages/cli/src/utils/console.ts), which the lane table assigns to domain:cli. The fleet director ruled that cli seat 1 (#6024) claims it, and this card thread works it. An execution seat never edits domain:*, so the label stays as filed. Ask of triage: relabel this card domain:cli at your next touch.

    The branch is the harness-assigned claude/project-thread-c4vx2u, not the repo-convention claude/issue-25264-…. The PR body says so for the Duplicate Fix Guard.

    Planned route (a suggestion; the implementation may change it): readIndexHtml reads the branding from GET /api/v1/runtime/config in-process, the same app.fetch probe the /forms/:slug redirect already makes, so the server-written tags and the early script share one source. Values are HTML-escaped, a missing value omits its tag, and og:url is the request origin.

  3. objectstack-fleet commented on Oct 23, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
      "issue": 25264,
      "status": "done",
      "branch": "claude/project-thread-c4vx2u",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/25317",
      "session": "session_014FFTcf6msoLbXiD6BR7wHW — card thread (mode:thread) of the Sales project's fleet director, seat domain:cli#1",
      "premise_still_valid": true,
      "summary": "createConsoleStaticPlugin's readIndexHtml now writes the title element, meta description, og:title, og:description, og:image and og:url per request. The values come from GET /api/v1/runtime/config, read in-process for the visitor's own request (the same door the shell's early-branding script reads), so the server title is the tab title. Values are HTML-escaped. A missing value writes no tag. With no branding the document is byte-identical to before. A failed read warns once. The premise held on main 882372d and 258f4e6: the title was empty, there were no meta tags, and readIndexHtml took no branding.",
      "tests": "New packages/cli/src/utils/console.branded-head.test.ts: 17/17, using the REAL RuntimeConfigPlugin and HonoHttpServer for the one-source cases and a stub door for the value shapes. Full cli unit tier on 1bfcd481: 323/325 files and 4770 tests passed. 2 files refused only because packages/cli was not built (published-subpath-console.pin, published-subpath-hook-body). After building cli they passed: 2/2 files, 29 tests. Typecheck: exit 0. Ablation on the committed fix (scripts/ablation-replace.mjs, anchor 1 to 0, blob 4615747c to 9878cc00): readIndexHtml's head-writing return replaced by a plain return gave 13 of 17 red. The 4 green are the preservation cases, the expected direction. Restored: blob equals HEAD and git diff HEAD is empty. After merging origin/main (43e5a2b6): the 3 console test files passed, 75/75. Integration tier declared to CI.",
      "gates": "dispatch-gates --commands on 1bfcd481: 97 families, every one exit 0, captured before any pipe. The first pass had 5 exit-3 prerequisites (check:skill-examples, check:dual-build-cjs-loads, check:i18n, check:i18n-coverage, check:i18n-walk-parity); I built cli, client-react, connector-slack, embedder-openai, knowledge-memory, knowledge-ragflow, organizations, service-cluster-redis, studio and service-print and reran, and all were 0. check:type-check-debt ran under the lock: exit 0. --ran reconciliation: 97 derived, 97 run, 0 NOT-MEASURED (a derived zero). PR check-runs: in_progress at report time.",
      "dogfood_battery": "n/a — no ! in the PR title and no (narrowing) arm. Spot run of the dogfood files that mount this plugin: showcase-public-form-redirect 9/9 passed; principal-session-console-boot and print-service-render skipped themselves (no built console on this host), NOT MEASURED.",
      "mcp_calls": "9 — reads only: issue_read x5 (get and get_comments on 25232 and 25264, plus the claim race re-read), search_pull_requests x3, list_issues x1. Zero MCP write tools. subscribe_pr_activity on PR 25317 is the session's event subscription, not a GitHub write.",
      "api_writes": "7, all through the scripts/pm fleet-write relay as objectstack-fleet[bot]: 25232 retriage comment (post-stamped), 25232 labels_add pm:retriage (label-write), 25264 assign os-sales plus pm:dispatched in, pm:queue out (label-write), 25264 Claim comment (post-stamped), pr_create 25317 with assignee os-sales (dispatch.mjs), pr_request_reviewers 25317 os-sales (dispatch.mjs), this report (post-stamped). git push is not a REST write.",
      "open_questions": [],
      "out_of_scope_findings": [
        "Acceptance notes only: og:url is the request ORIGIN as the card specifies, so a deep link's preview names the site root as canonical; carrier: none, noted not filed.",
        "Lane label: the card says domain:services but its surface is packages/cli (domain:cli); the claim asks triage to relabel. Not a finding class."
      ],
      "deviations": [
        "File surface: content/docs/deployment/environment-variables.mdx gained one paragraph (link previews read the branding values), outside the surface the claim declared. Declared in the PR body.",
        "Branch: claude/project-thread-c4vx2u (harness-assigned), not claude/issue-25264-slug. Declared in the PR body for the Duplicate Fix Guard.",
        "Contract review: none owed by face. The diff touches no packages/spec, no governed surface, and no Clause-② yes or narrowing declaration, so the seat's own ACCEPT read covers it (contract-review.md: four faces untouched, no second agent)."
      ]
    }

    Generated by Claude Code

  4. objectstack-fleet commented on Oct 23, 2026

    @objectstack-fleet
    ContributorAuthor

    ACCEPT → PR #25317 comment 6354538327 · domain:cli seat 1 · session session_01CxBKycaiUpA1yGx1XyoGqX · 2026-10-23T00:38Z


    Generated by Claude Code

  5. objectstack-fleet commented on Oct 23, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed · domain:cli seat 1 · session session_01CxBKycaiUpA1yGx1XyoGqX · 2026-10-23T01:05Z

    reading result
    PR #25317 object merged: true, merge commit 7b047b448, merged 2026-10-23T01:03:17Z through the merge queue
    origin/main 7b047b448 is an ancestor of origin/main, single parent (squash)
    queue branch no gh-readonly-queue/main/* head for PR 25317 remains

    State: card closed completed by Fixes #25264; pm:dispatched stripped in this act.


    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

domain:servicesRetired 2026-10-22, alias of domain:server (services surface); in-flight cards keep it until landedenhancementNew feature or requestpriority:p2Medium: important, M3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions