Skip to content

attach-requires-parent-edit c3: #4757 multi-delete pin can't execute against the prebuilt @objectstack/core (stale-dist / CI) #7668

Closed

Description

@huangyiirene

Symptom

All 17 cases in packages/services/service-storage/src/attachment-access-hooks.test.ts error with:

TypeError: withoutOperationPrivateKeys is not a function

The predicate-less unscoped-multi-delete refusal (#4757) cannot be expressed over REST (deleteMany with no ids/where → 400 before the hook is reached), so it rests on this unit pin — which cannot run in the checkout, leaving #4757 unguarded by an executable test here.

Root cause

Located — a build-ordering / CI test-infra gap, not a runtime defect. withoutOperationPrivateKeys is exported on origin/main at packages/core/src/security/operation-private-keys.ts:115 (and re-exported from packages/core/src/security/index.ts:145). The test resolves a stale built @objectstack/core that predates that export; the source is correct and the live server enforced every other gate in this item. The fix is a CI/build-ordering one: rebuild @objectstack/core before running the service-storage suite (or give the pin a live oracle).

Reproduction

Against the prebuilt tree, run attachment-access-hooks.test.ts → 17/17 error TypeError: withoutOperationPrivateKeys is not a function. Confirm git grep withoutOperationPrivateKeys origin/main -- packages/core/src/security/operation-private-keys.ts shows the export exists → the source is not missing the function; the built dist is stale.

Source

Extracted from the QA run #7635 (framework 92f26f7, console 6314e87f).

Activity

  1. claude commented on Aug 11, 2026

    @claude
    Contributor

    Dual-label resolution (queue+finding): promoted to pm:queue (finding stripped). Concrete, scoped CI/build-ordering fix with a named landing site — the #4757 pin's missing symbol is a stale prebuilt @objectstack/core, and withoutOperationPrivateKeys is verified exported at packages/core/src/security/operation-private-keys.ts:115 on origin/main @ 6a9dec6. domain:devx stands (test-infra/build ordering, not runtime code). No open duplicate found; #7671 cross-references this card as the owner.

    本评论来自分诊座位 Routine(#5474 试点),不构成认领。


    Generated by Claude Code

  2. self-assigned this
    on Aug 11, 2026
  3. huangyiirene commented on Aug 11, 2026

    @huangyiirene
    CollaboratorAuthor

    Claim: PM loop round 4 (devx seat, 2026-08-11 term) — wave 4 unit B
    Session: session_01GZKbx4xyF7U5WXj6ch49BM
    Branch: claude/issue-7668-storage-test-build-order
    Worktree: cloud session, own clone
    Domain: domain:devx
    File surface: the build-ordering fix face — expected turbo.json (test task deps) or the service-storage package's test config; the dev derives the true landing site and declares it in the PR (⛔ no product code, no checklist area files). If the right fix is instead a live oracle for the #4757 pin, that changes the surface into packages/services/service-storage test code — report the fork before implementing it.
    Container & model: M, mode:cloud, model: opus
    Serial constraints cleared: no other claim or open PR touches turbo.json or the service-storage test infra (checked at claim time). ⚠️ turbo.json is a shared hot file — merge origin/main before opening the PR.


    Generated by Claude Code

  4. huangyiirene commented on Aug 11, 2026

    @huangyiirene
    CollaboratorAuthor

    premise_still_valid: true — reproduced from a clean checkout at origin/main @ 7a8476f before touching anything.

    Root cause (my derivation, not the LEAD's guess)

    Neither candidate in the brief is the defect. turbo.json already declares "test": { "dependsOn": ["^build"] }, and pnpm turbo run test --filter=@objectstack/service-storage builds core first and passes 352/352 on unmodified main. turbo.json is untouched by this PR.

    The real cause is one directory down:

    packages/services/service-storage ships no vitest.config.ts, so @objectstack/core resolves through the workspace link to packages/core/dist/index.js — a build artifact. The verdict of every unit pin in the package is a function of build state, not of the source in the checkout.

    attachment-access-hooks.test.ts is the only executable guard on the #4757 predicate-less unscoped-multi-delete refusal (unexpressible over REST — deleteMany with no ids/where is rejected 400 before the hook). Against a prebuilt tree whose core dist predated the export it errored TypeError: withoutOperationPrivateKeys is not a function, while packages/core/src/security/operation-private-keys.ts:117 was correct throughout — matching the issue's diagnosis of stale dist, but locating the fixable seam in the package's test resolution rather than in task ordering.

    The loud error is the mild half. A core dist merely behind rather than missing the symbol lets a pin run green against core's old behaviour — a passing test not testing the checkout, with nothing in the output saying so.

    Why ordering can't fix it: turbo already orders correctly, so turbo run test was never the failing path. What broke are the paths turbo does not mediate — pnpm test in the package, vitest run <file>, an editor runner, a QA tree built at an older commit (how #7635 hit it). Those are exactly where a pin is re-run while someone is changing core. No dependsOn edit reaches them.

    PR

    #7778 (draft, base main, Fixes #7668) — branch claude/issue-7668-storage-test-build-order.

    Landing site

    2 new files, no product code and no test assertions changed:

    • packages/services/service-storage/vitest.config.ts — aliases @objectstack/core → packages/core/src/index.ts (anchored regex, array form, per the service-knowledge precedent; 10 packages already do this). Graph-wide aliasing keeps a single core instance for the deps still on dist; tsup externalizes workspace deps so none inline one.
    • .changeset/service-storage-tests-pin-core-to-source.md — patch.

    turbo.json not modified → the hot-file conflict risk in the claim does not apply. git merge origin/main was already up to date at push.

    Reproduction / verification (commands + results)

    1. Repro, clean tree, no dists — cd packages/services/service-storage && npx vitest run src/attachment-access-hooks.test.ts → Error: Failed to resolve entry for package "@objectstack/core" at attachment-access-hooks.ts:3.

    2. turbo exonerated — pnpm turbo run test --filter=@objectstack/service-storage → 24 files / 352 passed, before any change.

    3. Decisive A/B — stripped the export from the built packages/core/dist/index.js (source untouched) to recreate the exact attach-requires-parent-edit c3: #4757 multi-delete pin can't execute against the prebuilt @objectstack/core (stale-dist / CI) #7668 condition:

      core dist vitest.config.ts result
      stale present (PR) 30/30 pass
      stale absent (pre-PR) 17 failed / 13 passed

      The 17 reproduce the issue's count exactly, with its verbatim TypeError: withoutOperationPrivateKeys is not a function. So the config is demonstrably what closes the hole — installAttachmentAccessHooks does not authorize an UNSCOPED multi-delete: no id + no where reads as "nothing to authorize" and deleteMany runs over the whole table #4757 now has a pin a build artifact cannot silence.

    4. Regression sweep (dist restored): bare npx vitest run in the package (the previously-broken path) → 352 passed; turbo run test → 352 passed; eslint on the new file → clean; pnpm check:published-files → ✓; pnpm check:empty-changeset → ✓.

    Out-of-bounds findings — reported only, not fixed

    • The same hazard shape exists wherever a package with unit tests imports @objectstack/core and ships no vitest config. Fixed only the package this issue names; a repo-wide sweep — or a lint gate asserting the invariant — deserves its own issue.
    • service-storage's other runtime imports (@objectstack/spec/*, observability, platform-objects/*, objectql, types) still resolve to dist, so the suite still needs a build for those. Correct today (turbo orders it); no observed defect justifies widening the alias set now.

    Not touched: no merge, no auto-merge, no merge queue, no content/docs/releases/**, no git stash. CI convergence and the ready flip are PM's. The known base red check:platform-checklist → coverage.json · qa: UNCLASSIFIED (#7347) is unrelated and was not pursued.


    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

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions