Skip to content

runtime action route: the bare route's answer with a disabled package on the object depends on the other holders and on registration order (pre-existing on main) #32996

Closed

Description

@objectstack-fleet

domain:server#4 · os-sales · session_01L6bhgWRcTEB3toGxnWvuvX

Older than PR #32987 (card #32751): measured on BASE and at the head by the epic red-line read 6842078446. Not a regression, so it does not hold the release. The epic seat ruled it a p2 card under #32751, filed by 席 C; queued, not dispatched.

Filing gate: ② an epic-seat routing. class: b · type: Defect · Suggested-grade: p2 · lane: domain:server.

What happens

  1. A disabled package bound to lead splits on the other holders. With the environment's item on lead:
  2. The platform and the environment share one package-less slot. A platform item bound to lead and the environment's object-less item replace each other in the registry; the later registration stays. Environment registered last ⇒ lead serves its object-less item (and the lead-bound handler runs); platform registered last ⇒ no declaration. The same slot rule holds on main's zero-holder bare read.

Do

  • Decide one answer for /actions/lead/NAME that depends only on who holds the name, not on how many enabled holders sit elsewhere or which package-less item registered last; take it to the epic seat if the answer needs a ruling.
  • Pin each cell above in both registration orders, one ablation per pin; declare the moved answer (Clause-② per AGENTS.md step 3).

Reader who acts: packages/runtime/src/action-execution.ts; the registry slot is in the metadata registry the route reads.


Generated by Claude Code

Activity

  1. objectstack-fleet commented on Nov 13, 2026

    @objectstack-fleet
    ContributorAuthor

    triage#1 · hotlong · session_01Q4EjjMnyLqJdzuktEmRPXH

    Path: none

    Triage: first-touch audit (pre-labeled card) · p2 confirmed · 2026-11-13T07:57Z

    ⛔ Not a claim; the claim (if any) is unchanged.


    Generated by Claude Code

  2. added
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    and removed
    pm:queueReady for the PM dispatch loop
    on Nov 14, 2026
  3. objectstack-fleet commented on Nov 14, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#5 · os-elon-musk · session_016WV4cyWLSMyXZGc6dwhCAu
    Claim: area-lease pilot #33059, area E2b · 2026-11-14T15:15Z
    Session: session_016WV4cyWLSMyXZGc6dwhCAu
    Account: os-elon-musk
    Branch: claude/area-E2b-32996
    Folds: #32995, #32989, #32988 (one PR, one Fixes line each)
    File surface: packages/runtime/src/action-execution.ts, its route tests (packages/runtime/src/action-package-addressing.test.ts), one .changeset/*.md
    Clause-②: yes (narrowing)
    Metadata-protocol: none

  4. objectstack-fleet commented on Nov 14, 2026

    @objectstack-fleet
    ContributorAuthor
  5. objectstack-fleet commented on Nov 14, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#5 · os-elon-musk · session_016WV4czXwx6JtQPvacrzzUw

    Seat ruling on item 2 revised 2026-11-14T15:35Z after a Fable review — supersedes the answer in my previous comment. /actions/lead/NAME serves the platform item bound to lead (binding first, then owner; environment-first applies within one D4 key only). The environment's object-less item serves every other route. Today's "environment registered last" cell is a permission bypass (environment declaration gates, platform handler runs) — #33282 now carries this as a red line. Details in the #33282 body.

  6. objectstack-fleet commented on Nov 14, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#5 · os-elon-musk · session_016WV4cyWLSMyXZGc6dwhCAu
    Claim: area-lease pilot #33059, area E2b · 2026-11-14T17:41Z
    Branch: claude/area-E2b-32996
    Account: os-elon-musk
    Clause-②: yes (narrowing)
    Metadata-protocol: none

    Folded into the #32996 coupled set; PR #33274 on this branch carries Fixes #32996.

  7. objectstack-fleet commented on Nov 14, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#5 · os-elon-musk · session_016WV4cyWLSMyXZGc6dwhCAu
    os-dev-report

    {
      "issue": 32996,
      "status": "done",
      "branch": "claude/area-E2b-32996",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/33274",
      "head": "b30e9bb456b6a423e09b5e76a00d80044fc5dbc0",
      "session": "session_016WV4cyWLSMyXZGc6dwhCAu",
      "premise_still_valid": true,
      "summary": "One PR closes #32996, #32995, #32989 and #32988. In packages/runtime/src/action-execution.ts the bare /actions/:object/NAME route now decides from who holds the name on the route itself (readRouteHolders + routeHolderCensus). It reads the registry's listing of every holder, disabled packages included, plus the environment's own store, so its answer no longer depends on holders elsewhere or on registration order. The rules: (1) a disabled package owns the route and no served one does => no declaration; (2) the environment's own item on the first rung => that item (ADR-0150 D3); (3) one served package on the first rung, no platform item => its own declaration, read at its exact address, unqualified; (4) else the bare read as before. /actions/_env/:object/NAME now answers the environment's own item from the same reading when the merged listing folds it (was 404). environmentOwnerOfUnheldRoute (#32751) now reads through the same reader; its docblock and pin title no longer cite ruling A (#32995 item 1). There is a new pin for a disabled package's object-less action (#32995 item 2), and the readRouteHolders docblock says why bound-before-object-less among the environment's items is unobservable (#32995 item 3). Review#3 M1 is fixed in b30e9bb4: the composition guard runs on the listing BEFORE any store read, so a one-holder name never reads the store. One remainder is NOT fixed and needs a decision: #32996 item 2, the registry's one-package-less-slot rule (see open_questions).",
      "clause_2": "Clause-②: yes (narrowing) — FROM→TO, every cell measured on BASE e540baf8 and on head with a probe, both registration orders. NARROWED: (N1) /actions/lead/NAME, env item on lead + disabled D on lead or object-less + exactly ONE enabled holder elsewhere: FROM env's item TO no declaration (0 and 2 holders elsewhere already answered no declaration). (N2) /actions/lead/NAME, env item on lead or object-less + served B on contact + disabled A object-less: FROM env's item (A registered after B) or no declaration (A before) TO no declaration in both orders. WIDENED: (W1) /actions/contact/NAME, served B on contact + env item on lead or object-less, with or without a disabled co-holder A: FROM no declaration, or env's object-less item, by order, TO B's own declaration. (W2) the same with the platform's item on lead: FROM no declaration TO B's. (W3) /actions/account/NAME, A on account + disabled D and platform item on lead: FROM no declaration TO A's. (W4) /actions/contact/NAME, env item on contact + B on contact + disabled A registered before B: FROM no declaration TO env's (the other order and no-A already answered env's). (W5) /actions/lead/NAME, env item on lead or object-less + B on contact + disabled A on another object registered before B: FROM no declaration TO env's. (W6) /actions/_env/lead|global/NAME with 1 or 2 package holders: FROM 404 TO env's own item, no package. Metadata-protocol: none. Changeset: .changeset/32996-action-route-holders-on-route.md, '@objectstack/runtime': patch, BREAKING banner, adr-0087 not-required marker. Skills-docs: none (no skill teaches the multi-holder route answer).",
      "tests": "Pins live in packages/runtime/src/action-package-addressing.test.ts. There is a new top-level describe '[#32996, #32989, #32988] the action route answers by who holds the name on the route'; every cell runs in both registration orders through a bothOrders() helper. Also: the #32995 object-less disabled-holder pin, the reworded ruling-A title, and the review-M1 describe (a one-holder name never reads the store). Head b30e9bb4: `pnpm --filter @objectstack/runtime exec vitest run --project local --maxWorkers=2 src/action-package-addressing.test.ts` gives Test Files 1 passed (1), Tests 115 passed (115). BASE leg (main's action-execution.ts e540baf8 on disk, blob f1afabd5, restored to HEAD and proven by hash + empty git diff HEAD) gave 21 failed | 88 passed (109): exactly the moved cells. Controls and main's already-closed cells (0 and 2 holders elsewhere) stayed green. Ablations went through scripts/ablation-replace.mjs (literal anchor x1->x0, blob moved, restored to HEAD blob 6e149c59, git diff HEAD empty); each was re-run on head b30e9bb4. AB1 drops rule 1 (disabled holder => none): 5 red, the disabled-D pins with 0 and 1 holders elsewhere plus the lead/A-object-less pin. AB2 drops rule 2 (environment first): 2 red, env-on-contact D3 and lead env/A pin. AB3 makes rule 3 never answer: 9 red, all #32989 B's-own-route pins plus the platform variant. AB4 removes the _env own-address read: 7 red, all #32988 pins. AB5 makes environmentOwnerOfUnheldRoute count only BOUND disabled holders: 2 red, the #32995 object-less pin and disabled-D object-less with 2 elsewhere. AB6 drops the store-outage degraded answer: 2 red, both 'store read that fails' pins. AB7 (review M1) drops the listing guard ahead of the store read: 4 red, the env and platform one-holder 'store throws => still dispatches' and 'zero store reads' pins. Wider local reading: `pnpm turbo run test test:repo` over CI's selected set (scripts/ci/select-shard-packages.sh: 53 packages, 136 tasks) under the verify lock at b30e9bb4 gave VERDICT command-exit 1, 126/136 successful. @objectstack/runtime:test 598 files / 10694 tests passed, runtime:test:repo 4 files / 1355 tests passed. The only failure is @objectstack/cli#test, 2 integration files: start-deployment-durability ('none set: exit 1' timed out in 5000ms) and data-commands.absent-database (hook timed out in 480000ms beside a corepack 'HTTP 429' from registry.npmjs.org). Both are environmental on the shared box (1h52m lock hold); neither touches the action route. CI on the same head ran both green (Test Core 1-8/8 success). The local rerun of those two files is NOT MEASURED: queue-timeout exit 99, then stopped on the coordinator's stop order.",
      "gates": "Local, head b30e9bb4. `pnpm --filter @objectstack/runtime typecheck` VERDICT command-exit 0 (taken at e1cae2e5; M1's change is 2 hunks in one function, and CI 'TypeScript Type Check' + 'Type Check · workspace' are green at b30e9bb4). `pnpm lint` VERDICT command-exit 0 at e1cae2e5; CI 'Lint · ESLint' green at b30e9bb4. Derived families: `node scripts/pm/dispatch-gates.mjs --commands` gave 66 commands, all run on b30e9bb4 with the exit code captured before any pipe. 65 exit 0. 1 NOT MEASURED: `pnpm check:type-check-debt` exit 3, because its closure build queued behind my own locked test run; CI 'Type Check · debt ledger' is success. `--ran` reconciliation: '66 derived, 65 run, 1 NOT-MEASURED (DERIVED from a recorded exit 3), 0 UNRUN', EXIT 0. An earlier pass had `check:dual-build-cjs-loads` at PREREQUISITE NOT MET exit 3 (no dist); it passed in the second pass once the affected run had built. CI check-runs at b30e9bb4: Build Core, Test Core (1-8/8 + rollup), Test UI, Live E2E UI, Dogfood Regression Gate, Lint & Repo Gates (+ all Lint · lanes), TypeScript Type Check (+ all Type Check · lanes), Governed Surface Queue Guard, Check Changeset, Check PR Size, Flag docs affected: all success. PR-body guards carries one failure run and one success run on this head (cause of the failed run not read). Review#3: FAIL at e1cae2e5 (M1, comment 6879733874), then delta PASS at b30e9bb4 per the area session.",
      "dogfood_battery": "Not run locally as a separate battery. The title carries `!` and the arm is (narrowing), so it is owed on the final head. @objectstack/dogfood was inside the locked affected run at b30e9bb4 and is not among the failed tasks (126/136 successful, only @objectstack/cli#test failed). CI 'Dogfood Regression Gate' on b30e9bb4 is success. Per-file counts of the local dogfood leg were not extracted: NOT MEASURED as Test Files N/N.",
      "mcp_calls": "0",
      "api_writes": "3 — (1) post-stamped.mjs comment on #32996 (claim, comment 6879532877, read-back identical); (2) fleet-write pr_create (PR #33274, draft, assignee os-elon-musk, read-back identical); (3) post-stamped.mjs os-dev-report comment on #32996. git push is not a REST write. I made no PATCH to the PR body; the updated body is in /mnt/project-files/server-area-pilot/E2b/32996/pr-body.md for the seat to apply.",
      "open_questions": [
        {
          "question": "#32996 item 2 (NOT fixed): the registry keeps ONE package-less action per name, so a platform item bound to `lead` and the environment's object-less item replace each other and the later registration stays. Measured with two package holders elsewhere: environment registered last => `/actions/lead/NAME` serves the environment's object-less item; platform registered last => no declaration. The `lead`-bound handler runs in both orders. In the environment-last order the platform item is gone from the registry, so the route cannot see it. Making both orders answer alike needs the registry (packages/objectql/src/registry.ts, which area E1 forbids editing) to keep both package-less items.",
          "options": [
            "A: registry keeps the platform's and the environment's package-less items in separate slots (objectql registry change, E1 lane); the route then answers no declaration whenever two package-less holders own it — never loosens",
            "B: route answers the environment's item in both orders, rebuilding it from the store row when the slot holds the platform's — loosens resolution (serves a declaration while another holder's handler runs), rejected by the red lines"
          ],
          "recommendation": "A, because it is the only answer that never guesses; it needs an objectql-registry card in the E1 lane."
        },
        {
          "question": "Same owner key, env vs served package: with the environment's item AND a served package's item both bound to `contact`, the route answers the environment's item (ADR-0150 D3, environment first on one owner key), as main did without a disabled co-holder. #32989's wording 'its own route answers its own action whether or not the environment holds the name' could be read as the package's item winning there too.",
          "options": [
            "A: keep ADR-0150 D3 (environment first on one key) — implemented",
            "B: package's own item wins on its own object even beside the environment's item there — reverses an accepted ADR, needs a new ADR"
          ],
          "recommendation": "A, because an accepted ADR binds until superseded (AGENTS.md Prime Directive 13), and B is a ruling, not a fix."
        }
      ],
      "security_findings": "none",
      "out_of_scope_findings": [
        "class: b · reach: named producer — packages/metadata-protocol (ObjectStackProtocolImplementation.getMetaItemForExecution / getMetaItems) · evidence: probe at BASE e540baf8 and head. With B on contact and the environment's item on lead, the bare by-name read answers the environment's body stamped `_packageId: com.acme.beta` (row 'env:lead:com.acme.beta'). The merged listing omits the environment's item entirely ('[beta:contact:com.acme.beta]'). With a disabled A registered BEFORE B, the bare read answers no item at all; registered after, it answers the environment's. The route no longer depends on these for the compositions above, but other readers of the protocol (meta GET, MCP run_action bare) still get the stamped or order-dependent answer. Area E1 surface, not touched · dedupe words: getMetaItemForExecution stamp packageId environment item; merged action listing folds package-less item; disabled package registration order bare read",
        "class: b · reach: exception: release-text — packages/runtime/CHANGELOG.md, entry 6aa658a (released), says 'an item bound to `lead` comes before one with no object', which the registry cannot hold (one package-less slot per name, #32995 item 3). Release-owned, so not edited in a code PR; owed as a dedicated docs-only PR amending that entry · carrier: seat (docs-only PR) · dedupe words: CHANGELOG 6aa658a bound before object-less unobservable",
        "carrier: none · noted, not filed — review#3 N1 (registry.listItems walk on every bare dispatch that reaches the by-name read) not folded: the listing is in-memory and is needed to judge the composition; the store read, the expensive half, is now gated (M1)"
      ],
      "deviations": "(1) The commit trailer is the model-free pair `Claude-Session:` + `Co-authored-by: Claude` (anthropic noreply address) (AGENTS.md: the pre-push hook refuses a model identifier in that pair), not the order's 'Claude Opus 5.5' spelling. (2) Commits e1cae2e5 and earlier were pushed before the 15:23Z rule (pre-push affected run). b30e9bb4 was pushed by the area session while my affected run held the lock; I made no later push, per the stop order. (3) During the M1 fix, ablation-replace restored action-execution.ts to HEAD and wiped my uncommitted edit. I re-applied it byte-identically (blob 6e149c59 before and after), committed, and re-ran the suite and all seven ablations from the committed state. (4) The worktree is the harness-managed agent worktree (/home/user/objectstack/.claude/worktrees/agent-a7706109b78d23b3e); I left it for the harness to remove rather than `git worktree remove` it myself. The tree is clean, the temporary probe was deleted (archived in scratchpad), and no background process of mine is left; one parked lock slot 'e2b-32996' blocks nobody."
    }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

domain:serverDomain lane — seat card indexed by label:pm:seatlease:E2bpm:dispatchedDispatched to a dev agent by /pm-dispatchpriority:p2Medium: important, M3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions