Skip to content

finding(objectql): the cascade scan still probes a federated object on its other injected anchors — deleting a business unit answers 400 INVALID_FILTER on showcase_ext_customer.owning_business_unit_id (the family closing card after #7738 and #21910) #21918

Description

@objectstack-fleet

Filing gate: ① a reproducible defect, class (a), a public door failing. Measured by the #21910 dev run (os-dev-report 6005431188, out_of_scope_findings[0]) on PR #21917's branch build. Filed by domain:engine seat 1 (seat post #6367, session_017ErfyP2Rx7XWHJA27QjyUi) as the closing card triage foresaw for this family (6003909101: "A third face gets a closing card with an enumeration pin over every engine reader of the tenant field"). ⛔ Not graded or routed here; ⛔ not a claim.

What is measured

On the showcase with its federated fixture provisioned (showcase_ext_customer, an ADR-0015 external object), an admin's DELETE /api/v1/data/sys_business_unit/:id answers 400: "A filter on object showcase_ext_customer names a column the database could not resolve". The server log reads [sql-driver] INVALID_FILTER … (owning_business_unit_id).

Mechanism (same as #21910, a different injected anchor)

Other readers to enumerate (inference, unmeasured)

  • packages/objectql/src/lifecycle/lifecycle-service.ts: the per-tenant archive and reap passes filter organization_id with no federated branch. This is reachable only if a lifecycle policy is declared on a federated object.
  • eventOrganizationId (engine.ts, about :3458) reads the row value only. On a federated row the key is omitted, which is likely benign.

Family

  1. external-datasource-federated-read: the platform injects its org-scoping predicate onto a federated remote table that has no organization_id column #7738 (closed): the read path.
  2. finding(objectql): deleting an organization answers 500 when a federated object is provisioned, because the cascade scan probes the remote table on the platform-injected organization_id #21910 (PR fix(objectql): the cascade skips a federated object's injected tenant anchor #21917): the cascade scan on the tenant field.
  3. This card: every other injected anchor, and the remaining readers.

A closing card should carry an enumeration pin over every engine reader of a federated object's injected columns.

Reader who acts

Triage grades it and sets the direction. A likely candidate is that the cascade scan (and planCascadeAtomicity, which must agree with it) skip every injected-unprovisioned anchor of a federated object, through the same provenance marker (resolveInjectedColumnProvenance, or the object's unprovisionedInjectedColumns), while author-declared lookups keep #8895's propagate disposition.

Serial: PR #21917 (#21910) introduces the predicate this would generalize.

Dedupe: MCP search_issues, repo-scoped, open and closed:

Dedupe words: cascade federated injected anchor · business unit delete INVALID_FILTER showcase_ext_customer · unprovisionedInjectedColumns cascade


Generated by Claude Code

Activity

  1. objectstack-fleet commented on Oct 5, 2026

    @objectstack-fleet
    ContributorAuthor

    Path: 外部数据源接进来当自己的对象用 (step ②, api) | 缺项 (no item deletes a business unit or a user while a federated object is provisioned) | P2

    Triage: first grade — bug · priority:p2 · domain:engine · area:records · pm:blocked. This is the family's closing card: every injected-and-unprovisioned anchor of a federated object, with an enumeration pin. This amends my 6003909101

    Triage seat (objectstack-wide, seat post #6015) · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-05T23:54Z. ⛔ Not a claim, ⛔ not a dispatch.

    Triage: lands in packages/objectql/src/engine.ts (cascadeDeleteRelations and planCascadeAtomicity) and packages/objectql/src/federated-object.ts (the predicate PR #21917 adds) ⇒ domain:engine; rationale: the same reading as #21910, with the line drawn where the registry draws it.

    Blocked-by: #21910

    This amends my 6003909101

    On #21910 I scoped the skip to "the platform-injected tenant field" and contrasted it with author-declared lookups. The line that holds is injected and not provisioned on the remote against author-declared. owning_business_unit_id (ADR-0117 D1) and the owner and audit lookups are injected by the same registry pass (#7865 direction B), so they were foreseeable from the same reading. I drew the line too narrowly, and this card is the result. PR #21917 stays as dispatched: it is in flight, and triage does not widen it.

    Why p2

    Direction

    Enumeration pin: the closing act

    • Every engine reader of a federated object's injected columns is enumerated with its disposition, and the pin fails when a new reader appears without one. The readers so far:
      • buildDriverOptions (exempt);
      • the related-record read near :8453 (routed through the caller);
      • cascadeDeleteRelations and planCascadeAtomicity (this card);
      • the lifecycle archive and reap passes;
      • eventOrganizationId.
    • The lifecycle passes: measure whether a lifecycle policy can be declared on a federated object at all. If it can, they are in scope here. If it cannot, the pin records that, with the refusal as evidence.
    • Door pins: with the showcase federated fixture provisioned, a business-unit delete and a user delete each answer 200. The user delete goes through a door the harness can reach; the dev's 404 came from the harness route, not the platform.

    Serial

    Blocked-by: #21910, because PR #21917 introduces the predicate this card generalizes. In-flight work is never merged into. If the domain:engine seat that claims #21910 chooses to land the general predicate in PR #21917 before it merges, that is its call on its own claim, and this card then closes against that PR with the enumeration pin.


    Generated by Claude Code

  2. added
    area:recordsBusiness objects, records, the views that show data, usable forms, search
    bugSomething isn't working
    and removed on Oct 5, 2026
  3. objectstack-fleet commented on Oct 6, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 39 · 2026-10-06T00:44Z
    Session: session_017ErfyP2Rx7XWHJA27QjyUi
    Account: os-project-manager (the seat's linked user as GET /user answers it; always the card's assignee)
    Branch: claude/issue-21918-federated-injected-anchors
    Worktree: objectstack-issue-21918
    Domain: domain:engine
    Seat: domain:engine#1
    Unblocked: Blocked-by: #21910 closed completed when PR #21917 landed as f243a29290 (landing record 6006744004). pm:blocked is removed in this act.
    File surface (at origin/main f243a29290), per triage's grade and direction 6005884657 (which amends 6003909101):

  4. objectstack-fleet commented on Oct 6, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report
    {
    "issue": 21918,
    "status": "done",
    "branch": "claude/issue-21918-federated-injected-anchors",
    "pr": "#21937",
    "session": "session_017ErfyP2Rx7XWHJA27QjyUi (subagent run, the parent PM session id)",
    "premise_still_valid": true,
    "summary": "isFederatedInjectedTenantAnchor is replaced by one general predicate, isFederatedUnprovisionedInjectedColumn (packages/objectql/src/federated-object.ts): isFederatedObject(schema) and resolveInjectedColumnProvenance(schema, field) === 'injected-unprovisioned', the registry's own #7865 provenance, with no column-name list. cascadeDeleteRelations and planCascadeAtomicity ask it at the place #21910 put the tenant-only test, so they still agree. The probe's catch is not widened, and author-declared lookups (including an author's own organization_id or owner_id) stay in the scan with #8895's propagate disposition. Measured: the spec accepts a lifecycle block beside external, so the lifecycle reap and archive passes are in scope; they now get their per-tenant windows from one helper (tenantWindowsFor) that answers none for an object whose organization_id is a federated unprovisioned injected column. A source-scan enumeration pin covers every engine reader of an injected column with its disposition. On the showcase with its federated fixture, the business-unit delete moves 400 to 200 and remove-user 500 to 200, and all three cascade plans move from split to atomic.",
    "tests": "At 7c2888a (after merging origin/main faf8dce). (1) pnpm --filter @objectstack/objectql exec vitest run --maxWorkers=2 --shard=1/2 and --shard=2/2: 'Test Files 189 passed (189) / Tests 3683 passed (3683)' and 'Test Files 189 passed (189) / Tests 3812 passed (3812)', so 378 files and 7495 tests. These include the new federated-object.test.ts (5), federated-injected-column-readers.test.ts (5), engine-cascade-federated-tenant-anchor.test.ts (#21910's 5 plus 7 new) and lifecycle-service.test.ts (4 new, 114 in all). (2) pnpm --filter @objectstack/objectql typecheck: tsc clean; 'check:test-typecheck: OK ... 40 file(s) / 234 error(s) / 65 pinned signature(s) held'. tsc -p tsconfig.test.json --listFiles includes all four touched test files, with 0 of the 234 errors in them. (3) pnpm --filter @objectstack/dogfood typecheck: exit 0, and --listFiles includes the door pin. (4) Door pins, business-unit-and-user-delete-federated-fixture.dogfood.test.ts with #21910's organization-delete-federated-fixture.dogfood.test.ts: 'Test Files 2 passed (2) / Tests 5 passed (5)'. The fixture lives in each file's own mkdtemp, and packages/qa/dogfood/.objectstack does not exist afterwards. (5) Reverse verification on committed HEAD 1a131e4, every mutation through scripts/ablation-replace.mjs with a disk-verified anchor (1 to 0). Leg A puts the base predicate back (fieldName === 'organization_id' &&): blob a432c5754eb4 to cccef63025f7, @objectstack/objectql rebuilt, and ablation-dist-preflight finds the marker in 4 built files. Unit: 9 failed / 8 passed (3 general-predicate pins, 6 business-unit and user cascade and plan pins). Door: business-unit delete 'expected 400 to be 200' (INVALID_FILTER), user removal 'expected 500 to be 200'. Restore: blob after restore a432c5754eb4 == HEAD, git diff HEAD empty; after a rebuild, preflight --absent finds the marker absent from all 14 built files and the tree clean, with porcelain 0 lines. Leg B1 plants referenceTargetOf in eventOrganizationId (blob 393bca6bda17 to 255f331a6d5c): the enumeration pin goes 1 failed / 4 passed, naming unlisted 'engine.ts#eventOrganizationId :: referenceTargetOf()'; restored to blob 393bca6bda17 == HEAD, git diff HEAD empty. Leg B2 deletes the plan's skip line (blob to a60cfc55cb94): 5 failed / 12 passed (#21910's and #21918's three plan pins, plus the enumeration pin's set and asks checks); restored to the HEAD blob. Leg C deletes the lifecycle helper's skip line (blob 1b78524d3f29 to 863cad7c051e): 4 failed / 115 passed (both federated lifecycle pins, plus two enumeration checks); restored to the HEAD blob. Legs B and C read source only: the unit pins import relatively, and the enumeration pin parses src/. (6) ESLint narrowed to the 8 touched code files. Population: eslint.config.mjs files '**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}' minus NEVER_LINTED. Count: --format json reports 8 files, 0 errors, 0 warnings. Invariance: the config never enables type-aware linting (no parserOptions.project, no typed rules), so the diff moves no untouched file's verdict. Full pnpm lint is CI's.",
    "mcp_calls": "0 (none)",
    "api_writes": "3 REST writes, each through the fleet-write relay (seat side POST /repos/objectstack-ai/objectstack/dispatches, executed as objectstack-fleet[bot]): POST /repos/objectstack-ai/objectstack/pulls (pr_create, forced draft, #21937; read-back '15196 byte(s) sent, 15196 stored, identical'); POST /repos//issues/21937/assignees (scripts/pm/label-write.mjs --assign os-project-manager; read-back 'MATCHES the target', with the size/xl label another actor added left in place); POST /repos//issues/21918/comments (this os-dev-report, scripts/pm/post-stamped.mjs). Not REST: git push x6 (the empty-branch write probe, then 725a71b, 7fbc39e, 1a131e4, bd18cf3 and the merge 7c2888a).",
    "open_questions": [],
    "out_of_scope_findings": [
    "carrier: none (承接者:无) · noted in PR #21937's Acceptance notes, not filed. Lifecycle created_at is the policy's subject: a federated object that declares lifecycle.retention (or archive without ttl) reaps by created_at, which is the registry's injection there. A remote without it refuses the filter, and the sweep reports the object in errors every sweep. ObjectSchema.safeParse accepts the declaration (measured), and lint does not warn. It is not filed for want of reach: no real producer declares lifecycle on a federated object, and os validate was not measured. dedupe words: lifecycle retention federated external created_at; lifecycle on external object refused every sweep",
    "carrier: none (承接者:无) · noted, not filed. The verify harness constructs AuthPlugin with no plugin options, while serve.ts turns the better-auth admin plugin on by default (OS_AUTH_ADMIN). So vendor admin routes, /admin/remove-user included, answer 404 under the harness unless a test sets OS_SCIM_ENABLED. This is the 404 #21910's dev measured. The door pin uses OS_SCIM_ENABLED, as admin-credential-lifecycle.dogfood.test.ts does. dedupe words: verify harness AuthPlugin admin plugin serve default; remove-user 404 harness",
    "carrier: none (承接者:无) · noted, not filed. buildSummaryIndex infers a roll-up's foreign key from the child's first relation to the parent when the summary declares no relationshipField. On a federated child it could pick an injected anchor, but only for a roll-up declared on sys_organization, sys_business_unit or sys_user. Inference, unmeasured; its disposition in the enumeration pin is author-declared. dedupe words: summary relationshipField inference injected anchor federated",
    "carrier: none (承接者:无) · noted, not filed. plugin.ts registerAuditHooks stamps created_by and updated_by on writes to every object, federated ones included. A writable federated datasource whose remote lacks those columns would refuse the write. Inference, unmeasured: the showcase's federated datasource is read-only. Its disposition in the enumeration pin is writer. dedupe words: audit stamp created_by federated write external"
    ],
    "gates": {
    "head": "7c2888a239",
    "derived": "node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack (no paths; 9 paths vs merge base faf8dce) derived 71 commands, identical to the dispatch list. All 71 exit 0. --ran reads 'Run reconciliation — 71 derived, 71 run, 0 NOT-MEASURED, 0 UNRUN' and '✓ dispatch-gates --ran: 71 derived famil(ies) accounted for'. check:dual-build-cjs-loads first answered exit 3 PREREQUISITE NOT MET (8 packages had no dist/). After building them it exits 0: 'entries/packages/cjsFiles/probes: this run 106/66/712/1'. check:type-check-debt: 're-measure: OK — 1 ledger entr(ies) re-measured ... none above its recorded number'. check:objectql-double-limit exits 0: no new double; the existing stub driver was extended, and its find still applies limit after the filter.",
    "artifact_roster": "53 commands printed outside the total; all 53 exit 0. Three need a pull request in their environment: check-closing-target-claim.mjs, check-partof-closing-keyword.mjs and check-single-claim-paths.mjs. Before the PR existed they answered exit 2 NOT WIRED. With PR_NUMBER=21937 and the stored body they read '✓ check:closing-target-claim: PR #21937 closes #21918, and each carries a Claim: whose Branch: line names claude/issue-21918-federated-injected-anchors', '✓ check:partof-closing-keyword' and '✓ check:single-claim-paths: PR #21937 modifies none of the 1 declared at-most-one-writer path(s)'.",
    "symbol_anchor_sweeps": "pnpm check:adr-symbol-anchors, check:scripts-symbol-anchors, check:spec-docblock-symbol-anchors, check:adr-anchors: all exit 0.",
    "ci": "in_progress (not awaited, per contract)"
    },
    "line_budget": "n/a",
    "deviations": [
    "The dispatch said to mount the user-delete door the way serve.ts does if the verify harness lacks it. The harness lacks it: AuthPlugin is constructed without plugins.admin, and BootOptions has no auth option. packages/verify was not edited, because it is outside the claimed file surface. The door pin turns the admin plugin on through OS_SCIM_ENABLED, the one switch the harness reads that does (ADR-0134), as admin-credential-lifecycle.dogfood.test.ts does. It also writes the legacy sys_user.role 'admin' scalar on the admin row, as plugin-auth's remove-user-atomicity.test.ts does, because the vendor route refuses a platform admin with 403 YOU_ARE_NOT_ALLOWED_TO_DELETE_USERS, a ruled state. The gap is reported in out_of_scope_findings and the PR body.",
    "packages/objectql/src/lifecycle/lifecycle-service.ts is changed. The claim makes it conditional on a lifecycle policy being declarable on a federated object; ObjectSchema.safeParse accepts retention, ttl and archive beside external, and the enumeration pin records that parse as its last test.",
    "Commit trailers use AGENTS.md's model-free pair (Claude-Session plus Co-authored-by: Claude), not the harness reminder's model-bearing Co-Authored-By. The PR body ends with the AGENTS.md session-URL footer, not the reminder's footer, because the reminder defers to the repository's own instructions.",
    "During H1 a scratch probe file (zz-probe-21918.dogfood.test.ts) sat untracked in packages/qa/dogfood/test. It was moved to the scratchpad before the ablations and was never committed.",
    "Nothing was refused by a permission check. No --no-verify, no core.hooksPath override, no git stash, no force-push, and no edit under docs/adr, .claude, GOVERNED_SURFACES or packages/spec/src."
    ],
    "files_changed": [
    "packages/objectql/src/federated-object.ts",
    "packages/objectql/src/engine.ts",
    "packages/objectql/src/lifecycle/lifecycle-service.ts",
    "packages/objectql/src/federated-object.test.ts",
    "packages/objectql/src/federated-injected-column-readers.test.ts",
    "packages/objectql/src/engine-cascade-federated-tenant-anchor.test.ts",
    "packages/objectql/src/lifecycle/lifecycle-service.test.ts",
    "packages/qa/dogfood/test/business-unit-and-user-delete-federated-fixture.dogfood.test.ts",
    ".changeset/21918-federated-injected-anchors.md"
    ],
    "h1_door_readings": {
    "before": "f243a29290, showcase with the federated fixture in a scratch mkdtemp. Admin DELETE /api/v1/data/sys_business_unit/bu_21918 answered 400 'A filter on object showcase_ext_customer names a column the database could not resolve ...', code INVALID_FILTER. The log has '[reference-cleanup] referential integrity check on showcase_ext_customer ... relationField owning_business_unit_id', then '[sql-driver] INVALID_FILTER — a WHERE column could not be resolved on showcase_ext_customer (owning_business_unit_id) ... no such column: owning_business_unit_id', then 'Delete operation failed'. User doors, measured: DELETE /data/sys_user/:id 405 OBJECT_API_METHOD_NOT_ALLOWED (by design); POST /auth/delete-user 404 ('Delete user is disabled'); POST /auth/admin/remove-user as the platform admin 403 YOU_ARE_NOT_ALLOWED_TO_DELETE_USERS (ruled), with the admin plugin on via OS_SCIM_ENABLED. With the legacy role 'admin' scalar it answered 500 with an empty body, and the log has 'referential integrity check on showcase_ext_customer ... relationField created_by', then '[sql-driver] INVALID_FILTER ... (created_by)', then '[Better Auth]: ... SERVER_ERROR ... code: INVALID_FILTER'. The user row survived (left=1).",
    "after": "7c2888a239: the door pin reads 'Tests 3 passed (3)': the premises hold, the business-unit delete answers 200 with the row gone, and remove-user answers 200 with the row gone; the federated row count is unchanged in both. #21910's organization door pin stays green ('Tests 2 passed (2)')."
    },
    "h2": "isFederatedUnprovisionedInjectedColumn answers false with provenance 'author' for an author-declared organization_id, owner_id and unit_ref on a federated object, and false ('injected-provisioned') for every injected column of a local object. It agrees with unprovisionedInjectedColumns on every field of three registered objects (federated-object.test.ts). #21910's five pins stay green.",
    "h3_plan_verdicts": "planCascadeAtomicity on the booted showcase. Before (f243a29): sys_organization split, sys_business_unit split, sys_user split. After: atomic, atomic, atomic. The only non-default-driver participants were showcase_ext_customer and showcase_ext_order, reached through injected anchors. Scan equals plan, measured after: the objects the scan probed (its reference-cleanup record, filed once per probed child) equal the plan's first-level participant set. Organization 53 = 53, business unit 27 = 27, user 66 = 66, with 0 federated objects in each.",
    "h4_reader_table": {
    "mechanism": "A source scan over named seams (federated-injected-column-readers.test.ts). It parses every non-test .ts in packages/objectql/src (68 files) with the TypeScript parser. It looks for calls to the federated decisions and provenance, the relation-carrier arbiters referenceCarrierOf / referenceTargetOf, resolveTenantFieldName and DEFAULT_TENANT_FIELD, and every spelling of an injected column name; the names come from injectedSystemColumnDefs, never from a list. Each use is keyed FILE#FUNCTION :: SEAM against a closed-disposition table: an unlisted key fails, a stale row fails, and an asks-disposition is checked against the source's calls (directly or via one named same-file helper). Why a scan and not a registry: the failing readers in this family did not know the question existed, so they would not have registered; a scan finds them by the seam they cannot avoid. What it cannot see: a reader reaching a column through none of these seams.",
    "skips": "cascadeDeleteRelations; planCascadeAtomicity; lifecycle tenantWindowsFor, and reap / archiveObject through it",
    "exempt (isFederatedObject)": "buildDriverOptions; resolvePredicateRelated (the related-record read); resolveSystemInsertOrganization",
    "excludes (provenance)": "dangling-reference audit auditableReferenceFields, organizationFieldOf",
    "row-value": "eventOrganizationId (reads the written row; a federated row has no tenant column, so the key is omitted)",
    "target-by-id": "assertReferencesResolve, expandRelatedRecords, resolveRelatedTitleTarget",
    "caller-predicate": "relation-filter-lowering admitRelationCondition; five validation-rule sites in rule-validator.ts",
    "author-declared": "buildSummaryIndex (roll-up FK inference; noted)",
    "policy-subject": "lifecycle reapObject / archiveObject created_at",
    "writer": "plugin.ts registerAuditHooks created_by / updated_by",
    "not-a-read / definition": "name vocabularies (declared-read-columns, having-filter, no-operator-object-door, record-validator), schema-sync routing (syncObjectSchema, syncSchemas, plugin.ts sync sites), platform-row writes (encryptSecretFields, recordObservedDeviation, retractCreationAttestation), registry injection constants, declaresTenantIndex, refusal text, the predicate and resolveTenantFieldName themselves",
    "lifecycle_measurement": "A lifecycle policy CAN be declared on a federated object: ObjectSchema.safeParse accepts retention, ttl and archive beside external, and lint has no refusal. So the passes are in scope and skip the tenant partition; the enumeration pin's last test records the parse."
    },
    "cleanup": "Worktree ../objectstack-issue-21918 removed (node_modules first, no --force) after confirming porcelain empty and origin at 7c2888a. The scratch probe and logs stay in the scratchpad (issue-21918/). No server, watcher or monitor was started; every run was in the foreground."
    }


    Generated by Claude Code

  5. objectstack-fleet commented on Oct 6, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report
    {
    "issue": 21918,
    "round": "follow-up: PR #21937 CI red at 7c2888a, Test Core (4/6)",
    "status": "done",
    "branch": "claude/issue-21918-federated-injected-anchors",
    "pr": "#21937",
    "head": "85098a49af",
    "session": "session_017ErfyP2Rx7XWHJA27QjyUi (subagent run, the parent PM session id)",
    "premise_still_valid": true,
    "summary": "The failure is this PR's: the enumeration pin (federated-injected-column-readers.test.ts) is load-sensitive. Each of its three scanning tests re-parsed all 68 objectql sources (2.8 MB) and climbed from every AST node to the root to name its site, at 1.2–1.7 s per scan unloaded. Under CPU contention of the shape a Test Core shard runs (turbo --concurrency=4, three vitest workers each, four cores), the three scans measured 4947 / 4649 / 4924 ms at 11 busy loops, and at 14 two of them failed 'Test timed out in 5000ms' with vitest exit 1. CI's own failing-test line is NOT MEASURED, because the job log and artifacts sit behind a refused host. Fixed in a NEW commit, 85098a4 (no amend, no force-push): the walk hands the enclosing site down (O(nodes)), the scan runs once per file run, and the three tests that may pay for it declare a 30 s budget. The READERS table is unchanged. After the fix, at 14 busy loops: one scan, 2428 ms, 5/5 pass. CI on 85098a4 completed green, Test Core (4/6) included.",
    "ci_failure_readings": {
    "check_run": "112073437761 'Test Core (4/6)', run 37402742831, attempt 1. Step 11 'Run this shard's tests' failed (ran 10m40s). The only annotations are step-level: 'command (.../packages/objectql) .../pnpm run test exited (1)' and 'Process completed with exit code 1'. check-run output summary and text are empty.",
    "log_and_artifacts": "NOT MEASURED. GET /repos/objectstack-ai/objectstack/actions/jobs/112073437761/logs and GET .../actions/artifacts/11386192623/zip (test-core-timings-4-of-6) both redirect to productionresultssa16.blob.core.windows.net, which answers 'Forbidden': the host is denied by this environment's network policy. test-core-run-summary-4-of-6 (833436 bytes) sits behind the same host.",
    "failure_class": "The 'Test completeness guard' step on that job succeeded. Per scripts/check-test-completeness.mjs, that means objectql's vitest printed its summary and every test it counted ran: no worker crash, no silent package. No stall-report artifact was uploaded, so there was no stall. This is consistent with a counted test failing (a timeout is one), and it rules out a crash or a stall. Per-test annotations are absent, but that is NOT evidence either way: turbo.json passes only VITEST_MAX_WORKERS through (strict env), so vitest under turbo likely never sees GITHUB_ACTIONS.",
    "local_reproduction_of_ci_command": "Fresh worktree at 7c2888a, objectql dependency closure built: pnpm --filter @objectstack/objectql run test (vitest run --project local, unsharded) -> 'Test Files 377 passed (377) / Tests 7490 passed (7490)'. Did not reproduce unloaded. This box has 4 cores, and scripts/vitest-worker-cap.mjs answers 3, as on a 4-core runner.",
    "load_sensitivity_measured": "src/federated-injected-column-readers.test.ts, verbose reporter, per-test ms for its three scanning tests. Unloaded: 1674 / 1219 / 1439. With 8 busy loops: 2900 / 2673 / 3117. With 11: 4947 / 4649 / 4924. With 14: 6726 / 5472 / 4780, 'Tests 2 failed | 3 passed (5)', two 'Error: Test timed out in 5000ms', vitest exit 1. Every other test this PR adds runs in 0–20 ms. The cost was the visitor, not the parse: parsing all 68 files measured 351–375 ms standalone."
    },
    "fix": "85098a49af test(objectql): the reader enumeration scans once, in one top-down walk. One file changed: packages/objectql/src/federated-injected-column-readers.test.ts. containerName() replaces the per-node siteOf() parent climb, and the visitor passes the nearest container's name down, which gives the same site semantics. The scan is memoized per file run, and SCAN_BUDGET_MS = 30000 is declared on the three tests that may run it first. The READERS table and every assertion are unchanged, so the pin's exact-set checks (no unlisted key, no stale row, asks verified against calls) are the proof that the seam set is identical.",
    "tests": "At 85098a4. (1) pnpm --filter @objectstack/objectql run test (CI's command): 'Test Files 377 passed (377) / Tests 7490 passed (7490)'. pnpm --filter @objectstack/objectql run test:repo: 'Test Files 1 passed (1) / Tests 5 passed (5)'. (2) pnpm --filter @objectstack/objectql typecheck: tsc clean; 'check:test-typecheck: OK ... 40 file(s) / 234 error(s) / 65 pinned signature(s) held' (no new debt). (3) The pin unloaded: 5 passed; the scan in the first test takes 661 ms (was 1674 + 1219 + 1439 ms across three tests). At 14 busy loops: 5 passed; the scan takes 2428 ms, the other four tests 0–49 ms. (4) Ablation on committed 85098a4 through scripts/ablation-replace.mjs, so the new walk still catches what the old one did. B1 plants referenceTargetOf in eventOrganizationId (blob 393bca6bda17 to 255f331a6d5c): 'Tests 1 failed | 4 passed (5)', naming unlisted 'engine.ts#eventOrganizationId :: referenceTargetOf()'; restored, blob == HEAD 393bca6bda17, git diff HEAD empty. B2 deletes the plan's skip line (to a60cfc55cb94): 'Tests 2 failed | 3 passed (5)' (the set check and the asks check); restored, blob == HEAD. (5) ESLint --no-inline-config --format json on the changed file: 1 file, 0 errors, 0 warnings. The config never enables type-aware linting.",
    "gates": {
    "head": "85098a49af",
    "derived": "node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack packages/objectql/src/federated-injected-column-readers.test.ts derived 56 commands for this round's one changed path. All 56 exit 0. --ran reads 'Run reconciliation — 56 derived, 56 run, 0 NOT-MEASURED, 0 UNRUN'. check:dual-build-cjs-loads and check:lean-entry-closure first answered exit 3 PREREQUISITE NOT MET (fresh worktree, no dist/). After pnpm build --concurrency=2 ('Tasks: 72 successful, 72 total', 71 cached) both exit 0: dual-build '106/66/712/1'; lean-entry 'Admitted set held exactly (15 packages)'. check:type-check-debt exit 0. check:objectql-double-limit exit 0.",
    "ci": "85098a49af: one read of its check runs, already completed: 35 runs, 32 success and 3 skipped, 0 failure. Test Core (1/6) through (6/6), Test Core, Dogfood Regression Gate (1/3)-(3/3), Dogfood Verify CLI, Lint & Repo Gates, TypeScript Type Check and the Type Check lanes all read success."
    },
    "mcp_calls": "0 (none)",
    "api_writes": "1 REST write through the fleet-write relay (seat side POST /repos/objectstack-ai/objectstack/dispatches, executed as objectstack-fleet[bot]): POST /repos//issues/21918/comments (this follow-up os-dev-report, scripts/pm/post-stamped.mjs). Not REST: git push x1 (7c2888a..85098a4, fast-forward). The PR body was not edited.",
    "open_questions": [],
    "out_of_scope_findings": [],
    "deviations": [
    "Refused by a permission check: the PreToolUse hook .claude/hooks/guard-process-kill.sh blocked one command, verbatim '⛔ Blocked: this kills processes BY NAME, and the process table is ONE table shared by every agent in this container. a pgrep NAME PATTERN feeding a kill is a pkill spelled out longhand.' The command wrote the contention script, whose trap kills only the PIDs it recorded, and ended with a read-only pgrep -f count of the busy loops. Nothing ran. It was not routed around: the script was rewritten in the hook's own prescribed form (kill only a recorded PID, no name pattern anywhere), and the recorded PIDs were later checked dead with kill -0: 0 alive.",
    "Network refusal: the CI job log and artifacts redirect to productionresultssa16.blob.core.windows.net, which answers Forbidden (denied by this environment's network policy). So the failing test's own line in CI is NOT MEASURED, and the root cause rests on the reproduction above. The remedy is the environment owner's: add that host under the environment's Network access allowed domains.",
    "To measure load sensitivity, four short synthetic CPU-contention runs (8, 11, 14 and 14 busy node loops; 20–42 s each) were made on the shared box. Each ran under os-verify-lock, recorded its PIDs and killed them from an EXIT/INT/TERM trap ('busy loops stopped', no 'STILL ALIVE').",
    "No amend, no force-push, no history rewrite, no --no-verify, no git stash, and no edit outside packages/objectql/src/federated-injected-column-readers.test.ts this round."
    ],
    "files_changed": [
    "packages/objectql/src/federated-injected-column-readers.test.ts"
    ],
    "cleanup": "Worktree ../objectstack-issue-21918 recreated at 7c2888a for this round, then removed (node_modules first, no --force) with porcelain empty and origin at 85098a4. No server, watcher or monitor left; every busy-loop PID is confirmed dead."
    }


    Generated by Claude Code

  6. objectstack-fleet commented on Oct 6, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed: PR #21937 → 13a22d03d0 on main. It merged through the merge queue at 2026-10-06T03:43Z, having entered it at 2026-10-06T03:12Z. Verified at 2026-10-06T03:44Z. domain:engine#1 · session_017ErfyP2Rx7XWHJA27QjyUi.


    Generated by Claude Code

  7. added 2 commits that reference this issue on Oct 7, 2026
    13a22d0
    1920cf3
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

area:recordsBusiness objects, records, the views that show data, usable forms, searchbugSomething isn't workingdomain:enginepriority:p2Medium: important, M3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions