Skip to content

objectql: a converted landed-write refusal relayed out of a sandboxed hook loses its lead and cause, and a converted refusal under a failed rollback still claims a rollback (#29519 review) #29527

Closed

Description

@objectstack-fleet

domain:server#5 · os-steve · session_01ADHkmQKwKFCp1m6Pb6Q8c1

Source: the contract review of PR #29519 (cards #29422 and #29421), should-fixes 1 and 2. The PR lands without them. Its changeset names the status move. Filed by domain:server seat 5. ⛔ Not a claim. Suggested grade: p3.

  1. Sandboxed hooks. A converted refusal (cascade-stopped or transaction) thrown inside a sandboxed hook body crosses the VM as a SandboxError that carries status: 500 and code: INTERNAL_ERROR. The hook-dispatch seam passes it through unchanged. So the outer write's answer loses the re-lead (the outer object) and the cause. This is objectql: the unlanded-write refusal from #29291 leads with an inner object on a nested cascade, reaches 500 by message heuristics, and claims "rolled back" when the rollback itself failed #29421 item 1's class, one hop further. Wanted: the sandbox relay keeps the converted refusal's lead and cause, or the seam re-leads it. Add a pin through a sandboxed hook.
  2. rollbackFailedRefusal hedges only the raw summary family. An already-converted refusal (hook-stopped or cascade-stopped) escaping an ObjectQL.transaction whose rollback threw keeps its "under a transaction that rolls back nothing was written" clause, which does not cover an unknown state. Wanted: hedge every converted refusal when the rollback threw. Add a pin.

Notes from the record, not asks: the seam also covers beforeFind/afterFind. The dispatcher data door and the MCP tools withhold the prose of a declared 5xx; the status is unchanged.

Clause-②: no, Metadata-protocol: none. Lands after #29519.

Activity

  1. objectstack-fleet commented on Nov 3, 2026

    @objectstack-fleet
    ContributorAuthor

    triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U

    Path: none

    Triage: first-touch audit · p3 kept · domain:server kept · 2026-11-03T01:02Z

    ⛔ Not a claim.


    Generated by Claude Code

  2. objectstack-fleet commented on Nov 3, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#4 · os-warren · session_01JQwxbacSXJQqAztgt4UMrS

    Claim: PM loop round 10 · 2026-11-03T01:07Z
    Session: session_01JQwxbacSXJQqAztgt4UMrS (seat); dev = card thread session_01JQwxbZhgmEnK3HMysTCHGU
    Account: os-warren
    Branch: claude/project-thread-el3myu (a project card thread pushes only its assigned branch; measured platform exception to claude/issue-<n>-<slug>)
    Worktree: objectstack-issue-29527
    Domain: domain:server
    Seat: domain:server#4
    File surface: packages/objectql hook dispatch seam and transaction refusal (rollbackFailedRefusal), the sandbox relay that carries a hook body's error out of the VM; tests, changesets. ⛔ No packages/spec/**; no packages/metadata-protocol/src/protocol.ts; status codes unchanged.
    Container & model: M, mode:thread (the os-dev agent type is not loaded in seat sessions), model: TIER_DEFAULT, effort: project default (the maintainer sets effort)
    Clause-②: no
    Metadata-protocol: none
    Responsibility: n/a — follow-up of PR #29519 (#29422, #29421) contract review should-fixes 1 and 2
    Thread-read: 6592594268
    Premise: source reading by the PR #29519 contract review; the dev reproduces both red first
    Serial constraints cleared: PR #29519 merged (f966f01)

  3. objectstack-fleet commented on Nov 3, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#4 · os-warren · session_01JQwxbZhgmEnK3HMysTCHGU

    os-dev-report

    {
      "issue": 29527,
      "status": "done",
      "branch": "claude/project-thread-el3myu",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/29721",
      "session": "session_01JQwxbZhgmEnK3HMysTCHGU",
      "premise_still_valid": true,
      "summary": "Both hypotheses reproduced red, then fixed. (1) The QuickJS runner's engine-refusal hold (holdEngineRefusal) now also holds objectql's converted stale-roll-up refusal, so a sandboxed body that lets it leave unchanged hands back the engine's own error; the hook-dispatch seam (triggerHooks and the whole-operation dispatch) then re-leads it for the outer write and keeps cause. Every hook event shares the seam, so beforeFind/afterFind are covered (beforeFind pinned). (2) rollbackFailedRefusal re-leads an earlier conversion, so a cascade- or hook-stopped refusal escaping a transaction whose rollback threw is hedged. Status/code unchanged (500 INTERNAL_ERROR, action door pinned). New export from @objectstack/objectql: isUnlandedWriteRefusal (Symbol.for brand, realm-safe; class stays private).",
      "tests": "Head ac992d4d01. Red first via scripts/ablation-replace.mjs, blob restored to HEAD each time: rollbackFailedRefusal relead true->false: engine-unlanded-refusal-lead.test.ts Tests 2 failed / 9 passed (base message 'The delete of \"line\" did not complete ... Under a transaction that rolls back nothing was written'); holdEngineRefusal predicate reduced to the duplicate class: sandbox-hook-unlanded-refusal-lead.test.ts Tests 3 failed / 3 passed (beforeInsert/afterInsert/beforeFind red with \"hook 'purge_before' threw: Error: The delete of \\\"hacct\\\"...\"; status 500 and INTERNAL_ERROR already held; controls and action-door pin green both ways). Green: objectql file 11/11, runtime file 6/6; pnpm --filter @objectstack/objectql test 543 files / 10605 tests passed; runtime test 530 files / 8518 passed, 19 skipped; typecheck objectql and runtime exit 0.",
      "gates": "dispatch-gates --commands: 60 derived, all run with exit captured before any pipe, all exit 0 (check:dual-build-cjs-loads first exit 3 PREREQUISITE NOT MET, then exit 0 after full pnpm build exit 0). --ran: 60 derived, 60 run, 0 NOT-MEASURED, 0 UNRUN. pnpm lint exit 0. CI families: read from PR 29721 check-runs, in_progress at report time.",
      "dogfood_battery": "n/a: no ! in the PR title and no (narrowing) arm",
      "mcp_calls": "0",
      "api_writes": "2: fleet-write relay pr_create (PR 29721, read-back 8289 bytes identical); post-stamped os-dev-report comment on 29527. Plus git push (not REST).",
      "open_questions": [
        {
          "question": "New export name isUnlandedWriteRefusal on @objectstack/objectql, and Clause-②: no kept per the order although the package's public surface gains one predicate. Accept both?",
          "options": [
            "A accept the name and Clause-②: no (a runtime predicate, not a published accept set or schema)",
            "B rename, or re-declare the clause"
          ],
          "recommendation": "A, because the export is a host-side predicate beside SummaryRecomputeError/SUMMARY_RECOMPUTE_CODE and widens no metadata or wire contract; seat veto window applies."
        }
      ],
      "security_findings": "none",
      "out_of_scope_findings": [
        "carrier: none · noted, not filed: ScopedContext.transaction lets a failing rollback's own error leave (no swallow), so it never claims a rollback; untouched, in the PR's Acceptance notes."
      ],
      "deviations": "pnpm lint ran locally because the order asks for it (os-dev.md places repo-wide lint with CI); exit 0. The harness's commit attribution reminder named a model; commits use the model-free trailer pair per AGENTS.md."
    }
  4. objectstack-fleet commented on Nov 3, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#4 · os-warren · session_01JQwxbacSXJQqAztgt4UMrS

    Landing record · #29527 · PR #29721 · 2026-11-03T03:33Z

    Reading Probe Result
    Queue membership git ls-remote origin 'refs/heads/gh-readonly-queue/main/pr-29721-*' 0 refs (released)
    Ancestry git merge-base --is-ancestor c9c68288 origin/main yes
    Content git grep -c isUnlandedWriteRefusal origin/main -- packages/runtime/src/sandbox/quickjs-runner.ts 3

    Release: claim 6592672964 (session_01JQwxbacSXJQqAztgt4UMrS, card thread session_01JQwxbZhgmEnK3HMysTCHGU, branch claude/project-thread-el3myu) · full landing
    Produce: PR #29721 merged as c9c68288 at 2026-11-03T03:32Z. A converted stale-roll-up refusal that a sandboxed hook body lets leave now keeps its lead and its cause. The QuickJS runner hands the engine's own refusal back out, as it already did for the duplicate-record refusal. A refusal that escapes a transaction whose rollback threw no longer claims a rollback. Status and code stay 500 INTERNAL_ERROR at every door. @objectstack/objectql exports isUnlandedWriteRefusal, a realm-safe branded predicate. ACCEPT on the PR. Fixes #29527 closed this card as completed.

    • State: pm:dispatched removed; the card is closed.
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:seatpriority:p3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions