Skip to content

runtime: enabling a package whose validation rule reads a field of a disabled package is accepted, and every write to that object then answers 400 #30756

Closed

Description

@objectstack-fleet

domain:server#4 · os-elon-musk · session_016WV4czvZykUqhYxYXSSDt4

Found while building #30379 (ruling A, record 6670643454), outside its scope. Measured by the #30379 dev on a local branch over main 6ef7f583, with a harness pin. ⛔ Not a claim.

What happens

Ruling A on #30379 makes the disable door refuse while an enabled package's validation rule reads a field that the package being disabled adds through objectExtensions. The mirror order is not guarded:

  1. Disable package X, which adds field f to object O. Nothing reads f yet, so the disable is accepted.
  2. PATCH /packages/<P>/enable for a package P whose validation rule on O reads f is accepted.
  3. From then on, every PATCH /data/O/:id answers 400 VALIDATION_FAILED, because the rule validator fails closed on the withheld field.

A boot that replays such a package-state file lands in the same state.

Expected (to be ruled or graded)

The enable door answers this as ruling A answers the disable: a refusal naming package · object · rule, with the #30724 error code. Alternatively, the maintainer rules a different answer for enable. The boot replay path needs the same reading.

Related

Suggested grade: p3, domain:server. Serial: after the #30379 code PR, which adds the scan this card would reuse.

Activity

  1. objectstack-fleet commented on Nov 6, 2026

    @objectstack-fleet
    ContributorAuthor

    triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U

    Path: none

    Triage: audit · p1 · domain:server · 2026-11-06T00:52Z

    ⛔ Not a claim.


    Generated by Claude Code

  2. added
    domain:serverDomain lane — seat card indexed by label:pm:seat
    pm:queueReady for the PM dispatch loop
    priority:p1High: required for production / M2
    on Nov 6, 2026
  3. objectstack-fleet commented on Nov 6, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#4 · os-elon-musk · session_016WV4czvZykUqhYxYXSSDt4

    Parked · 2026-11-06T02:43Z

    Blocked-by: #30379

    The enable-door refusal reuses the scan and the #30724 error code that the #30379 code PR adds to the disable door. That code PR is built (on a local branch in the #30379 card thread) and opens once #30724 lands. When #30379 closes, domain:server#4 takes this card into the same card thread, which holds the scan. State: pm:queue → pm:blocked. The p1 grade stands.

  4. added
    pm:blockedBlocked by another issue/PR — body carries Blocked-by: #N
    and removed
    pm:queueReady for the PM dispatch loop
    on Nov 6, 2026
  5. objectstack-fleet commented on Nov 6, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#4 · os-elon-musk · session_016WV4czvZykUqhYxYXSSDt4

    Unblocked · 2026-11-06T16:13Z · Blocked-by: #30379 is cleared: #30379 closed completed when PR #30825 merged (1de7def003). That PR adds the scan and the PACKAGE_DISABLE_RESTRICTED_BY_VALIDATION_RULE code this card's enable door reuses (readDisableDependentRules in packages/runtime/src/domains/packages.ts). Moved pm:blocked → pm:queue, unclaimed: server seat 4 is signing off.

  6. added
    pm:queueReady for the PM dispatch loop
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    and removed
    pm:blockedBlocked by another issue/PR — body carries Blocked-by: #N
    pm:queueReady for the PM dispatch loop
    on Nov 6, 2026
  7. objectstack-fleet commented on Nov 6, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#2 · os-tesla · session_01L9qibyvxbrLP4jm4PargoH

    Claim: PM loop round 1 · fresh claim after the domain:server#4 unblock and release 6689542338 · 2026-11-06T16:30Z
    Session: session_01L9qibyvxbrLP4jm4PargoH
    Account: os-tesla (the card's assignee)
    Branch: claude/project-thread-fyy60n (the card thread's designated branch; dev session session_01L9qibxjrKo69n5BEsgi5qA)
    Worktree: objectstack-issue-30756
    Domain: domain:server
    Seat: domain:server#2
    File surface: the enable door's pre-check in packages/runtime/src/domains/packages.ts (reusing readDisableDependentRules from PR #30825), the boot replay read in packages/runtime/src/app-plugin.ts if measured reachable, their tests, one changeset. ⛔ No packages/spec edit, ⛔ no new error code (stop on breach).
    Container & model: M, mode:thread, model: TIER_DEFAULT (dispatch-gates --tier: no path mandate), effort: high
    Clause-②: no (narrowing)
    Metadata-protocol: none
    Responsibility: the enable door is ours (packages/runtime, objectstack); the disable door got the scan in PR #30825, the enable door has none; every write to the object answers 400 once such an enable is accepted.
    Thread-read: 6689542338
    Premise: holds. On origin/main c77dd17f readDisableDependentRules is called from the disable door and the POST /packages overwrite of an enabled package, not from the enable door.
    Serial constraints cleared: #30962 (this seat, claude/project-thread-lzvklr) edits the persistence write in the same enable/disable handlers; this card keeps to the pre-check before any state moves, and whichever PR opens second merges origin/main after the first lands.

    Narrowing: the seat runs a CONTRACT_REVIEW_TIER contract review before landing. Order: /mnt/project-files/server-seat-2/r2026-11-06/dispatch-30756.md (frame FRAME.md beside it).

  8. objectstack-fleet commented on Nov 6, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#2 · os-tesla · session_01L9qibyvxbrLP4jm4PargoH

    Claim amendment · 2026-11-06T16:31Z · amends claim 6689904888

    File surface (added, measured by the dev):

    Error code: PACKAGE_DISABLE_RESTRICTED_BY_VALIDATION_RULE is reused (same invariant: a disabled package's withheld field vs an active rule); no new code. Everything else in the claim stands.

  9. objectstack-fleet commented on Nov 6, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#2 · os-tesla · session_01L9qibxjrKo69n5BEsgi5qA
    os-dev-report

    {
      "issue": 30756,
      "status": "done",
      "branch": "claude/project-thread-fyy60n",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/31018",
      "head": "9788f1d5",
      "session": "session_01L9qibxjrKo69n5BEsgi5qA",
      "premise_still_valid": true,
      "summary": "PATCH /packages/:id/enable (and POST /packages enableOnInstall: true on an overwrite of a disabled row) now asks readEnableDependentRules before enablePackage/setPackageDisabled and answers 409 PACKAGE_DISABLE_RESTRICTED_BY_VALIDATION_RULE (reused, no new code) naming package · object · rule and the disabled package to enable first. Judged on the registry's own fold twice: getAllObjects(undefined, { asEnabled }) (new optional objectql fold option) against today's fold, withheld fields = storage fold minus served fold; only rules the enable brings into service count, tenant overlay active:false honoured. Boot replay measured reachable (state file records only disables) and answered with one warn per registry at kernel:bootstrapped naming every rule and the remedies; boot neither refuses nor rewrites operator state.",
      "boot_replay_finding": "Reachable: loadDisabledPackageIds seeds only disables, so a reading package not listed boots enabled beside a disabled field package (new release adding the rule, or a pre-fix state file). Pinned: such a boot answers 400 VALIDATION_FAILED on writes with the validator text 'which this object does not declare — fix the rule's condition, or declare the field' (wrong repair). Decision: skip-with-loud-log (warn, not error: the requester of every refused write is told). Not refuse (one object vs whole runtime; no enable act exists at boot), not rewrite (would override the operator's recorded disable). Read at kernel:bootstrapped after kernel:ready rehydrates, once per registry (WeakSet).",
      "tests": "runtime packages-disable-module-extension-fields.test.ts 27/27 (7 new or flipped: flipped #30379 enable measure pin → 409; enable refusal; CONTROL field package enabled → 200; CONTROL tenant overlay switch → 200; install true-arm refusal + CONTROL; boot warn + CONTROL). objectql registry-disabled-package-extensions.test.ts 19/19 (asEnabled fold + CONTROL byte-identical when unset / not disabled / unknown). Full suites on 08be854f: objectql 559 files / 11202 tests passed; runtime 550 files / 8823 passed 19 skipped. After merge 9788f1d5: rebuild 82/82, both typechecks exit 0, both harness files green. Ablation (runtime source via relative import, no dist leg): anchor 'const dependents = readEnableDependentRules(registry, id);' → '[] ; void readEnableDependentRules', anchor 1→0, blob c38a69765e94→072561ead485, result 2 failed | 25 passed, both 'expected { status: 200, code: undefined } to deeply equal { status: 409, … }' (flipped pin + refusal pin; controls green); restored blob == HEAD c38a69765e94, git diff HEAD empty.",
      "gates": "Local on 08be854f: dispatch-gates --commands → 95 families, each run with stdin from /dev/null after a full non-docs non-ui build; 95 exit 0 (check-plugin-teardown-shape --self-test exit 3 first for the missing pinned commit 621a4876 in the shallow clone, exit 0 after fetching it). --ran reconciliation: 95 derived, 95 run, 0 NOT-MEASURED. eslint --no-inline-config packages/runtime packages/objectql exit 0. objectql + runtime typecheck VERDICT exit 0. CI check-runs on PR #31018: in_progress at report time.",
      "dogfood_battery": "9788f1d5 · os-verify-lock VERDICT command-exit 0 (26m21s) · Test Files 428 passed | 3 skipped (431) · Tests 3560 passed | 41 skipped (3601) · no failures",
      "line_budget": "+672/-27 across 9 files (well under 3000); no governed path",
      "files_changed": [
        "packages/objectql/src/registry.ts",
        "packages/objectql/src/registry-disabled-package-extensions.test.ts",
        "packages/runtime/src/domains/packages.ts",
        "packages/runtime/src/domains/packages-disable-module-extension-fields.test.ts",
        "packages/runtime/src/app-plugin.ts",
        "content/docs/api/error-catalog.mdx",
        "content/docs/api/metadata-api.mdx",
        "content/docs/deployment/marketplace-handbook.mdx",
        ".changeset/30756-enable-door-dependent-rule.md"
      ],
      "deviations": [
        "File surface widened beyond the order (objectql registry.ts asEnabled, the install true arm, three docs pages), accepted by the seat and amended into the claim before the PR.",
        "Changeset moves @objectstack/objectql too (patch) for the fold option.",
        "Post-merge re-check scoped to rebuild + typecheck + the two harness files + full dogfood; the 95-family sweep and full package suites ran on the pre-merge head 08be854f; CI runs the rest.",
        "The relay pr_create carried assignees os-tesla (claim Account); harness MCP assignee/reviewer step skipped per frame."
      ],
      "mcp_calls": "5 reads (issue_read get + get_comments x2; ToolSearch loads not counted) + 1 subscribe_pr_activity (harness subscription, not a GitHub write); 0 GitHub MCP writes",
      "api_writes": "2 — relay pr_create (fw-20261106T174519Z-fea523, run 42264525339) and this os-dev-report comment via post-stamped",
      "open_questions": [],
      "security_findings": "none",
      "out_of_scope_findings": [
        "class: a · reach: not measured through a door · a FRESH install (no enableOnInstall, or true on a new id) of a package whose rule reads a field a disabled package withholds lands enabled through the install door, the same refused state; noted in Acceptance notes · dedupe words: fresh install enabled withheld field validation rule",
        "carrier: cloud · the cloud route /api/v1/cloud/environments/:env/packages/:pkg/disable (and any cloud enable route) not measured, as on the card",
        "carrier: spec lane · error-code-ledger.zod.ts comment on PACKAGE_DISABLE_RESTRICTED_BY_VALIDATION_RULE says it names why a disable is refused; it now also answers the mirror enable"
      ]
    }
  10. objectstack-fleet commented on Nov 6, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#2 · os-tesla · session_01L9qibyvxbrLP4jm4PargoH

    ACCEPT → PR #31018 comment 6692759674 (head de2d28ef) · 2026-11-06T18:50Z · review 6692238619 + delta 6692259121 PASS; the seat readies it and arms auto-merge. The fresh-install case becomes its own card.

  11. objectstack-fleet commented on Nov 6, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#2 · os-tesla · session_01L9qibyvxbrLP4jm4PargoH

    Landing · 2026-11-06T19:17Z · PR #31018 MERGED 2026-11-06T19:16Z as 90523379 (Fixes #30756): enabling a package whose validation rules read fields that a disabled module adds is now refused (409, naming each package · object · rule and the module to enable first) and writes nothing; a boot already in that state logs one warning. The card is done; the seat drops pm:dispatched. Follow-up owed: a fresh POST /packages of such a package still lands enabled (review should-fix 2), filed by the seat as its own card.

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

Metadata

Metadata

Assignees

Labels

bugSomething isn't workingdomain:serverDomain lane — seat card indexed by label:pm:seatpriority:p1High: required for production / M2

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions