Skip to content

job pull: a job with no declared organization runs its pull with no tenantId, so the import match lookup cannot carry the organization the writes are stamped with #31823

Closed

Description

@objectstack-fleet

domain:server#2 · os-steve · session_01ADHkmQKZzp4DLtqiCkZZRM

Filing gate: ① product defect with a named seam and a measured reach (step-0 finding of #31812, from #31778 G9). ⛔ Not a claim.
reach: measured by #31812's card thread on origin/main 6be71abc (SQLite, real ObjectQL + SqlDriver + protocol). packages/runtime/src/app-artifact-handlers.ts jobExecutionContext(undefined) answers { isSystem: true } with no tenantId for a job that declares no organization, which is #31778's G9 setup. #31812 makes the runImport match lookup carry the caller's organization when the context has a tenantId, so on this path the lookup still has no organization predicate and still scans the table once per row (62 min for 100,000 rows in G9). Yet every write of the same pull IS stamped with the install's organization: objectql/src/tenancy/system-write-organization.ts resolveSystemWriteOrganization derives it at write time (#8844; ADR-0131 D9: derived under single only when exactly one organization exists, otherwise refused).
Business reason: the most common sync setup (a job with no organization on a single-organization install) keeps the slow, table-scanning match even after #31812, so a large sync stays unusable.
Reader: the next domain:server seat, from pm:queue, after #31812 lands.
Dedupe (open AND closed): jobExecutionContext organization derive, job pull tenantId system context → nothing on point.
class: a · type: Bug (performance)
Grade: p2. Same reach as #31812, on the path #31778 measured.

维护者速读

没有声明组织的定时同步任务,运行时上下文里没有组织信息。#31812 修好后,导入匹配会带上调用者的组织条件,但这条路径带不上,同步大表时照样逐行扫全表。可数据写入时,系统其实已经推导出了组织并写进每一行。这张卡让任务在运行前就用同一套规则推导出组织,交给拉取使用。

What to build

  1. A job that declares no organization runs with the organization that a system-context write would resolve to (resolveSystemWriteOrganization, the one rule). The context is resolved once per run, before the pull reads or writes. ⛔ No second derivation rule. Where the rule refuses (several organizations, a wall), the run keeps today's behaviour, which is the write-time refusal; say so in the PR.
  2. Tests: a no-organization job on a single-organization install → the pull's context carries that organization, and the match lookup carries it (a pin on the query, RED on main). CONTROL: a job that declares an organization is unchanged; a multi-organization install refuses as it does today.

Surface

  • Expected file surface: packages/runtime/src/app-artifact-handlers.ts and its tests, root .changeset/. Name any other file in the PR.
  • Clause-②: no.

Related: #31812, #31778 (G9), epic #31763. Blocked-by: #31812.

Activity

  1. added
    bugSomething isn't working
    pm:queueReady for the PM dispatch loop
    domain:serverDomain lane — seat card indexed by label:pm:seat
    on Nov 9, 2026
  2. objectstack-fleet commented on Nov 9, 2026

    @objectstack-fleet
    ContributorAuthor

    triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U

    Path: none

    Triage: confirmed p2 · pm:queue → pm:blocked on #31812 · 2026-11-09T05:58Z

    ⛔ Not a claim.


    Generated by Claude Code

  3. added
    pm:blockedBlocked by another issue/PR — body carries Blocked-by: #N
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    and removed
    pm:queueReady for the PM dispatch loop
    pm:blockedBlocked by another issue/PR — body carries Blocked-by: #N
    on Nov 9, 2026
  4. objectstack-fleet commented on Nov 9, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#2 · os-steve · session_01ADHkmQKZzp4DLtqiCkZZRM

    Claim: PM loop round 19 · 2026-11-09T08:05Z
    Session: session_01ADHkmSu9QNgRmHrQYpuG2N (card thread, dev) · seat session_01ADHkmQKZzp4DLtqiCkZZRM
    Account: os-steve
    Branch: claude/project-thread-wo4lmt
    Worktree: objectstack-issue-31823
    Domain: domain:server
    Seat: domain:server#2
    File surface: packages/runtime/src/app-artifact-handlers.ts and its tests, root .changeset/; any other file named in the PR. ⛔ No packages/spec/**, no ui/**, no change to resolveSystemWriteOrganization
    Container & model: M, mode:thread, model: default tier, effort: high
    Clause-②: no
    Metadata-protocol: none
    Responsibility: step-0 finding of #31812 (from #31778 G9), filed by this seat
    Thread-read: none
    Premise: holds as filed, read at claim on origin/main 2e9121ed: jobExecutionContext(undefined) answers { isSystem: true } with no tenantId, so #31812's matchScopeOf scopes nothing on a no-organization job's pull
    Serial constraints cleared: Blocked-by #31812 is closed (PR #31846 merged 8fca8f7)

    Scope: Fixes #31823. A no-organization job's pull runs as the organization resolveSystemWriteOrganization derives (single, exactly one organization), resolved once per run; every other decision keeps today's context. Order: /mnt/project-files/server-seat2/standard-order.md + /mnt/project-files/server-seat2/31823-delta.md. Summons: the maintainer's 「server 任务很多,你应该跟进并发5」 (project chat, 2026-11-09T02:28Z).

  5. objectstack-fleet commented on Nov 9, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#2 · os-steve · session_01ADHkmSu9QNgRmHrQYpuG2N
    os-dev-report

    {
      "issue": 31823,
      "status": "done",
      "branch": "claude/project-thread-wo4lmt",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/31886",
      "head": "e31d876f",
      "session": "session_01ADHkmSu9QNgRmHrQYpuG2N",
      "premise_still_valid": true,
      "summary": "Measured on main 2e9121ed (with #31812 at 8fca8f70): a no-organization job pull ran as {isSystem:true}; its match lookup was `where external_id = ?` (SQLite SCAN) while every row was stamped org_one. The engine now answers its own system-write decision through a new public facade method ObjectQL#systemWriteOrganization() (the same facet body resolveSystemInsertOrganization now calls: posture in force, memoised probe, sys_organization registered, core's resolveSystemWriteOrganization). The runtime pull run closure asks it once per run before the pull: derived -> jobExecutionContext(derived id); refuse / no-organization-object / engine without the method -> today's context; a throwing probe -> one warn naming the job, today's context. Declared organizations, body and handler forms unchanged. 10k-row G9-shape upsert: 41,566 ms -> 8,930 ms.",
      "tests": "head e31d876f. runtime job-pull suite 33/33 green (new: 10 fake-engine cases + 3 real ObjectQL+SqlDriver+protocol+runImport cases); objectql system-write-organization.test.ts 46/46 (6 new). RED on main: runtime src restored from 2e9121ed -> Tests 3 failed | 30 passed (derived context; probe warn; real-engine one-organization: expected [{isSystem:true}] to equal [{isSystem:true,tenantId:'org_one'}]). Ablation via scripts/ablation-replace.mjs (anchor `return decision?.kind === 'derived' ? decision.organizationId : undefined;` -> `return undefined;`, x1->x0, blob 0175ea79->6b12f08f): Tests 2 failed | 31 passed; restored blob == HEAD, git diff HEAD empty; runtime suite imports source, no dist leg. Pre-merge full: objectql build+typecheck, runtime typecheck, objectql test 580 files / 11942 tests, runtime test 566 files / 9171 passed 19 skipped, VERDICT command-exit 0. Post-merge (origin/main b6fa08e3): turbo build ./packages/** 81/81, both typechecks, scoped suites 46 + 74 green; on e31d876f runtime typecheck + job suites 74/74 green.",
      "gates": "dispatch-gates --commands on e31d876f: 72 derived families, all exit 0; --ran: 72 derived, 72 run, 0 NOT-MEASURED, 0 UNRUN. Earlier rounds: check-plugin-teardown-shape --self-test and check:dual-build-cjs-loads exit 3 until the 621a4876 fixture fetch and a full packages build; check:test-source-alias exit 1 on the edited job-pull suite (needed module-top import '@objectstack/metadata', fixed in e31d876f). eslint --no-inline-config on the 5 touched files and on packages/objectql/src + packages/runtime/src: exit 0. CI: not waited on (in_progress at report time).",
      "dogfood_battery": "n/a — no `!` in the PR title and no (narrowing) arm",
      "line_budget": "+411 / -16 across 6 files (runtime handler 75, runtime test 233, objectql facet 30, facade 27, objectql test 49, changeset 13 changed lines); under the 3000-line human-merge threshold",
      "files_changed": [
        ".changeset/31823-job-pull-organization.md",
        "packages/objectql/src/engine-middleware-and-context.ts",
        "packages/objectql/src/engine.ts",
        "packages/objectql/src/system-write-organization.test.ts",
        "packages/runtime/src/app-artifact-handlers.ts",
        "packages/runtime/src/app-artifact-handlers.job-pull.test.ts"
      ],
      "deviations": [
        "File surface widened to packages/objectql (engine.ts, engine-middleware-and-context.ts, system-write-organization.test.ts), named in the PR: the engine's assembly (ruling 2) was private, so it is made reachable through one public facade method instead of a second assembly in the runtime.",
        "Clause-②: the claim says `no`; measured on the diff it is `yes (widening)` (one new public method on @objectstack/objectql). PR body and changeset carry the measured line. Changeset also bumps @objectstack/objectql patch.",
        "No ablation of the objectql method itself; the delta's one ablation (skip the derivation, runtime) went red. The insert path refactor is covered by the existing system-write-organization insert pins (46/46).",
        "G9 timing measured with a scratch in-process harness (real engine + protocol + runImport, 10k rows), not the prod-mode os serve probe from #31778."
      ],
      "mcp_calls": "5 — all reads: issue_read get, issue_read get_comments x2, pull_request_read get_files (#31846), pull_request_read get (#31886 read-back)",
      "api_writes": "2 — relay pr_create (objectstack-ai/objectstack#31886, read-back identical 12359 bytes), this os-dev-report comment via post-stamped.mjs",
      "open_questions": [
        {
          "question": "New public name: ObjectQL#systemWriteOrganization() on @objectstack/objectql (returns core's SystemWriteOrganizationDecision). Keep it?",
          "options": [
            "A keep `systemWriteOrganization()`",
            "B rename (e.g. `resolveSystemWriteOrganization()` to mirror core's predicate; it collides in name with the imported core function inside the facet module)",
            "C drop the engine method and assemble posture/probe/registry in the runtime (a second assembly the delta forbids unless unreachable)"
          ],
          "recommendation": "A, because it is the engine's own decision under a noun-shaped accessor, and B's name already belongs to core's pure predicate."
        },
        {
          "question": "`log once at warn` on a probe failure: read as one warn per failing run (each run resolves once). Acceptable, or should it be once per job until a run succeeds?",
          "options": ["A one per failing run (as built)", "B latch per job until the next successful resolution"],
          "recommendation": "A, because a probe outage also fails the pull's writes at the engine on that run, and a latch adds state for a case already loud."
        }
      ],
      "security_findings": "none",
      "out_of_scope_findings": []
    }

    Stamped 2026-11-09T09:26Z.


    Generated by Claude Code

  6. objectstack-fleet commented on Nov 9, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#2 · os-steve · session_01ADHkmQKZzp4DLtqiCkZZRM

    ACCEPT → PR #31886 comment 6737201333 · head e31d876f · 2026-11-09T09:58Z · landing: pr_ready + automerge_enable

  7. objectstack-fleet commented on Nov 9, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#2 · os-steve · session_01ADHkmQKZzp4DLtqiCkZZRM

    落地 · 2026-11-09T10:46Z

    reading value
    PR #31886 MERGED 2026-11-09T10:46Z, merge commit a15b544d5 (ACCEPT 6737201333; TIER_DEFAULT PASS 6736693368)
    #31823 closed by the PR's Fixes #31823

    Landed:

    • A job whose pull declares no organization asks the engine once per run which organization its system writes would be stamped with (ObjectQL#systemWriteOrganization(), the decision the engine's own system insert makes) and pulls as it when the engine derives one. The import's match lookup then probes the (organization_id, key) index: a 10k-row upsert went from 41.6 s to 8.9 s. Rows land where they did.
    • Every other decision, an engine without the method, and a failing probe (one warn per run) keep { isSystem: true }. A declared organization and the body and handler forms are unchanged.

    Carried: none.

  8. added a commit that references this issue on Nov 10, 2026
    a15b544
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:serverDomain lane — seat card indexed by label:pm:seatpriority:p2Medium: important, M3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions