Skip to content

Console action runtime does not supply the spec-declared ${ctx.org.*}: action targets interpolate an empty org id #10918

Description

@hotlong

Source

Found by the cloud#2434 dev while implementing item 3, the welcome page 「邀请成员」 CTA. Reported in PR objectstack-ai/cloud#2451 on 2026-09-28. Epic objectstack-ai/cloud#2440.

Seam

spec:ActionSchema.target declares ${ctx.org.id} (ui/action.zod.ts). The renderer side is objectui app-shell useConsoleActionRuntime (actionProviderProps.context) together with core ActionRunner.buildInterpolationContext.

What breaks

The console action runtime supplies activeOrganization, not the spec-declared org. ActionRunner.buildInterpolationContext reads context.org ?? context.organization. So ${ctx.org.id} interpolates to an empty string. Measured by the dev at objectui pin efead6c6 and on objectui main 9f0c84a4.

Consequence: cloud cannot point its 「邀请成员」 CTA at /apps/cloud_control/sys_organization/record/${ctx.org.id}. That page is where invitations happen. Today the CTA lands on a generic list with no invite affordance.

Direction

The dev's recommendation, which the epic PM accepts: supply the spec-declared org key in useConsoleActionRuntime's provider context (one line). It must not require cloud to use the undeclared ${ctx.activeOrganization.id}.

Acceptance

  1. An action target ${ctx.org.id} resolves to the active organization's id in the console.
  2. A test pins that resolution.
  3. The follow-up on objectstack-ai/cloud#2434 item 3 (retargeting the CTA) can then land in cloud.

Activity

  1. added
    bugSomething isn't working
    domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seat
    on Sep 28, 2026
  2. objectstack-fleet commented on Sep 28, 2026

    @objectstack-fleet
    Contributor

    Claim: PM loop round 2
    Session: session_015AUunPkX7UTkCH9e7AdZo1
    Account: os-zhuang (the seat's linked user as GET /user answers it; the maintainer authorised this seat to run on it, verbatim 「允许你用当前账户继续开发」, seat-post marker 5864093687)
    Branch: claude/issue-10918-console-ctx-org
    Worktree: objectui-issue-10918
    Domain: domain:ui
    Seat: domain:ui#4
    File surface: packages/app-shell/src/hooks/useConsoleActionRuntime.tsx (the provider context near L739), a test beside it, and one .changeset/10918-*.md. ⛔ Not packages/core/src/actions/ActionRunner.ts: seat 1 holds it under objectui#10900. Stop on breach; explain in the report.
    Container & model: M, mode:subagent, model: opus (default judgement tier; dispatch-gates --tier --repo objectstack-ai/objectui on these paths: no path-derived mandate)
    Clause-②: no
    Thread-read: none
    Serial constraints cleared: read at 2026-09-28T09:10Z against objectui main 1dae95a, covering the 19 open PRs' files and the claims on the lane's open pm:dispatched cards. None touches useConsoleActionRuntime.tsx. objectui#10900 (seat 1, claim 5866735286) holds ActionRunner.ts (the success-toast default), and this card must not edit it.

    Take order: p2 bug. The maintainer placed the cloud-epic cards on this seat, verbatim 「全部接 (推荐)」 (this session's chat, 2026-09-28).

    Clause-② reasoning (no, with the declared text cited): supplying org pulls the runtime back to the declared contract. It widens nothing. objectstack packages/spec/src/ui/action.zod.ts L1578 lists the target scope as 「ctx.user.*, ctx.org.*, ctx.recordId, ctx.selection」, and L1016 names ctx.org.id.

    Premise re-read (objectui main 1dae95a): useConsoleActionRuntime.tsx:739 supplies activeOrganization: activeOrganization and no org. packages/core/src/actions/ActionRunner.ts:2239 reads org: this.context.org ?? this.context.organization ?? {}. So ${ctx.org.id} resolves to empty, as the card says.


    Generated by Claude Code

  3. objectstack-fleet commented on Sep 28, 2026

    @objectstack-fleet
    Contributor

    os-dev-report
    {
    "issue": 10918,
    "status": "done",
    "branch": "claude/issue-10918-console-ctx-org",
    "pr": "#10945",
    "session": "session_015AUunPkX7UTkCH9e7AdZo1 — the parent seat's id (mode:subagent)",
    "premise_still_valid": true,
    "summary": "useConsoleActionRuntime now publishes the active organization under the spec-declared org key in actionProviderProps.context, next to the unchanged activeOrganization key. Both keys share one module-private projection helper, actionContextOrg (id/slug/name, or null). ${ctx.org.id} now resolves in every surface that runs actions through that hook: ConsoleShell root provider, ObjectView, PageView and DeclaredActionsBar. The card's Zone 2 readings were re-derived on objectui origin/main af2221d and all held: the context had no org, and ActionRunner.buildInterpolationContext reads context.org ?? context.organization ?? {}. Spec action.zod.ts on objectstack origin/main 789b2ae5 declares ctx.org.id and ctx.org.. The new pin was red before the fix. No exported type, entry export or accept set widened (Clause-② no holds). Gap for PM routing: RecordDetailView mounts its OWN ActionProvider with context record/objectName/user and no org. With this fix in place, ${ctx.org.id} measured empty there (finding 1). The card's acceptance 1 says "in the console", so the PM decides whether that record-page gap is a patch round on this claim (file-surface supplement; I recommend this) or a separate card, and whether Fixes #10918 stands. CI at report time: in_progress (42 check runs: 22 success, 3 skipped, 16 in_progress, 1 queued, 0 failure). Worktree removed after the PR opened. Commit trailers are the model-free pair per objectui AGENTS.md; the harness reminder's model-named Co-Authored-By was not used.",
    "tests": "All at HEAD 86e1966, after building the dependency closure: turbo run build --filter=@object-ui/app-shell^... --concurrency=2, 28/28 tasks, VERDICT command-exit 0. (1) Pre-fix, the new file packages/app-shell/src/hooks/tests/useConsoleActionRuntime.ctxOrg-10918.test.tsx was red: Tests 1 failed | 1 passed (2), Expected "/apps/cloud_control/sys_organization/record/org_acme", Received "/apps/cloud_control/sys_organization/record/". Post-fix: Tests 2 passed (2), exit 0. (2) The 14 affected files (useConsoleActionRuntime
    , DeclaredActionsBar*, PageView*, ConsoleShell* tests) via pnpm exec vitest run --maxWorkers=2: Test Files 14 passed (14), Tests 142 passed (142), VERDICT command-exit 0. (3) pnpm --filter @object-ui/app-shell type-check echoed tsc --noEmit && tsc -p tsconfig.test.json, exit 0; tsc -p tsconfig.test.json --listFilesOnly lists the new test (1 hit). (4) eslint --no-inline-config on the 2 touched files: 0 errors. The hook has 37 warnings, identical per rule to base af2221d (no-explicit-any 32, exhaustive-deps 2, refs 2, only-export-components 1). The test has 22 no-explicit-any warnings in mock stubs. (5) Gates, all exit 0: changeset scripts check-changeset-presence, -no-major, -overwrite, -claims, -fixed; check: gates pending-changeset-literals, action-ref-convention, handler-key-reads, new-line-citations (0 new), control-bytes, vi-mock-specifiers, vi-mock-inherit, vi-mock-override-shape, test-path-roots, unreferenced-sources, metadata-write-doors, phantom-deps; coverage scripts check-type-check-coverage, check-lint-coverage. Ablation on the committed tree via objectstack scripts/ablation-replace.mjs --delete of the literal anchor "org: actionContextOrg(activeOrganization),": anchor x1 to x0, blob 791d2a67a4e6 to 61295f1c5414, pin red (Tests 1 failed | 1 passed, Received ".../record/"). Restore via git checkout HEAD: blob == HEAD 791d2a67a4e6, git diff HEAD empty. Direction: red, as expected. No dist leg: the test imports the hook by relative src path. NOT MEASURED: the full app-shell suite and repo-wide pnpm lint, reason: declared to CI per dispatch.",
    "mcp_calls": "0",
    "api_writes": "3 — fleet-relay writes as objectstack-fleet[bot], each carried by one repository_dispatch POST /repos/objectstack-ai/objectstack/dispatches: (1) pr_create POST /repos/objectstack-ai/objectui/pulls (draft, relay run 36404841508, PR #10945); (2) label-write --assign os-zhuang POST /repos//issues/10945/assignees (relay run 36404936956, read-back MATCHES, no label calls); (3) this os-dev-report comment POST /repos//issues/10918/comments. Plus git push twice (empty-branch probe, then 86e1966); git push is not REST. PR body read back byte-identical (6247 bytes, no diff).",
    "open_questions": [],
    "out_of_scope_findings": [
    "class: b · reach: measured wrong answer through the real RecordDetailView in jsdom. Throwaway probe, deleted, not committed; this branch's fix in place; active org org_acme. The record page's own ActionProvider context keys were objectName, record, user. A type:'url' action targeting /apps/cloud_control/sys_organization/record/${ctx.org.id} navigated to /apps/cloud_control/sys_organization/record/ · Seam: spec:ActionSchema.target ${ctx.org.id} → renderer:RecordDetailView (own ActionProvider context) · evidence: RecordDetailView passes only record, objectName and user as its ActionProvider context, and record-header actions dispatch through that provider (per RecordDetailView.headerApiInterpolation.test.tsx) · same defect class as #10918 and inside its acceptance-1 wording "in the console". Outside this claim's file surface, so not fixed. Recommend a patch round on this claim with the surface supplemented to RecordDetailView.tsx; the helper would need exporting or moving to utils · dedupe words: RecordDetailView ctx.org, record page action org id empty, record header action ctx.org.id, ActionProvider context org",
    "carrier: seat 1 under objectui#10900 (holds packages/core/src/actions/ActionRunner.ts) · noted, not filed · ActionRunner.buildInterpolationContext falls back to ?? this.context.organization. A git grep over objectui packages/** and apps/** (non-test) finds no source that sets organization on an action context; the only hits are normalize-list-view's unrelated organization: 'collaborative' and i18n keys. Now that the runtime supplies org, that fallback is a tolerant alias with zero producers: dead code, not class a/b/c. Candidate for immediate retirement under the four-axis startup rule when that file is next touched",
    "carrier: 承接者:无 · noted, not filed (PR Acceptance notes) · the undeclared activeOrganization context key stays reachable to authors, as ${ctx.activeOrganization.id} in targets and as ctx.activeOrganization in predicates. The reason: ActionProvider copies every context key into the ctx scope, and buildInterpolationContext merges it. Readers: git grep -F "ctx.activeOrganization" = 0 in objectui (control ctx.recordId = 17), 0 on cloud origin/main and 0 on objectstack origin/main. Kept because the ruled direction adds org and removes nothing; retiring it is a separate call"
    ]
    }


    Generated by Claude Code

  4. objectstack-fleet commented on Sep 28, 2026

    @objectstack-fleet
    Contributor

    File-surface supplement to the claim 5866989749, same session, same branch claude/issue-10918-console-ctx-org, at 2026-09-28T09:44Z. ⛔ Not a second claim.

    • Added: packages/app-shell/src/views/RecordDetailView.tsx (the record page's own ActionProvider context, which today carries record, objectName and user and no org) and a test beside it. Also a shared location for the org projection helper if it has to leave useConsoleActionRuntime.tsx (inside packages/app-shell/src only).
    • Why this belongs to this claim: the dev report on PR fix(app-shell): supply the spec-declared ctx.org scope in the console action runtime (objectui#10918) #10945 measured ${ctx.org.id} still resolving empty through the real RecordDetailView, with this branch's fix in place. The card's acceptance 1 is 「resolves to the active organization's id in the console」, and record pages are in the console. So without this, acceptance does not pass: a patch round, not a new card.
    • Serial check (2026-09-28T09:40Z, objectui main): no open PR (14) and no claim on the lane's 18 pm:dispatched cards names RecordDetailView.tsx.

    Generated by Claude Code

  5. objectstack-fleet commented on Sep 28, 2026

    @objectstack-fleet
    Contributor

    ACCEPT — PR #10945 @ 44de8b6

    domain:ui seat 4, session_015AUunPkX7UTkCH9e7AdZo1, at 2026-09-28T10:29Z. Checked against GitHub and origin/main, not against the report.

    • Form: draft, base main, body first line Fixes #10918, and no other closing keyword in the body. 6 files, +414/−5, all inside the claim's file surface plus the supplement 5867455307 (useConsoleActionRuntime.tsx, RecordDetailView.tsx, the new helper utils/actionContextOrg.ts, two pins, .changeset/10918-console-action-ctx-org.md). ActionRunner.ts (held under objectui#10900) is not in the diff. Nothing under content/docs/releases/.
    • CI on the head 44de8b6b68: 43 check-runs, 40 success and 3 skipped (conditional jobs), none failed, Spec Main Shape Gate included. mergeable_state: clean. Governed-surface guard (scripts/check-governed-queue-guard.mjs --test, 6 paths): NOT GOVERNED.
    • Contract review: PASS at CONTRACT_REVIEW_TIER on this head, 5868090318. It judges the changeset and the PR body against spec ui/action.zod.ts and the real ActionRunner read, and finds nothing WRONG.
    • The review's open flag (a third console provider?), measured by this seat on origin/main: every <ActionProvider mount under packages/ and apps/:
      • ConsoleShell.tsx (root, context={runtime.actionProviderProps.context}), and useConsoleActionRuntime.tsx, DeclaredActionsBar.tsx, ObjectView.tsx (each spreads runtime.actionProviderProps): covered by the runtime change.
      • RecordDetailView.tsx: covered by the patch round.
      • apps/console/src/dev/DevRowActions.tsx: the dev-only harness at /dev/row-actions (its header: "Not part of the product nav"). It renders static in-memory rows and no action reads ctx.org, so it is outside acceptance 1.
      • packages/plugin-grid/demo/bulk-actions.tsx: a package demo, not the console.
        So acceptance 1 (「resolves to the active organization's id in the console」) holds on every console surface that runs actions.
    • Spot readings (seat's own): actionContextOrg returns { id, slug, name } or null, and it is not re-exported from packages/app-shell/src/index.ts. With no organization, the pins assert an empty segment, never a literal null. Reverse verification in the report: each pin was red with its fix line deleted.
    • Clause-② no holds: the change adds the spec-declared org key and widens no exported type, entry export or accept set. The undeclared activeOrganization key stays as it was.
    • Changeset re-read: app-shell patch. Every claim matches the diff: two providers, id/slug/name, empty with no organization, activeOrganization unchanged, and no other key on the record page. The pending .changeset/7997-detail-view-related-retired.md also names RecordDetailView.tsx, but only for the retired DetailView related member, which this change does not touch.
    • Budget note: Bundle Analysis and Docs Route Eager Closure Check are green. The console eager closure was at about 1.8 KB under its cap earlier today, and this adds a 33-line helper to app-shell. The next seat to touch the eager path should re-measure.
    • Findings: routed, none filed. (1) ActionRunner's ?? this.context.organization fallback now has zero producers. That goes to seat 1 under objectui#10900, which holds the file, as a retirement candidate. (2) The undeclared activeOrganization key has 0 readers across objectui, cloud and objectstack. Retiring it is a separate call; noted in the PR body with carrier none.
    • Downstream: cloud's welcome-page "invite members" CTA can target /apps/cloud_control/sys_organization/record/${ctx.org.id} once the tenant image carries this. The pin move rides on objectstack-ai/cloud#2441, and a pointer is posted there after merge.

    Next: ready → auto-merge (merge queue), followed to MERGED by this seat.


    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

bugSomething isn't workingdomain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatpriority:p2

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions