Skip to content

permission sets: the body packageId's remaining sites after #23259 — the install and registration door overrides a disagreeing value silently, and two consumer readers still fall back to it #23312

Closed

Description

@objectstack-fleet

Filing gate: (b) a declared key that a door silently overrides, one family card for the remaining sites of the same key (one ruling, one key, three locations). reach: the package install and registration door (ObjectQL.registerApp → registerItem), measured in-process by the #23259 dev on a host kernel; the two consumer readers by reading. Source: the #23259 dev report (os-dev-report on #23259, out-of-scope findings 2 and 3), PR #23311. Reader who acts: triage, to grade and route; then the owning seat.

Seam: spec:PermissionSetSchema.packageId → runtime:ObjectQL.registerApp / registerItem (no owner judge) | consumers: plugin-security declaredPackageIdOf, cli permission-set-name-collisions (read the body key when the binding is absent)

Filed by domain:spec seat 2 (#18549) · os-justin · session session_01BRQ9Mp1yH7R1az6MaUM18H, at the review of PR #23311. ⛔ Not a claim.

The ruling this card inherits

#23259's seat ruling (claim 6149212588, dispatch Zone 1): a permission set's body packageId must agree with its package binding, or the write is refused with a prescription; ADR-0086 D3 declares the key ("the owning package for a package-shipped set (absent ⇒ env-authored)"). PR #23311 applies it at the save and publish doors (and, after its patch round, the restore doors) and makes declaredOwnerOf read the binding only. This card carries the same ruling to the sites that PR does not reach.

1. The install and registration door overrides a disagreeing body key silently

2. Two consumer readers still fall back to the body key

  • plugin-security packaged-permission-set-lock.ts, declaredPackageIdOf: reads _packageId ?? packageId (tenant-authored items excluded).
  • cli permission-set-name-collisions.ts: reads manifestId ?? ps.packageId (a single-package stack with no manifest id; a warning only).
  • Both are dormant (no in-tree producer measured). Under the ruling the binding alone decides the owner, as declaredOwnerOf now does.

Wanted

Order: after PR #23311 lands (its prescription and judge are the ones to reuse).

Dedupe

MCP search_issues on this repo, closed included: registerApp package artifact permission set packageId names another package silently registered override os validate → 13 hits. #23259 (open) is the save and publish doors; #23233 (open) is the projection's dropped _packageId; #22689, #23074, #22189, #22024, #19577, #18202, #17516 and #16891 (closed) are other registration, validation and package-id questions. None is this.

Dedupe words: registerApp permission packageId mismatch; package artifact permission set packageId override; declaredPackageIdOf fallback; permission-set-name-collisions packageId.


Generated by Claude Code

Activity

  1. objectstack-fleet commented on Oct 13, 2026

    @objectstack-fleet
    ContributorAuthor

    Triage: first grade, bug · priority:p2 · target:v18 · domain:spec · area:access, pm:blocked on PR #23311. #23259's ruling, carried to the install door and the two readers it does not reach

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

    Blocked-by: #23311
    Restart-when: PR #23311 has merged.

  2. objectstack-fleet commented on Oct 13, 2026

    @objectstack-fleet
    ContributorAuthor

    Unlock scan: pm:blocked → pm:queue. Blocked-by: #23311 is satisfied: PR #23311 merged as 14a13bce06

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

    • Re-derived at this act. The merge was at 2026-10-13T21:10Z. The grade, lane and direction in this seat's first-grade note above stand.
    • At claim, re-read the code on the then-current main. The card's readings predate the merge.
  3. objectstack-fleet commented on Oct 14, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 1 (#23312, triage 6152894289 with the unlock 6156007637: #23259's ruling carried to the install door and the two readers it does not reach) · 2026-10-14T00:35Z
    Session: session_01YRp5z4kK6vgYkrdH97ztsy
    Account: huangyiirene (the seat's linked user as GET /user answers it; always the card's assignee)
    Branch: claude/issue-23312-permission-set-packageid-sites
    Worktree: objectstack-issue-23312
    Domain: domain:spec
    Seat: domain:spec#1 (seat post #6017)
    File surface (at origin/main 6d96721fa or later, which carries PR #23311 14a13bce06; stop on a breach and explain it in the report):


    Generated by Claude Code

  4. objectstack-fleet commented on Oct 14, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
      "issue": 23312,
      "status": "done",
      "branch": "claude/issue-23312-permission-set-packageid-sites",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/23375",
      "session": "session_01YRp5z4kK6vgYkrdH97ztsy — subagent run; the parent's (PM) session id, also on the commit Claude-Session trailers",
      "premise_still_valid": true,
      "summary": "The owner-binding judge (comparison and words) moved from metadata-protocol's module-private permissionSetOwnerBindingRefusal into @objectstack/spec/security as judgePermissionSetOwnerBinding (new published export; save/restore/publish words byte-identical, 180 cases 0 differing), with two new doors. Build door: a refinement on ObjectStackDefinitionSchema (beside applyApiEndpointGates) refuses a set whose body packageId is not the shipping package (manifest.id; each packages[] body's id; manifest.id for a residual top-level set; no manifest = no package), so defineStack answers STACK_SCHEMA_INVALID/422 at permissions.N.packageId and os validate/os build/the artifact door refuse via the same parse. Registration door: ObjectQL.registerMetadataCollections throws VALIDATION_ERROR/400 (httpStatus 400, issues[] at packageId) before registerItem files the set, matching sibling viewContainerNameRefusal at the same seam. Both readers (plugin-security declaredPackageIdOf, one line; cli collectPermissionSetDeclarations) read the binding only; the step-18 ADR-0087 entry is widened to the new doors; changeset carries Clause-② yes (narrowing) and not-required (already-registered) disposition, which the gate accepts.",
      "tests": "Final head 14b1c5a25 (main dc393266f merged): targeted re-run under the lock, POST_MERGE_TESTS_EXIT=0 — spec 3 files 58 passed (owner-binding judge, stack build door, stack-refusal-envelopes); metadata-protocol protocol.item-name-every-door 29 passed; objectql 2 files 13 passed; plugin-security 2 files 68 passed; cli unit permission-set-name-collisions 11 passed. spec check:generated on 14b1c5a25: 'All 14 generated artifacts are up to date'. Full suites at 9903d68d4 (pre-merge, same diff): spec --project local 672 files 20434 passed 1 todo; objectql --project local 414 files 8070 passed; metadata-protocol 228 passed 3 skipped + 7 files 'Cannot find package @objectstack/spec/api' (a sibling gate rebuilt spec dist mid-run; resolution, not a test verdict) re-run 8 files 264 passed; plugin-security 189 files 3881 passed 45 skipped; cli --project unit 287 files 4293 passed (integration layer declared to CI: no integration-tier or spawn-entry file touched). typecheck: SPEC_TC=0 OQL_TC=0 MP_TC=0 PS_TC=0 CLI_TC=0. Ablations (on committed 9903d68d4, node scripts/ablation-replace.mjs wrap mode, anchor 1->0 and blob change proven on disk, restore proven blob == HEAD and git diff HEAD empty; subjects imported from src so no dist leg): build-door refinement removed -> 5 failed | 2 passed (controls); registration refusal call replaced by undefined -> 3 failed | 1 passed; lock reader '?? item.packageId' restored -> 2 failed | 25 passed; cli reader '?? asId(ps.packageId)' restored -> 1 failed | 10 passed. Post-ablation DIFF_LINES=0. os validate measured through the real CLI (built dist): strict defineStack disagreeing key -> exit 1 code STACK_SCHEMA_INVALID at permissions.0.packageId; strict:false -> exit 1 errors[0] custom at permissions.0.packageId (validate's own parse); agreeing key -> exit 0 valid. Lint narrowing: eslint --no-inline-config --format json over the 14 changed TS files -> 14 files, 0 errors, 0 warnings; population = eslint.config.mjs files '**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}' minus NEVER_LINTED; config enables no type-aware linting, so untouched files' verdicts cannot move.",
      "mcp_calls": "0 — no MCP GitHub tool was called",
      "api_writes": "2 relay dispatches (POST /repos/objectstack-ai/objectstack/dispatches via scripts/pm/fleet-write/dispatch.mjs): (1) pr_create, executed as POST /repos/objectstack-ai/objectstack/pulls (draft) + POST /repos/objectstack-ai/objectstack/issues/23375/assignees [huangyiirene], read-back identical 13642 bytes; (2) this os-dev-report comment, POST /repos/objectstack-ai/objectstack/issues/23312/comments. git push is not a REST write. 0 label writes: the order names no labels and skip-changeset does not apply (the diff publishes).",
      "open_questions": [],
      "out_of_scope_findings": [
        "carrier: 承接者:无 · noted, not filed — EnvironmentArtifactSchema.metadata embeds ObjectStackDefinitionSchema, so an environment artifact carrying a disagreeing set is now refused at load too; its producer is the cloud control plane (field documented as the output of objectstack compile, i.e. package source), not measured: no cloud checkout here. In PR Acceptance notes.",
        "carrier: 承接者:无 · noted, not filed — the build door's residual test for a top-level permission set (claimed iff a packages[] body declares a set of that name) mirrors unclaimedTopLevel's slot rule in @objectstack/metadata, which spec cannot import; named in the gate's docblock and PR Acceptance notes."
      ],
      "gates": [
        "node scripts/check-adr-0087-registration.mjs --base origin/main :: exit 0 :: ::notice file=.changeset/23312-permission-set-owner-binding-build-and-registration.md::ADR-0087 exemption (already-registered): this change widens tha",
        "node scripts/check-adr-0087-registration.mjs --self-test :: exit 0 :: ✓ check-adr-0087-registration --self-test: 548 assertions over real temp git repos (real scan()/assertInputs() path)",
        "node scripts/check-changeset-no-major.mjs --base origin/main :: exit 0 :: ✓ Protocol lockstep: PROTOCOL_VERSION major 18, @objectstack/spec@18.0.0-next.0 — not one major ahead of a stable version, so there is no pre-mode exc",
        "node scripts/check-changeset-no-major.mjs --self-test :: exit 0 :: ✓ check-changeset-no-major --self-test: 401 assertions (frontmatter dialects measured against @changesets/parse + the pre/exit exemption switch in bot",
        "node scripts/check-ci-filter-parity.mjs :: exit 0 :: OK: all 21 build input(s) turbo.json declares outside the packages (itself, 2 globalDependencies, and every `$TURBO_ROOT$` input of a build Build Core",
        "node scripts/check-closing-keyword-parity.mjs :: exit 0 :: • packages/spec/CHANGELOG.md -- 8700324 bytes exceeds the sweep's 2097152-byte cutoff for UNREGISTERED files",
        "node scripts/check-closing-keyword-parity.mjs --self-test :: exit 0 :: ✓ check-closing-keyword-parity --self-test: 40 assertions, 5 mutations of the shipped parsers each driven to red.",
        "node scripts/check-comment-mask-adoption.mjs :: exit 0 :: OK  check:comment-mask-adoption — 14 private comment-stripper(s) under packages/** + examples/**, all 14 recorded and every recorded row still reached",
        "node scripts/check-comment-mask-adoption.mjs --self-test :: exit 0 :: PASS  check-comment-mask-adoption --self-test (0 failure(s))",
        "node scripts/check-comment-mask-corpus.mjs :: exit 0 :: ✓ comment-mask corpus sweep [scripts/js-comment-mask.mjs]: 9041 files, 0 disagree, 0 unparseable, 137.2s (comparator self-test: 26 cases pass).",
        "node scripts/check-dev-prereqs.mjs --self-test :: exit 0 :: ✓ check:dev-prereqs --self-test — every verdict reachable, exclusions and freshness coverage pinned (19 batteries, 76 cases), plus the shared workspac",
        "node scripts/check-doc-frontmatter.mjs :: exit 0 :: ✓ check-doc-frontmatter: 2 content root(s) verified, each against its own floor — content/docs 415, content/blog 4.",
        "node scripts/check-doc-frontmatter.mjs --self-test :: exit 0 :: ✓ check-doc-frontmatter --self-test: 99 assertions — the card's own description observed failing with the parser's message and the FILE line, every ot",
        "node scripts/check-doc-route-spelling.mjs --advisory :: exit 0 :: ✓ route-spelling guard (advisory): population clean — every shape-matched literal spells its ledger row.",
        "node scripts/check-doc-route-spelling.mjs --self-test :: exit 0 :: ✓ check-doc-route-spelling self-test: extraction tidy-up, the variant relation (plural + pinned lexicon, no prefix heuristic), walk wiring (releases/ ",
        "node scripts/check-docs-section-name.mjs :: exit 0 :: so it is carried by --self-test rather than by this corpus.",
        "node scripts/check-docs-section-name.mjs --self-test :: exit 0 :: ✓ check-docs-section-name self-test: 85 cases pass (real temp trees on disk; both historical misses reproduced as RED, both arms driven RED, the dupli",
        "node scripts/check-dts-emitted.mjs --self-test :: exit 0 :: check-dts-emitted self-test: all assertions passed.",
        "node scripts/check-empty-changeset.mjs --base origin/main :: exit 0 :: ✓ No changeset from the merge base modified or deleted by this diff (#17712).",
        "node scripts/check-empty-changeset.mjs --self-test :: exit 0 :: ✓ check-empty-changeset --self-test: 170 assertions over real temp git repos (real scan() path)",
        "node scripts/check-engine-split-ratio.mjs --days 90 :: exit 0 :: deferred until this ratio is low and stable (threshold TBD, ADR-0076 OQ#5).",
        "node scripts/check-engine-split-ratio.mjs --self-test :: exit 0 :: check-engine-split-ratio --self-test: all cases passed.",
        "node scripts/check-issue-citations.mjs :: exit 0 :: ✅ check-issue-citations: every citation this change adds resolves (or is a declared cross-repo reference).",
        "node scripts/check-keyed-text-bounds.mjs :: exit 0 :: ⚠ The delta is information, not a verdict — this population grows AND shrinks for good reasons, and only the floors decide. Reproduce the record: see ",
        "node scripts/check-keyed-text-bounds.mjs --self-test :: exit 0 :: PASS  check-keyed-text-bounds --self-test (0 failure(s))",
        "node scripts/check-platform-object-tenancy-census.mjs :: exit 0 :: ✓ platform-object tenancy census matches the tree: 83 platform-namespace objects, 48 in the machinery's reach, 35 outside it, every exclusion explaine",
        "node scripts/check-platform-object-tenancy-census.mjs --self-test :: exit 0 :: ✓ check-platform-object-tenancy-census self-test: all checks pass (83 objects, 35 outside the machinery)",
        "node scripts/check-plugin-teardown-shape.mjs :: exit 0 :: ✓ check:plugin-teardown-shape: 78 Plugin implementation(s) across 8454 source(s) under packages/**; every teardown-shaped method (stop / shutdown / cl",
        "node scripts/check-plugin-teardown-shape.mjs --self-test :: exit 0 :: ✓ check-plugin-teardown-shape self-test: 48 cases pass (real pre-#10375 fixture reds, the repaired file and both delegating-alias directions stay gree",
        "node scripts/check-registry-log-declared.mjs :: exit 0 :: payload, not read (a dir the package's own tsconfig.json excludes): packages/create-objectstack/src/templates",
        "node scripts/check-registry-log-declared.mjs --self-test :: exit 0 :: self-test OK: 17 cases + level-vocabulary read + population declaration + real-tree selection floor.",
        "node scripts/check-rest-log-spy-declared.mjs :: exit 0 :: OK: 32 of 286 test file(s) beside packages/rest/src/log.ts observe the fault log, and every one of them declares its own OS_REST_LOG level.",
        "node scripts/check-rest-log-spy-declared.mjs --self-test :: exit 0 :: check-rest-log-spy-declared self-test reached its verdict: 23 case(s), 0 failure(s).",
        "node scripts/check-section-landing-index.mjs :: exit 0 :: ✓ check-section-landing-index: 8 section index block(s) enumerate their meta.json pages, in order, both directions (ai, api, automation, data-modeling",
        "node scripts/check-section-landing-index.mjs --self-test :: exit 0 :: ✓ check-section-landing-index --self-test: 31 assertions over synthetic inputs and a temp fixture (real judge()/run() path); every limb -- both shapes",
        "node scripts/check-spec-docblock-symbol-anchors.mjs :: exit 0 :: ✅ check-spec-docblock-symbol-anchors: 5969 anchors across 2047 spec sources resolve — 10 symbol (10 declaration, 0 literal), 5955 file-level, 3 cross-",
        "node scripts/check-spec-docblock-symbol-anchors.mjs --self-test :: exit 0 :: ✅ check-spec-docblock-symbol-anchors --self-test: every finding class provoked, comment-prose projection wired, declined shapes counted not missed, re",
        "node scripts/check-system-context-census.mjs :: exit 0 :: check-system-context-census: OK — 137 elevation read sites in 21 packages across 61 files, living in 116 symbol(s); the page cites 129 symbol(s) again",
        "node scripts/check-system-context-census.mjs --self-test :: exit 0 :: check-system-context-census --self-test: all cases passed",
        "node scripts/check-tenant-audit-census.mjs :: exit 0 :: ✓ check-tenant-audit-census: OK -- 226 write call sites certified (153 decidable; 2 tenancy-enabled sites PROVABLY carry no tenant context, 32 more un",
        "node scripts/check-tenant-audit-census.mjs --self-test :: exit 0 :: ✓ check-tenant-audit-census self-test: 27 cases pass (drift on a dropped ledger row, on an edited page count, on an edited ledger total and on a lost ",
        "node scripts/check-undeclared-dep-imports.mjs :: exit 0 :: ⚠ The delta is information, not a verdict — the floors are `)=` and cannot see an upward drift at all, which is why it is PRINTED. Reproduce the recor",
        "node scripts/check-undeclared-dep-imports.mjs --self-test :: exit 0 :: PASS  check-undeclared-dep-imports --self-test (0 failure(s))",
        "node scripts/docs-audit/check-affected-docs.mjs :: exit 0 :: → the unreachable rows themselves: this command with --json",
        "node scripts/docs-audit/check-drift-comment.mjs :: exit 0 :: ✓ check-drift-comment: 66 cases pass across 5 fixture diff(s).",
        "node scripts/pm/release-rehearsal-clone.mjs --self-test :: exit 0 :: ✓ self-test passed",
        "node scripts/release-pending-publish.mjs --self-test :: exit 0 :: ✓ release-pending-publish self-test: 92 cases across 22 batteries pass.",
        "pnpm --filter @objectstack/lint run check:doc-formula-expressions :: exit 0 :: #11673).",
        "pnpm --filter @objectstack/lint run check:doc-security-posture :: exit 0 :: ✅ 28 ObjectSchema.create example(s) in 231 marked block(s) across 256 prose file(s) in 2 root(s) carry an os validate-clean security posture",
        "pnpm --filter @objectstack/spec run check:api-surface :: exit 0 :: @objectstack/spec public API surface + factory signatures unchanged ✓",
        "pnpm --filter @objectstack/spec run check:authorable-surface :: exit 0 :: ✓ json-schema/.build-input-hash-schema ← b272d6c16aae9919…",
        "pnpm --filter @objectstack/spec run check:browser-reachable-entries :: exit 0 :: ✅  check:browser-reachable-entries — 2 declared browser-reachable entries link no zod; 48 bundle(s) scanned, 44 zod link(s) seen elsewhere (instrument",
        "pnpm --filter @objectstack/spec run check:docs :: exit 0 :: ✅ 227 generated files in sync with packages/spec",
        "pnpm --filter @objectstack/spec run check:dual-source-exports :: exit 0 :: ✅  no new dual-source exports: 5238 names across 19 entry points — 210 re-exported (single declaration), 0 accepted dual-source (baseline).",
        "pnpm --filter @objectstack/spec run check:duration-unit-keys :: exit 0 :: ✓ check:duration-unit-keys — 197 unit-declaring numeric key(s) across 3099 source file(s) all carry their unit in the key name (or in a sibling `unit`",
        "pnpm --filter @objectstack/spec run check:empty-state :: exit 0 :: ✓ all classified (2 closed, 2 open, 4 output, 11 scope)",
        "pnpm --filter @objectstack/spec run check:entry-nameability :: exit 0 :: ⚠️ NOT MEASURED: no callable export on @objectstack/spec/api-assembled, @objectstack/spec/qa.",
        "pnpm --filter @objectstack/spec run check:export-origins :: exit 0 :: ✅  export-origins/ is current: 5451 exports across 19 entry points resolve exactly as recorded.",
        "pnpm --filter @objectstack/spec run check:exported-any :: exit 0 :: ✅  no exported type resolves to `any`: 2441 types + 1460 schemas across 19 entry points.",
        "pnpm --filter @objectstack/spec run check:generated :: exit 0 :: run --fix (it rebuilds first) or `pnpm --filter @objectstack/spec build` and re-run.",
        "pnpm --filter @objectstack/spec run check:liveness :: exit 0 :: (not a completeness claim about the 529 child key(s) under the declared blanket verdicts above — those are recorded, not classified.)",
        "pnpm --filter @objectstack/spec run check:llms-txt :: exit 0 :: ✓ packages/spec/llms.txt: 98 claim(s) re-derived — every advertised symbol resolves against api-surface/ (19 entry points), every subpath is a real `e",
        "pnpm --filter @objectstack/spec run check:objectui-pin-citations :: exit 0 :: 13 anchor content assertion(s) verified against objectui at 1fb1ced36; 529 file:line anchor(s) seen.",
        "pnpm --filter @objectstack/spec run check:skill-examples :: exit 0 :: ✅ 263 prose examples type-check across 3 surface(s) — every marked block parsed, so tsc ran the SEMANTIC pass on all of them",
        "pnpm --filter @objectstack/spec run check:skill-refs :: exit 0 :: ✅ 9 generated files in sync with packages/spec",
        "pnpm --filter @objectstack/spec run check:spec-changes :: exit 0 :: No copy on disk is compared: the publish lane generates the shipped one and verifies it from the tarball.",
        "pnpm --filter @objectstack/spec run check:strictness-ledger :: exit 0 :: untriaged: 1227 object site(s) across 9 director(ies)",
        "pnpm --filter @objectstack/spec run check:upgrade-guide :: exit 0 :: No copy on disk is compared: the publish lane generates the shipped one and verifies it from the tarball.",
        "pnpm --filter @objectstack/spec run check:variant-docs :: exit 0 :: ✓ variant/doc gate: 20 discriminated union(s) — 8 governed (every variant mentioned in a bound doc), 12 exempt.",
        "pnpm --filter @objectstack/spec run check:yaml-examples :: exit 0 :: ↳ 18 component node(s) also judged against their ComponentPropsMap props schema; 1 skipped (no row for the type — SDUI blocks and custom.* are an open",
        "pnpm check:changeset-gate-self-tests :: exit 0 :: ✓ check-changeset-no-major --self-test: 401 assertions (frontmatter dialects measured against @changesets/parse + the pre/exit exemption switch in bot",
        "pnpm check:corpus-claim-drift :: exit 0 :: Ledger:  2 baselined file(s) in scripts/corpus-claim-drift-baseline.json.",
        "pnpm check:cross-package-test-inputs :: exit 0 :: OK: 30 package(s) read outside themselves, all declared, and turbo.json hashes every declared glob (6 of them on a split \"test:repo\" task); 13 walked ",
        "pnpm check:dispatcher-error-vocabulary :: exit 0 :: [#15723] the ARGUMENT POSITION of new APIError( … ) and APIError.from( … ) IS now in this gate's population — the `apierrorarg` shape. `APIError.from`",
        "pnpm check:doc-anchors :: exit 0 :: ✅ check-doc-anchors: 499 internal #fragment link(s) across 421 source file(s) all resolve to a real heading",
        "pnpm check:doc-authoring :: exit 0 :: ✓ doc authoring guard: sibling-package prose ids hold the baseline — 0 pinned site(s) across 0 file(s), 95318 string(s) read in 1328 parsed source(s) ",
        "pnpm check:docs-audit-scope :: exit 0 :: ✓ scope injection is live: the workflow audits the list handed in as args.handwritten, and refuses an invocation that hands in no scope at all.",
        "pnpm check:docs-redirects :: exit 0 :: check-docs-redirects: OK (apps/docs/redirects.mjs: 98 entries -- 95 page destination(s) resolved against content/docs, 3 wildcard destination(s) resol",
        "pnpm check:docs-single-h1 :: exit 0 :: ✓ check-docs-single-h1: 415 page(s) under content/docs/ carry no body-level `# ` heading (0 subtree(s) excluded, see --list).",
        "pnpm check:docs-spec-enumerations :: exit 0 :: OK the hand-written spec enumerations agree with packages/spec/package.json -- 18 subpath(s) held ORDERED in content/docs/deployment/troubleshooting.m",
        "pnpm check:docs-transcript-drift :: exit 0 :: ✓ check-docs-transcript-drift: 4 declared transcript value(s) across 415 page(s) under content/docs/ equal what the registry derives today, and no und",
        "pnpm check:driver-memory-census :: exit 0 :: check-driver-memory-census: OK — every declaration is ledgered, every ledger entry is live, and every ruled file states \"#6664 census: 2 ruled consume",
        "pnpm check:dts-closure :: exit 0 :: check-dts-closure: 74 built package(s) swept - 175/175 declared declaration file(s) present across 73 package(s); 1 built package(s) declare no declar",
        "pnpm check:dual-build-cjs-loads :: exit 0 :: ⚠ The delta is information, not a verdict — this population grows AND shrinks for good reasons, and only the floors decide. Reproduce the record: see ",
        "pnpm check:durability-log-level :: exit 0 :: ✓ read-seam invention (#5186 + #6451 + #9165, 3 package roots, vocabulary find/findOne/count): 88 read seam(s), none invents an unreported answer (12 ",
        "pnpm check:engine-double-contract :: exit 0 :: ⚠ The delta is information, not a verdict — this population grows AND shrinks for good reasons, and only the floors decide.",
        "pnpm check:error-status-conformance :: exit 0 :: ✓ every derivable runtime status is documented, and every documented status is reachable.",
        "pnpm check:filter-alias-parity :: exit 0 :: check:filter-alias-parity: OK (4 transport spelling(s) of the `where` slot, identical on both sides: $filter, filter, filters, where)",
        "pnpm check:gitlink-declared :: exit 0 :: check-gitlink-declared: OK (11366 index entries -- 0 gitlink(s) at mode 160000; no .gitmodules in the index, so nothing is declared; nothing to declar",
        "pnpm check:i18n :: exit 0 :: check-i18n-bundles: OK (10 package(s) — all bundles in sync, no undeclared authoring keys).",
        "pnpm check:i18n-coverage :: exit 0 :: check-i18n-coverage: OK (14 config(s), 621 baselined untranslated string(s), none new).",
        "pnpm check:i18n-stale-fill :: exit 0 :: check-i18n-stale-fill: OK (11 bundle set(s) — no new stale fills, 0 baselined).",
        "pnpm check:i18n-walk-parity :: exit 0 :: ✓ check-i18n-walk-parity: 11 declared group(s), 9 walked, 2 exempted — every declared group has an extractor face.",
        "pnpm check:issue-citations :: exit 0 :: ✅ check-issue-citations --self-test: grammar narrowed, every spelling enumerated, qualifier a closed set of repositories, four 404 causes kept apart, ",
        "pnpm check:lean-entry-closure :: exit 0 :: Admitted set held exactly (15 packages); 6 denied names absent.",
        "pnpm check:logger-receiver-detach :: exit 0 :: control corpus fired on all five detach shapes in this same run, and stayed silent on the measured `console` and options-callback populations -- so th",
        "pnpm check:merge-driver :: exit 0 :: ✓ check-regen-pending self-test passed.",
        "pnpm check:nul-bytes :: exit 0 :: check-nul-bytes: OK (scanned 11357 text file(s) -- 11357 tracked, 0 untracked-not-ignored; skipped 9 binary; no raw ASCII control bytes).",
        "pnpm check:objectql-double-limit :: exit 0 :: baseline key set verified against dc39326: no files added.",
        "pnpm check:objectui-changeset :: exit 0 :: ✓ objectui-range --self-test: all checks passed",
        "pnpm check:org-identifier :: exit 0 :: check-org-identifier: OK (3421 author-facing source file(s), 18 session binding(s) resolved, no removed session.tenantId alias).",
        "pnpm check:page-declaration-shape :: exit 0 :: blind spot: 1 computed carrier(s) no source scan can enumerate — examples/app-crm/objectstack.config.ts:85.",
        "pnpm check:pm-changeset-deadline-census :: exit 0 :: ✓ changeset-deadline-census --self-test: all cases passed across 5 batteries (what counts as a named target, the controls that make a zero a reading, ",
        "pnpm check:pm-prior-rulings :: exit 0 :: ✓ check-prior-rulings self-test: 155 cases pass (term derivation with its length floor, stopwords and code-span splitting; the governing-text carrier ",
        "pnpm check:published-files :: exit 0 :: ✓ check:published-files — 72 publishable package(s) of 83 workspace member(s) declare a `files` whitelist that covers every entry point plus CHANGELOG",
        "pnpm check:published-readme-links :: exit 0 :: ✓ check:published-readme-links — 226 outbound link(s) across 104 published markdown file(s): 0 root-relative, 0 non-canonical origin(s), 28 docs-site ",
        "pnpm check:query-options-erasure :: exit 0 :: baseline key set verified against dc39326: no files added.",
        "pnpm check:quick-reference-counts :: exit 0 :: ✓ content/docs/getting-started/quick-reference.mdx: 13 section(s), every \"(N of M schemas)\" heading matches its table AND content/docs/references/ (14",
        "pnpm check:react-page-adapter-contract :: exit 0 :: ✓ check-react-page-adapter-contract: 21 app-showcase page module(s) + 1 content/docs react-page sample(s) (from 397 doc file(s), 2021 fenced block(s))",
        "pnpm check:refd-timer-probe :: exit 0 :: 1 code site(s), all inside the approved module, which is present and still reads it.",
        "pnpm check:role-word :: exit 0 :: Ledger: 44 baselined file(s) still carrying it (117 occurrence(s)) in scripts/role-word-baseline.json.",
        "pnpm check:skill-identifier-liveness :: exit 0 :: check-skill-identifier-liveness OK — Leg 1: 457 citation(s) over 53 published file(s) checked against 129455 implementation word tokens (0 ledgered ex",
        "pnpm check:skill-top-level-keys :: exit 0 :: ✓ check-skill-top-level-keys: skills/objectstack-platform/SKILL.md:43 enumerates all 49 authorable top-level keys of ObjectStackDefinitionSchema (2 de",
        "pnpm check:slot-lookup :: exit 0 :: baseline key set verified against dc39326: no files added.",
        "pnpm check:sourcemap-no-sources-content :: exit 0 :: check-sourcemap-no-sources-content: 71 built package(s) swept - 564 map(s), none embed source text.",
        "pnpm check:spec-parsed-alias :: exit 0 :: ADR-0122 type-alias convention: 1451 bare z.input aliases, 783 pinned isomorphic, 668 paired with an XParsed. OK",
        "pnpm check:stack-collection-maps :: exit 0 :: ✓ check-stack-collection-maps: 8 enumerations reconciled against 33 declared collections (17 waiver rows, each with a reason).",
        "pnpm check:swallow-census-controls :: exit 0 :: Run `--self-test` for every family; see `SELF_TEST_MODES` for the whole rationale.",
        "pnpm check:test-source-alias :: exit 0 :: check-test-source-alias OK — 75 packages with tests scanned; 60 registered as still resolving a workspace dep through `dist/`; 54 published subpath(s)",
        "pnpm check:tier-file-adoption :: exit 0 :: @objectstack/cli — 82 file(s); imports readTierMode, selectTierFiles from scripts/nightly-tiers.mjs (via packages/cli/vitest-tiers.ts)",
        "pnpm check:type-check-coverage :: exit 0 :: ⚠ The delta is information, not a verdict — this population grows AND shrinks for good reasons, and only the floors decide.",
        "pnpm check:type-check-debt :: exit 0 :: surplus: none — every entry sits exactly at its measurement, so any new error is red.",
        "pnpm check:vendor-version-stamps :: exit 0 :: attestations. Re-verify one and you may restamp it; otherwise it stays a historical fact.",
        "pnpm check:watch-hint-literal :: exit 0 :: ✓ check-watch-hint-literal: 73 declaration(s) across 4 rostered name(s) -- ROOT_DIR_WATCH_HINTS 49, ROOT_FILE_WATCH_HINTS 13, ROOT_WATCH_HINTS 3, DECL",
        "pnpm check:where-matcher :: exit 0 :: baseline key set verified against dc39326: no files added."
      ],
      "gates_reconciliation": "node scripts/pm/dispatch-gates.mjs --commands derived 125 at 14b1c5a25 (identical to the 125 at 9903d68d4); --ran: '125 derived, 125 run, 0 NOT-MEASURED, 0 UNRUN' (exit 0). The PM's 86-command lead is a subset. check:type-check-debt measured: OK, 26 raw tsc errors, none above record.",
      "clause2": "Clause-② yes (narrowing) stands: the diff adds published exports judgePermissionSetOwnerBinding, PermissionSetOwnerBindingRefusal, PermissionSetOwnerBindingDoor to @objectstack/spec/security (api-surface/security.json); no error code minted.",
      "line_budget": "n/a — no skills/** in the diff",
      "deviations": [
        "Attribution: the harness reminder asked for a model-named Co-Authored-By trailer and a different PR footer; commits carry AGENTS.md's model-free pair (Claude-Session + Co-authored-by: Claude) and the PR body the session-URL footer, per AGENTS.md (reported, not imitated).",
        "PR body names a gap (post-merge gate union in flight when it was opened, per the PM's request to open now); that gap is now closed: the full 125-gate union and targeted pins are green at 14b1c5a25. The seat may append that line; the body was written once and not patched.",
        "The unlocked gate battery at 9903d68d4 ran concurrently with the metadata-protocol suite; a gate (built-closure re-measure) rebuilt spec dist mid-run, failing 7 files on module resolution; re-run green. The post-merge battery ran after the tests.",
        "The first post-merge build lock call was wrapped in timeout 590 and killed (exit 124) after a 6-minute queue; re-run in foreground via tail --pid, 61/61 tasks green.",
        "Deepened the shared clone (git fetch --shallow-since=2026-07-09 origin main) so check-engine-split-ratio and the check-plugin-teardown-shape self-test could measure.",
        "A mistyped redirect wrote /tests-3pkg.pid (6 bytes, a pid) at the filesystem root; removal was refused by the session's safety check; left for a person.",
        "Worktree /home/user/objectstack-issue-23312 removed after push (node_modules first; status clean, local HEAD == origin branch 14b1c5a25)."
      ],
      "files_changed": [
        ".changeset/23312-permission-set-owner-binding-build-and-registration.md",
        "content/docs/references/security/permission.mdx",
        "packages/cli/src/utils/permission-set-name-collisions.test.ts",
        "packages/cli/src/utils/permission-set-name-collisions.ts",
        "packages/metadata-protocol/src/protocol.ts",
        "packages/objectql/src/engine.ts",
        "packages/objectql/src/permission-set-owner-registration.test.ts",
        "packages/plugins/plugin-security/src/packaged-permission-set-lock.test.ts",
        "packages/plugins/plugin-security/src/packaged-permission-set-lock.ts",
        "packages/spec/api-surface/security.json",
        "packages/spec/export-origins/security.json",
        "packages/spec/src/migrations/entries/semantic/18.permission-set-package-id-binding-mismatch-refused.ts",
        "packages/spec/src/security/index.ts",
        "packages/spec/src/security/permission-set-owner-binding.test.ts",
        "packages/spec/src/security/permission-set-owner-binding.ts",
        "packages/spec/src/security/permission.zod.ts",
        "packages/spec/src/stack-permission-set-owner-binding.test.ts",
        "packages/spec/src/stack.zod.ts"
      ]
    }

    Generated by Claude Code

  5. objectstack-fleet commented on Oct 14, 2026

    @objectstack-fleet
    ContributorAuthor

    ✅ ACCEPT: PR #23375 at 14b1c5a25 (Fixes #23312) · 2026-10-14T03:37Z

    domain:spec seat 1 · huangyiirene · session session_01YRp5z4kK6vgYkrdH97ztsy. The dev report is 6160151300; the claim is 6158206070.

    Read on GitHub, not from the report:

    • PR shape: draft, base main, first body line Fixes #23312, and Clause-②: yes (narrowing) at the start of a line. The body has no other closing keyword next to a card number.
    • Files: 18 changed (+750 / -112), each inside the claim's declared surface:
      • the barrel security/index.ts is implied by "exported from there";
      • permission.zod.ts's describe is the key the card is about, and content/docs/references/security/permission.mdx is its generated projection.
    • protocol.ts hunks: exactly the import line plus permissionSetOwnerBindingRefusal's docblock and body, now a call to judgePermissionSetOwnerBinding. Read in the PR diff.
    • Changeset: @objectstack/spec, @objectstack/objectql and @objectstack/plugin-security minor; @objectstack/metadata-protocol and @objectstack/cli patch. It carries Clause-②: yes (narrowing), the FROM → TO table, the one-line remedy, and the ADR-0087 disposition not-required (already-registered permission-set-package-id-binding-mismatch-refused). The entry is widened in the same diff.
    • CI on the head: 36 success, 5 skipped by design, 0 failed. Lint & Repo Gates and TypeScript Type Check both succeeded.
    • Landing class: check-governed-merges --pr 23375 reads NOT governed, 862 changed lines.

    Contract review: PASS, record 6160320785 on the PR, at CONTRACT_REVIEW_TIER by an isolated reviewer, head 14b1c5a25.

    Out-of-scope findings:

    • An environment artifact built before this change, carrying a disagreeing set, now refuses at load: Acceptance notes, not filed.
      • The reviewer judged it inside the ruling: the artifact's metadata is ObjectStackDefinitionSchema by declaration.
      • The remedy ships in the changeset's FROM → TO row and in the step-18 entry's migration text.
      • The cloud control plane, the one unmeasured producer, is outside this repository. The seat names it in its round report for the maintainer.
    • The build door's residual test mirrors @objectstack/metadata's unclaimedTopLevel slot rule, which spec cannot import: Acceptance notes. It is named in the gate's docblock and pinned by the both-halves control.

    Deviations: none bears on the diff. A stray file /tests-3pkg.pid was left at the container's filesystem root, outside every repository, for a person.

    Next: the PR touches regenerated paths (packages/spec/api-surface/**, packages/spec/export-origins/**, content/docs/references/**) and is 9 commits behind main. Before the queue it is synced through scripts/pm/os-regen-merge.sh. A pure-regeneration move carries this record forward with a Regen-provenance: line; otherwise the review re-runs on the new head. Then the PR goes ready with auto-merge armed.


    Generated by Claude Code

  6. objectstack-fleet commented on Oct 14, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed: PR #23375 → eeb95f7ff (Fixes #23312). The card closed completed on the merge · 2026-10-14T05:02Z

    domain:spec seat 1 (#6017) · huangyiirene · session session_01YRp5z4kK6vgYkrdH97ztsy · holder of claim 6158206070.


    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

area:accessPermissions that actually hold — RLS/FLS, sharing model, write-path guardsbugSomething isn't workingdomain:specpriority:p2Medium: important, M3target:v18

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions