Skip to content

spec: judge a collection item's _packageId against its owning body at defineStack / os validate / os build, so the stamp the boot now refuses (#28534) is refused where it is written #28627

Closed

Description

@objectstack-fleet

epic:#27293#1 · sunhaolin · session_017xRBcVBKAFk1ntXSjvPt7U

Filing gate: ① a follow-up a landed review owes, finding class (b) (where a refusal fires vs where the author writes). Raised by the CONTRACT_REVIEW_TIER delta review of PR #28587 (comment 6555831347, should-fix S5); re-read by this seat. Child of #27293 (E4). ⛔ Not a claim. It is out of #28534's claim because it is a packages/spec change.

The gap

PR #28587 makes objectql's registerApp and the manifest service's residual door refuse a collection item whose body carries a _packageId other than its registering package's (collectionItemOwnerStampRefusal, packages/objectql/src/engine.ts on that branch). The refusal fires at boot or install only. defineStack, os validate and os build still accept such a body, so the author learns about it from a failed install, not where they wrote it. For permission sets the same rule already runs at build time: ADR-0086 D3's judgePermissionSetOwnerBinding (packages/spec/src/security/permission-set-owner-binding.ts:88, read at origin/main d94dd3a6) is shared by the build doors and the boot.

Work

After PR #28587 lands, add one judge for a collection item's _packageId against its owning body, in packages/spec, the way judgePermissionSetOwnerBinding is shaped. Run it at defineStack, os validate, os build and the artifact parse, and have objectql's refusal call the same judge so the two cannot disagree. Tests: a body carrying a foreign _packageId is refused by os validate with a located message; the same body without the stamp passes (control). This change is in the spec lane (Metadata-protocol: narrowed, the build-door half of the same narrowing), so it needs that lane's review.

Business reason

An AI that writes a metadata app should be told at os validate that a key does not belong in the body, not at an administrator's install.

Reader

The triage seat grades and routes it (spec lane); the epic PM of #27293 dispatches after PR #28587 lands.

Dedupe

The 500 most recent issues and PRs (read 2026-11-01T07:03Z), titles grepped for _packageId|owner stamp|owner binding: 0 hits. Control connector: 53 hits.

Activity

  1. objectstack-fleet commented on Nov 1, 2026

    @objectstack-fleet
    ContributorAuthor

    triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U

    Path: 错的必须被响亮拒绝并给处方 | none | P③

    Triage: p2 · domain:spec · area:devpath · pm:epic kept · 2026-11-01T09:55Z

    ⛔ Not a claim.


    Generated by Claude Code

  2. objectstack-fleet commented on Nov 1, 2026

    @objectstack-fleet
    ContributorAuthor

    epic:#27293#1 · sunhaolin · session_017xRBcVBKAFk1ntXSjvPt7U

    Hand-off to the lane, on the maintainer's instruction in this seat's session (2026-11-01, verbatim: 「按你的方案交给车道」). pm:epic → pm:queue in this act; the domain:* label stays. This card waited for PR #28587 (#28534), which merged as d2c487407, so it builds on the classifier on main. The card stays a sub-issue of #27293 for tracking only, and the epic seat no longer reserves it. The triage grade stands.

  3. objectstack-fleet commented on Nov 1, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:spec#4 · marchtian · session_017ihL2fi9zfJhUTubyJYb6V

    Claim: PM loop round 3 · 2026-11-01T11:49Z
    Session: session_017ihL2fi9zfJhUTubyJYb6V
    Account: marchtian
    Branch: claude/issue-28627-owner-stamp-build-door
    Worktree: objectstack-issue-28627
    Domain: domain:spec
    Seat: domain:spec#4
    Ruling-ref: the epic seat's hand-off to the lane, on the maintainer's 「按你的方案交给车道」 (6557064500); triage p2 (6556398151); the boot-door refusal merged in PR #28587 (d2c487407), whose Clause-②: no (narrowing) / Metadata-protocol: narrowed this card completes at the build doors. The pattern is ADR-0086 D3's shared judge (judgePermissionSetOwnerBinding).
    File surface: one judge module in packages/spec (shaped like security/permission-set-owner-binding.ts: an exported pure judge returning the prescription and located issue text, no envelope); its callers at defineStack (stack.zod.ts), os validate / os build (packages/cli) and the artifact parse; packages/objectql/src/engine.ts#collectionItemOwnerStampRefusal rebuilt on the judge, keeping its envelope and its registration-written-stamp exemption in objectql; tests; regenerated artefacts by repo tooling only; changesets (@objectstack/spec minor, and each other moved package). On a breach the dev stops and explains it in the report.
    Container & model: M, mode:subagent, model: TIER_DEFAULT (opus) under the quota-exhaustion exemption (maintainer 「你没有fable了,使用 opus开发完之后等契约复审」), effort: high
    Clause-②: no (narrowing)
    Metadata-protocol: narrowed
    Responsibility: an author (often an AI) who writes another package's _packageId into an item body hears it at defineStack / os validate / os build, located, instead of from a failed install | one spec judge that the build doors and objectql's registration refusal both call, so they cannot disagree | the install-door semantics PR #28587 already landed; authored and stored connector rows (#28594)
    Thread-read: none
    Premise: holds on f23e051232. collectionItemOwnerStampRefusal exists only in packages/objectql/src/engine.ts:4881 (callers in engine.ts and plugin.ts:3171, residual door). No packages/spec or packages/cli file judges _packageId. judgePermissionSetOwnerBinding (packages/spec/src/security/permission-set-owner-binding.ts:88) is read by stack.zod.ts, cli/src/utils/permission-set-name-collisions.ts, metadata-protocol and objectql: the shape to follow.
    Serial constraints: not measured at claim time; the dev reads the open-PR file lists for engine.ts, stack.zod.ts and the cli validate/build files before opening the PR.

  4. objectstack-fleet commented on Nov 1, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:spec#4 · marchtian · session_017ihL2fi9zfJhUTubyJYb6V
    os-dev-report

    {
      "issue": 28627,
      "status": "done",
      "branch": "claude/issue-28627-owner-stamp-build-door",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/28685",
      "head": "7f7161f2a6",
      "session": "session_017ihL2fi9zfJhUTubyJYb6V",
      "premise_still_valid": true,
      "summary": "One pure judge judgeCollectionItemOwnerStamp(item, type, name, owner, door) in packages/spec/src/kernel/collection-item-owner-stamp.ts (doors build/register, no envelope) plus OWNER_STAMP_JUDGED_COLLECTIONS (28 stack collections). The stack schema runs it as a superRefine beside the permission-set owner-binding gate, so defineStack, os validate, os build and the artifact parse judge once (no cli source change). objectql collectionItemOwnerStampRefusal calls the judge with door register, keeping its VALIDATION_ERROR/400 envelope, the REGISTRATION_WRITTEN_STAMPS exemption and byte-identical boot words (no pin flipped). An objectql test proves, key by key over STACK_DEFINITION_KEYS, that the build door's set equals the set registerApp refuses.",
      "tests": "at 7f7161f2a6 under the verify lock: spec test exit 0 (784 files / 23142 tests); objectql test exit 0 (513 / 9901); metadata-protocol test exit 0 (291 passed 4 skipped / 21944 passed 26 skipped); cli unit exit 0 (363 / 5162); cli integration file collection-item-owner-stamp-validate-door.test.ts exit 0 (2/2); spec+objectql+cli typecheck exit 0. New: spec kernel judge test 9, spec stack build-door test 11, objectql doors test 3, cli validate-door test 2; objectql collection-item-owner-stamp.test.ts unchanged and green. Ablation at 3df38f8d81 via ablation-replace (anchor '.superRefine(applyCollectionItemOwnerStampGate)' x1 to x0, blob 3fcb4152 to 4c4c845a), spec rebuilt, dist preflight --absent: marker absent from 588 files; the os validate test turned red (expected exit 1, got 0), control green; restored blob == HEAD, git diff HEAD empty, rebuilt, preflight: marker present in 4 files, tree clean, file green again.",
      "gates": "node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack at 7f7161f2a6: 88 commands, exit codes recorded before any pipe. 87 exit 0; pnpm check:dual-build-cjs-loads exit 3 PREREQUISITE NOT MET (seven dists outside the diff), built via turbo cache, re-run exit 0. --ran: 88 derived, 88 run, 0 NOT-MEASURED (record carries the re-run code for that gate). spec check:generated after merge: all 14 artifacts up to date. Local-only check:dev-prereqs exit 1 (35 ui/** workspace packages unbuilt in this container; not the change). PR check-runs: none reported yet at report time (in_progress); CI owns the full farm.",
      "dogfood_battery": "7f7161f2a6 · os-verify-lock VERDICT command-exit 0 · Test Files 369 passed, 3 skipped (372) · Tests 2981 passed, 28 skipped (3009) · no failure",
      "line_budget": "12 files, +824 / -12 vs main (tests 522, judge module 144, stack.zod.ts +83/-1, engine.ts +18/-11, changesets 44, generated 10)",
      "files_changed": [
        ".changeset/28627-item-owner-stamp-build-door.md",
        ".changeset/28627-item-owner-stamp-shared-judge.md",
        "packages/spec/src/kernel/collection-item-owner-stamp.ts",
        "packages/spec/src/kernel/collection-item-owner-stamp.test.ts",
        "packages/spec/src/kernel/index.ts",
        "packages/spec/src/stack.zod.ts",
        "packages/spec/src/stack-collection-item-owner-stamp.test.ts",
        "packages/spec/api-surface/kernel.json",
        "packages/spec/export-origins/kernel.json",
        "packages/objectql/src/engine.ts",
        "packages/objectql/src/collection-item-owner-stamp-doors.test.ts",
        "packages/cli/test/collection-item-owner-stamp-validate-door.test.ts"
      ],
      "deviations": [
        "Independent review: none run, per the order (no Fable quota); the PR waits for the seat's contract-review hand-off.",
        "null at the build door is refused by the item schema's own type check (invalid_type at the item's _packageId), not by the judge, because _packageId is declared z.string() and the stack refinement does not run over an inner type failure; '' and foreign strings get the judge's prescription.",
        "The objectql changeset is patch: its verdicts and words are unchanged; it carries both declaration lines, a BREAKING banner worded as declared on the spec minor, and the adr-0087 not-required marker, in the service-automation shape of #28587.",
        "The ablation ran at 3df38f8d81, before the one merge of origin/main; the merge touched no packages/spec, objectql or cli file.",
        "The ran record carries the re-run exit code for check:dual-build-cjs-loads (first pass exit 3, prerequisite)."
      ],
      "mcp_calls": "3 — mcp__github__issue_read (get, get_comments) on #28627, mcp__github__pull_request_read (get) on #28587; reads only",
      "api_writes": "2 — POST /repos/objectstack-ai/objectstack/dispatches (relay pr_create, opened #28685 as draft, no labels, no assignees); this report comment via post-stamped.mjs. git push is not a REST write.",
      "open_questions": [],
      "out_of_scope_findings": [
        "carrier: seat · noted, not filed — apps and objects bodies keep a foreign _packageId at registerApp's body seam (apps: registry.registerApp -> registerItem -> applyProtection writes only an unset key; objects: stampObjectPackage), so neither door refuses it; the objectql key-by-key test records registerApp refusing in exactly 28 collections and not in apps/objects/objectExtensions. reach: not measured through a public door (no install-local probe), so not filed; dedupe words: _packageId app owner stamp, registerApp apps stampPackage, object owner stamp",
        "carrier: seat · noted, not filed — the manifest service's residual registration judges every ARTIFACT_FIELD_TO_TYPE type not in RESIDUAL_TYPES_WITH_OWN_PATH (apps included), while the body seam does not judge apps: a read-only inference about two boot doors judging different sets; dedupe words: residual owner stamp apps",
        "carrier: seat · noted, not filed — MetadataPlugin's artifact registration (registerArtifactBodyCollections, packages/metadata/src/plugin.ts) stamps through applyProtection and does not call the judge; read-only inference, not measured; dedupe words: metadata plugin artifact _packageId stamp"
      ]
    }
  5. objectstack-fleet commented on Nov 1, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:spec#4 · marchtian · session_017ihL2fi9zfJhUTubyJYb6V

    Landed · 2026-11-01T14:41Z · PR #28685 was merged by the queue at 2026-11-01T14:41Z as e170d42438. The card closed by Fixes, and this seat strips pm:dispatched in the same act.

    What landed (Clause-②: no (narrowing)):

    • The judge. One spec judge, judgeCollectionItemOwnerStamp (@objectstack/spec/kernel), over the 28 collections the boot door judges.
    • The build door. The stack schema runs the judge, so defineStack, os validate, os build and the artifact parse refuse a foreign _packageId, located at the item's _packageId.
    • The boot door. objectql's collectionItemOwnerStampRefusal now calls the same judge. Its words are unchanged.

    Record:

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

Metadata

Metadata

Assignees

Labels

area:devpathThe road — create, dev, verify, publish/install, connect an agent, iteratedomain:specfindingpriority:p2Medium: important, M3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions