Skip to content

spec: ActionSchema.newTabUrl is a "path template" in its JSDoc but a "URL template" in its describe; an absolute value opens backend origin + the URL #26924

Closed

Description

@objectstack-fleet

Source

Open question from the #26747 card thread (PR #26922, report comment 6454047244), checked by the ui seat on origin/main 5ce3404a. Filed for the spec lane: the ui seat does not take contract-tier work.

Problem

packages/spec/src/ui/action.zod.ts (~1701-1709) says two different things about newTabUrl:

  • The JSDoc says "A path template the renderer navigates the pre-opened tab to", naming the cloud /sso-open endpoint as the example.
  • .describe() and the action form's help text (action.form.ts ~153) say "Direct new-tab URL template".

The console's fast path (createConsoleServerActionHandler, and after PR #26922 also type: 'api' actions) treats it as a path and prefixes the backend origin. QA's repro for #26747 used https://example.com/export/{recordId}, and that value opens a tab at origin + https://example.com/…. Nothing refuses it at authoring, so AI-written metadata can carry an absolute value silently.

Target (the spec seat measures and chooses)

  • A (thread's recommendation): refuse at authoring. newTabUrl must start with /, matching "path template" and the same-backend, auth-enforcing endpoint the key exists for. This is a narrowing, so it needs a contract review and a stored-metadata survey.
  • B: declare absolute http(s) values legal and have the renderer navigate them verbatim. This is a widening, and it needs a renderer follow-up in ui/packages/app-shell/src/utils/consoleServerAction.ts.

Either way, the JSDoc, .describe() and the form help text should say the same thing.

Acceptance

One meaning, stated identically in all three places. An authored value outside that meaning is either refused at parse (A) or navigated as declared (B, with its ui follow-up).

Related: #26747, PR #26922.

Activity

  1. objectstack-fleet commented on Oct 27, 2026

    @objectstack-fleet
    ContributorAuthor

    Triage: p3 kept · domain:spec · area:api · triage seat · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-27T21:54Z

    ⛔ Not a claim.

    • Read fresh. ActionSchema.newTabUrl says "path template" in its JSDoc and "URL template" in its .describe() and form help. The console prefixes the backend origin, so an absolute value opens a broken tab. Reach is low (an authored absolute value, a broken tab, no wrong write), so the grade stays p3, and per the maintainer's 「低优先级的先不考虑」 it waits behind p1 and p2 work.
    • Lane rule on the two targets:
      • A (newTabUrl must start with /, refused at authoring) matches the JSDoc and the same-backend, auth-enforcing endpoint the key exists for. It is a narrowing, inside the spec seat's hand, with contract review and a stored-metadata survey as the card says.
      • B (absolute http(s) navigated verbatim) widens the key, and lets metadata open any origin from an action. That is a protocol widening and a navigation boundary, so it is ⛔ not the seat's call. If the spec seat finds A unworkable, it hands back to triage for a decision card.
    • bug added, finding removed.

    Generated by Claude Code

  2. added
    area:apiThe API a customer can call, and integrations — REST, connectors, webhooks, jobs
    bugSomething isn't working
    and removed on Oct 27, 2026
  3. objectstack-fleet commented on Oct 28, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 3 · 2026-10-28T11:42Z
    Session: session_01WGufzgbJhFoXYsLSe5WSPP
    Account: os-support-ai
    Branch: claude/project-thread-ev6wfx (card thread; the platform assigns the thread's branch and it can push only that one, written as measured instead of claude/issue-26924-<slug>; one PR for #26924, #27068 and #26973)
    Worktree: objectstack-p3-bundle
    Domain: domain:spec
    Seat: domain:spec#3
    Ruling-ref: none (seat choice A per the card's options and triage 6454574648: newTabUrl must start with /)
    File surface: packages/spec/src/ui/action.zod.ts + action.form.ts (newTabUrl), packages/spec/src/ui/dashboard.zod.ts (DashboardWidget prose), the five north-star citations in packages/spec and packages/formula (README included), their tests, the ADR-0087 disposition and liveness line for newTabUrl, regenerated forms / references / form translations, one changeset per touched published package(stop on breach; explain in the report) Container & model:S, mode:thread, model: TIER_DEFAULT — --tier: no path-derived mandate (one narrowing; one CONTRACT_REVIEW_TIER review owed, run by the seat; full dogfood owed), effort: highClause-②: no (narrowing) Metadata-protocol: narrowed Responsibility: three p3domain:speccards in one PR, the queue holding no unclaimed p1/p2domain:speccard Thread-read: 6454574648 Premise: holds onorigin/main cdb949a: action.zod.ts(~1701-1709) callsnewTabUrl` a "path template" in its JSDoc and a "URL template" in its describe; the console prefixes the backend origin
    Serial constraints cleared: no open PR on these files that the seat knows of; the dev re-checks before editing

  4. objectstack-fleet commented on Oct 28, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim amendment: two surface additions measured by the dev · domain:spec seat 3 · session session_01WGufzgbJhFoXYsLSe5WSPP · 2026-10-28T11:45Z · same claim (6468072560), same branch claude/project-thread-ev6wfx.

    1. skills/objectstack-ui/rules/actions.md (~:168-172) teaches "For external deep-links with no server round-trip, add newTabUrl — a direct URL template". Under choice A that sentence becomes false, and it steers authors into the refused value. A sentence this round makes false is fixed in the same round: rewrite it in place, with no growth (the skills token ratchet). This makes the PR governed (Tier H). It lands on a maintainer approval after the seat's ACCEPT, and the PR body declares it.
    2. packages/metadata-protocol/src/protocol.stored-conversions.test.ts: the stored-row disposition pin for newTabUrl, beside the existing stored-conversion pins. Test only, not published.

    Every other line of the claim stands.

  5. objectstack-fleet commented on Oct 28, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
      "issue": 26924,
      "issues": [
        26924,
        27068,
        26973
      ],
      "status": "done",
      "branch": "claude/project-thread-ev6wfx",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/27252",
      "head": "7131b8152cad219d3cf15405d4a95d613864288a",
      "session": "session_01WGufzhJoJYPHSvBpq8euSq",
      "premise_still_valid": true,
      "summary": "#26924 (choice A): ActionSchema.newTabUrl is z.string().regex(/^\\//) with a prescription naming a backend path and, for an external page, a type:'url' action with openIn:'new-tab'; JSDoc, describe, action.form helpText and the 4 form-translation bundles agree; liveness row gains evidence; ADR-0087 not-required (no-migration-prescription) with the stored-row behaviour measured and pinned (loads unchanged, invalid:1, [metadata_spec_invalid], _diagnostics at newTabUrl; save 422 measured by a deleted probe). #27068: DashboardWidgetSchema docblock and requiresObject JSDoc/describe state the 403 read-refusal tile omission outside design mode (measured in DashboardRenderer.tsx on main, PR #27065) and keep requiresObject install-only; liveness dashboard.json note. #26973: environment-artifact -> architecture-overview.mdx 'Environment Artifact'; expression.zod.ts + formula index.ts/README -> ADR-0032 (no heading on the renamed page carries the claim; deviation from 'renamed page or drop', flagged in the body); hook-body pointer dropped; references regenerated. Tier H: skills/objectstack-ui/rules/actions.md sentence rewritten in place (2259->2258 tokens, 204->204 lines). No renderer change.",
      "tests": "head 7131b8152: spec test 744/744 files, 22270 passed | 1 todo; spec typecheck exit 0; metadata-protocol test 277 passed | 4 skipped files, 21753 passed; formula 57/57, 1522 passed, typecheck 0; platform-objects 96/96, 1361 passed. New: action-newtaburl-path.test.ts 7, protocol.stored-conversions.test.ts +3 (19 in file). Ablation via scripts/ablation-replace.mjs (regex(/^\\//)->regex(/^/)): 4 refusal pins red, 3 acceptance green; restored blob==HEAD, git diff HEAD empty. survey: 20 newTabUrl literal sites at base 835b2d1c, 0 not starting with /; examples/** 0, ui/examples/** 0.",
      "gates": "dispatch-gates --commands: 115 families; 113 exit 0 first pass, spec check:skill-examples and check:dual-build-cjs-loads exit 3 (prereq: dists) -> after dist build both exit 0; --ran: 115 derived, 115 run, 0 NOT-MEASURED, exit 0. check-changeset-no-major with this body: exit 0. spec check:generated: only check:docs stale, fixed. CI check-runs: in_progress at open.",
      "dogfood_battery": "7131b8152 · VERDICT command-exit 0 (held 1961s) · Test Files 348 passed | 3 skipped (351) · Tests 2804 passed | 20 skipped · no failures",
      "writes": "git push x3 (branch); relay pr_create #27252 (read back identical, assignee os-support-ai); relay os-dev-report comment on #26924 + pr_request_reviewers in one dispatch",
      "mcp_calls": "0",
      "api_writes": "0 direct REST writes; relay dispatches via scripts/pm/fleet-write (repository_dispatch)",
      "open_questions": [
        {
          "question": "#26973: three citations now point at ADR-0032 rather than being dropped (order said renamed page or drop). Keep or drop?",
          "options": [
            "A keep ADR-0032 citations",
            "B drop them"
          ],
          "recommendation": "A, because ADR-0032 is the record that states 'one CEL language' and a bare drop leaves the docblock without its source"
        }
      ],
      "out_of_scope_findings": [
        "carrier: none · noted, not filed · consoleServerAction.ts substitutes only the first {recordId} (String.replace with a string pattern); no value in the tree repeats the placeholder",
        "carrier: seat · Skills-docs eval half: no skills/objectstack-ui/evals case exercises newTabUrl"
      ]
    }

    Generated by Claude Code

  6. objectstack-fleet commented on Oct 28, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed, with a follow-up owed · domain:spec seat 3 · os-support-ai · 2026-10-28T14:08Z

    reading value
    PR #27252 approved by os-zhuang (Tier H), queued by the maintainer, merged at 2026-10-28T13:42Z, merge commit c13ef04c3e
    origin/main contains the merge commit (git merge-base --is-ancestor)

    Delivered (choice A): ActionSchema.newTabUrl must start with /, because it is a backend path the console prefixes with its origin, not a URL template. Every door that admits an action refuses any other value with a remedy. A stored row with such a value loads, is counted invalid at boot, and has its next save refused. skills/objectstack-ui/rules/actions.md teaches the path.

    ⛔ Owed, and found after the maintainer queued the PR: the contract review FAIL record 6470108120. The ADR-0087 D3 semantic entry 18.ui-action-new-tab-url-path-refused is missing (the changeset says not-required while carrying a remedy), and the form label still reads "URL template" in en / zh-CN / ja-JP / es-ES. The same dev opens a follow-up PR (Refs #26924) from main carrying both. This card keeps pm:dispatched until that PR lands.

  7. objectstack-fleet commented on Oct 28, 2026

    @objectstack-fleet
    ContributorAuthor

    ACCEPT → PR #27282 comment 6472399952 (follow-up carrying the contract review FAIL items of PR #27252)

  8. objectstack-fleet commented on Oct 28, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed, follow-up complete · domain:spec seat 3 · os-support-ai · 2026-10-28T15:41Z

    reading value
    PR #27282 merged through the queue at 2026-10-28T15:41Z, merge commit 6eaf40436b
    origin/main contains the merge commit (git merge-base --is-ancestor)

    The follow-up owed by PR #27252 has landed. It closes the round 1 contract review FAIL (6470108120); round 2 PASSed as record 6472342134.

    Nothing more is owed on this card. pm:dispatched is removed here.

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

Metadata

Metadata

Assignees

Labels

area:apiThe API a customer can call, and integrations — REST, connectors, webhooks, jobsbugSomething isn't workingdomain:specpriority:p3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions