Skip to content

metadata-protocol: refuse at the save door a tenant overlay that changes a packaged validation rule's condition or type (ruling C′ / B on #27949, the save-door half) #28448

Closed

Description

@objectstack-fleet

domain:server#1 · os-tesla · session_01L9qibz3ZUbZ8Qcn1rDw9yc

Filing gate: ③ the maintainer dispatched this card directly. At 2026-10-31T15:47Z Tesla answered 「同意」 to batch #433. Its item 3 is ruling B on #27949, record 6545641964: "the save-door half is a follow-up card queued behind the protocol.ts serial queue (#6367), Blocked-by: the landing PR". ⛔ Not a claim. No grade is set: triage sets it (the parent is p2).

Blocked-by: #28445

Business reason. Ruling C′ (6524229156) sets the boundary. A tenant may reword or switch off a packaged validation rule, but the rule's condition and type belong to the package. Once #28445 lands, the overlay load path reports such a copy and runs the package's condition. That keeps an upgrade from failing on stored data, but the admin who saved the change gets no answer when they save. This card makes the save door say so, loudly and with the prescription, at the moment the admin writes it.

Reader who acts: domain:server seat 1 (os-tesla), which holds #27949.

Wanted

  1. A metadata save of a tenant overlay object is refused when it carries a same-named copy of a validation rule contributed by the base package or an extender and changes that rule's condition or type. The refusal comes with C′'s prescription: "to change the condition, add a rule under a new name, or change it in the package".
  2. Use the predicate fix(objectql)!: the registry fold merges validation rules by name; a tenant's same-named copy carries only its message and active, and a changed condition is reported at load, never run #28445 lands and leaves uncalled (SchemaRegistry.tenantRuleCopyRefusal, packages/objectql/src/registry.ts), so the load report and the save refusal judge one way.
  3. A copy that changes only message or active saves. A rule under a new name saves, since D9.2a keeps it additive.
  4. Pins: condition change refused, type change refused, reword saves, active: false saves, and a new name saves. Add one ablation per refusal.

Where: the metadata save door's refusal set in packages/metadata-protocol/src/ (protocol-save-door-refusals.ts holds its siblings). If the change touches protocol.ts itself, it waits its turn in that file's serial queue.

Contract: Clause-②: no (narrowing: a save that succeeds today is refused), so contract review is owed. Metadata-protocol: narrowed; the dev confirms from the diff. No spec key.

Dedupe: none found in the parent's thread or its rulings (6524229156, 6545641964). This is the half they name.

Links: #27949, PR #28445, ruling C′ 6524229156, ruling B 6545641964, ADR-0029 D9.2a.

Activity

  1. added
    area:recordsBusiness objects, records, the views that show data, usable forms, search
    domain:serverDomain lane — seat card indexed by label:pm:seat
    and removed on Oct 31, 2026
  2. objectstack-fleet commented on Oct 31, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#1 · os-tesla · session_01L9qibz3ZUbZ8Qcn1rDw9yc

    Claim: maintainer-direct card (filing gate ③), unblocked by #28445 · card thread · 2026-10-31T20:41Z
    Session: session_01L9qibz3ZUbZ8Qcn1rDw9yc (the seat). The card thread is the #27949 thread, session_01L9qibx3A7AeHSnSr39oxaV.
    Account: os-tesla (the seat's linked user; the card's assignee)
    Branch: claude/project-thread-9ii7md, restarted from origin/main (its #27949 work merged as 50015d84)
    Worktree: the card thread's own container checkout
    Domain: domain:server
    Seat: domain:server#1
    Ruling-ref: 6545641964 (letter B, Tesla 「同意」 2026-10-31T15:47Z), under C′ 6524229156
    File surface: the metadata save door's refusal set in packages/metadata-protocol/src/ (⛔ not protocol.ts while #28451 holds it), registry.ts only for an export, their tests, and a .changeset/28448-*.md. ⛔ No packages/spec.
    Container & model: S, mode:thread, model: opus, effort high; contract-review tier before the queue
    Clause-②: no (narrowing)
    Metadata-protocol: narrowed
    Thread-read: 6545641964
    Premise: the predicate tenantRuleCopyRefusal is on main and not called yet; the dev confirms this first.
    Serial constraints: protocol.ts is held by #28451 (seat 2, draft). This card does not edit it, or it stops and queues.
    Resume-from: none (fresh from origin/main)
    Order: /mnt/project-files/server-seat-1/r2026-10-31/dispatch-28448.md (project file, not public)

    State: pm:queue → pm:dispatched, assigned to os-tesla, recorded by this claim.

  3. objectstack-fleet commented on Oct 31, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#1 · os-tesla · session_01L9qibx3A7AeHSnSr39oxaV
    os-dev-report

    Report at 2026-10-31T22:11Z for PR #28478, head 5b370a28. Contract review is owed (Clause-②: no (narrowing), Metadata-protocol: narrowed).

    {
     "issue": 28448,
     "status": "done",
     "branch": "claude/project-thread-9ii7md",
     "pr": "https://github.com/objectstack-ai/objectstack/pull/28478",
     "session": "session_01L9qibx3A7AeHSnSr39oxaV",
     "premise_still_valid": true,
     "summary": "Branch restarted from origin/main 52306d40 (after #28445 merged as 50015d84). The save door now refuses a tenant object's same-named copy of a packaged or extender validation rule that changes any package-owned key (C′), with 422 INVALID_METADATA, one issue (rule validation-rule-copy-changed) at the copy's first changed key, and the prescription as hint. The judge is SchemaRegistry.tenantRuleCopyRefusal, the predicate #28445 left uncalled and the load report reads, reached as a host fact of the runtime authoring gate (packagedRuleCopyRefusalOf in protocol-authoring-gate.ts, findPackagedRuleCopyGaps in runtime-authoring-gate.ts). That gate is already called from active save, publish, batch publish, draft-publish verdict and restore, so protocol.ts is untouched. Reword, active:false, new name and drafts still save. Clause-② no (narrowing) and Metadata-protocol narrowed: contract review owed.",
     "tests": "head 5b370a2809: new runtime-authoring-gate.validation-rule-copy.test.ts 4/4 (pure gate); new objectql protocol-validation-rule-copy-save-refusal.test.ts 5/5 (real SchemaRegistry + real save door: condition refused, type refused, reword saves, active:false saves, new name saves). Sibling suite metadata-protocol vitest Test Files 287 passed / 4 skipped, Tests 21879 passed / 26 skipped. metadata-protocol + objectql typecheck exit 0. 4 ablations via scripts/ablation-replace.mjs over the 5-case objectql file under os-verify-lock: B1 host never asks (dist marker ablation-b1, preflight proven) 2 failed; B2 gate drops finding (dist marker ablation-b2) 2 failed; B3 type not package-owned in registry predicate 1 failed (type pin); B4 condition not package-owned 1 failed (condition pin). All restored, dist rebuilt, final tree clean.",
     "gates": "local: dispatch-gates --commands on 5b370a2809 derived 69 over 6 paths; 69 run with stdin /dev/null, 69 exit 0 (list: server-seat-1/r2026-10-31/out/28448-gates-ran-5b370a2809.list); --ran: 69 accounted, 0 NOT-MEASURED, exit 0. On the previous head f78357758b check:engine-double-contract and check:objectql-double-limit were red on the new test's double (ledger row; find ignoring limit), fixed in 5b370a2809. Scoped lint (root lint command over packages/metadata-protocol and the new objectql test) exit 0. CI on the PR not yet read at report time.",
     "dogfood_battery": "5b370a2809 · os-verify-lock VERDICT command-exit 0 (held 1209s) · Test Files 362 passed / 3 skipped (365) · Tests 2922 passed / 28 skipped (2950) · no failures",
     "line_budget": "474 changed lines (+473 / -1), 6 files, under the 3000 threshold",
     "files_changed": [
      ".changeset/28448-save-door-validation-rule-copy.md",
      "packages/metadata-protocol/src/protocol-authoring-gate.ts",
      "packages/metadata-protocol/src/runtime-authoring-gate.ts",
      "packages/metadata-protocol/src/runtime-authoring-gate.validation-rule-copy.test.ts",
      "packages/objectql/src/protocol-validation-rule-copy-save-refusal.test.ts",
      "scripts/engine-double-contract.pinned.json"
     ],
     "deviations": [
      "The dispatch named the save-door refusal set (protocol-save-door-refusals.ts). Its members are reached only through calls in protocol.ts, so the rule sits in the runtime authoring gate, a sibling refusal-set file protocol.ts already calls on every door. No protocol.ts line was needed, so nothing to queue behind #28451.",
      "scripts/engine-double-contract.pinned.json is outside the named file surface: the gate asks for the --write ledger row for the new test's engine double (3 rows added, 0 lost).",
      "registry.ts was not edited (the predicate was already reachable on engine.registry); it was mutated only for ablations B3/B4 and restored."
     ],
     "mcp_calls": "2 — reads only: issue_read get, issue_read get_comments (#28448); 0 writes",
     "api_writes": "2 — relay pr_create (run 41246969694, POST pulls → #28478) and this os-dev-report comment (POST issues/28448/comments); git push is not a REST write",
     "open_questions": [],
     "out_of_scope_findings": []
    }
  4. objectstack-fleet commented on Oct 31, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#1 · os-tesla · session_01L9qibz3ZUbZ8Qcn1rDw9yc

    Landing record · #28448 · PR #28478

    domain:server seat 1 · session_01L9qibz3ZUbZ8Qcn1rDw9yc · 2026-10-31T23:12Z

    Reading Probe Result
    Merged GET pulls/28478 merged, f5e69532
    Ancestry git merge-base --is-ancestor f5e69532 origin/main yes
    Content git grep -c tenantRuleCopyRefusal in metadata-protocol/src/runtime-authoring-gate.ts on origin/main present (2)

    Release: claim 6548869850 (session_01L9qibz3ZUbZ8Qcn1rDw9yc, branch claude/project-thread-9ii7md) · landed, card closed.

    • State: pm:dispatched removed, assignee cleared.
    • Resume-from: none (the branch merged; nothing unpushed).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:recordsBusiness objects, records, the views that show data, usable forms, searchdomain:serverDomain lane — seat card indexed by label:pm:seat

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions