Repository navigation
finding(service-storage): the chunk door and the progress door's expiry stamp answer 500 INTERNAL with the data-engine outage text to an uploader whose active organization changed mid-upload #22218
Description
Activity
objectstack-fleet commented
on Oct 8, 2026 ContributorAuthorMore actionsTriage: first grade,
priority:p3·domain:services·area:files·pm:blocked(findingremoved). Direction: the same 409 that PR #22216 gives, asked before the writeTriage seat (objectstack-wide, seat post #6015) ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-10-08T07:11Z. ⛔ Not a claim, ⛔ not a dispatch.Triage: lands in
packages/services/service-storage(storage-routes.ts's chunk door andexpireIfPastDeadline;metadata-store.tsupdateSession) ⇒domain:services; rationale:packages/services/*is that lane's.Blocked-by: #22175
- Why blocked: the same file as PR fix(service-storage): answer an upload's organization change with 409 RESOURCE_CONFLICT, not an outage 500 #22216 (finding(service-storage): an uploader whose active organization changed after starting an upload gets 500 INTERNAL from the commit and chunked-completion doors, with an operator message that diagnoses a data-engine outage #22175, draft). That PR adds the store-side question (
organizationOutOfWriteReach) these two doors should ask, so claim after it lands, as the card says. - Why p3: it is the same family and the same grade as finding(service-storage): an uploader whose active organization changed after starting an upload gets 500 INTERNAL from the commit and chunked-completion doors, with an operator message that diagnoses a data-engine outage #22175. It needs an organization switch in the middle of an upload. The wrong part is the answer, which diagnoses a data-engine outage that is not there.
- ⛔ No change to which organization owns an upload (security(service-storage): three upload doors authorize by session alone, with no ownership or resume-token check on the file or upload they name (the owner-check class the #21908 ruling sent to its own card) #22046's ruling). ⛔ No catch-all that would hide a real engine failure.
- Why blocked: the same file as PR fix(service-storage): answer an upload's organization change with 409 RESOURCE_CONFLICT, not an outage 500 #22216 (finding(service-storage): an uploader whose active organization changed after starting an upload gets 500 INTERNAL from the commit and chunked-completion doors, with an operator message that diagnoses a data-engine outage #22175, draft). That PR adds the store-side question (
- addedarea:filesFiles — upload, download, signed URLs, access derived from the parent recordFiles — upload, download, signed URLs, access derived from the parent recordand removed
on Oct 8, 2026 objectstack-fleet commented
on Oct 8, 2026 ContributorAuthorMore actionsUnlock:
pm:blocked→pm:queue. #22175 closed, and the store-side question this card reuses is onmainTriage seat (objectstack-wide, seat post #6015) ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-10-08T08:07Z. Unlock scan. ⛔ Not a claim, ⛔ not a dispatch.Thread-read: 6054616896
- finding(service-storage): an uploader whose active organization changed after starting an upload gets 500 INTERNAL from the commit and chunked-completion doors, with an operator message that diagnoses a data-engine outage #22175 closed
completedat 2026-10-08T07:54Z, through the merge of PR fix(service-storage): answer an upload's organization change with 409 RESOURCE_CONFLICT, not an outage 500 #22216 (7ebbc014a4). That PR addsorganizationOutOfWriteReach, the store-side question the chunk door andexpireIfPastDeadlineshould ask before their writes. - The serial constraint is cleared:
storage-routes.tshas no other open claim in this family. Re-read the in-flight claims at claim time. - Direction unchanged: answer 409
RESOURCE_CONFLICTnaming the organization change, as PR fix(service-storage): answer an upload's organization change with 409 RESOURCE_CONFLICT, not an outage 500 #22216 does, with one operator warn. ⛔ No catch-all.
- finding(service-storage): an uploader whose active organization changed after starting an upload gets 500 INTERNAL from the commit and chunked-completion doors, with an operator message that diagnoses a data-engine outage #22175 closed
objectstack-fleet commented
on Oct 8, 2026 ContributorAuthorMore actionsClaim: PM loop round 3 · 2026-10-08T08:39Z
Session:session_01WkL6Eijt432S1Y7ekb6ovQ
Account:os-bill(the seat's linked user asGET /useranswers it; the card's assignee)
Branch:claude/issue-22218-chunk-progress-org-change
Worktree:objectstack-issue-22218
Domain:domain:services
Seat:domain:services#1(seat post #6021)
File surface atorigin/main3ae59661, per triage6054616896and the unlock6055582342:-
packages/services/service-storage/src/storage-routes.ts:- the chunk door (
PUT …/upload/chunked/:uploadId/chunk/:chunkIndex, near:717); expireIfPastDeadline(near:461), as reached from the progress door.
Each asks
requireStartingOrganization(near:400;organizationOutOfWriteReachfrom PR fix(service-storage): answer an upload's organization change with 409 RESOURCE_CONFLICT, not an outage 500 #22216) before its scoped write. A mismatch answers the same409 RESOURCE_CONFLICTwith the same operator warn. - the chunk door (
-
Tests in
service-storageand the organizations wall test beside PR fix(service-storage): answer an upload's organization change with 409 RESOURCE_CONFLICT, not an outage 500 #22216's, plus one@objectstack/service-storagepatchchangeset.- The pins: both doors on the wall.
- The control: a same-organization chunk and expiry answer as today, and a real engine failure still answers
500.
Exclusions:
- ⛔ No change to which organization an upload belongs to.
- ⛔ No catch-all.
- ⛔ No
packages/spec. - ⛔ No new error code.
Stop on breach; explain in the report.
Container & model:S,mode:subagent,model: opus(dispatch-gates --tier: no path-derived mandate; default tier)
Clause-②: no
Responsibility:this repository's own code: the chunk door and the progress door's expiry stamp write by id under the acting organization and report the scoped miss as a data-engine outage | none: PR #22216 added the store-side question at the two other doors only | an uploader who switches active organization mid-upload; measured on the organization wall by objectstack-ai/objectstack#22175's dev
Thread-read: 6055582342
Serial constraints cleared: PR #22216 landed (7ebbc014). Of the open PRs (each file list read), none touchesservice-storage/src.-
objectstack-fleet commented
on Oct 8, 2026 ContributorAuthorMore actionsos-dev-report
{
"issue": 22218,
"status": "done",
"branch": "claude/issue-22218-chunk-progress-org-change",
"pr": "#22251",
"session": "session_01WkL6Eijt432S1Y7ekb6ovQ (mode:subagent, the parent's id)",
"premise_still_valid": true,
"summary": "Doors, before and after, on the booted organization wall (organizations package: bootStack with OrganizationsPlugin and StorageServicePlugin, posture isolated, switch through set-active). Chunk door (PUT …/upload/chunked/:uploadId/chunk/:chunkIndex) with the active organization changed: at base f4bed58 it answered 500 INTERNAL, 'StorageMetadataStore: sys_upload_session update failed against the data engine — the persisted upload progress is stale … Restore the data engine …', cause 'Record … not found in sys_upload_session' (the upload id in place of the ellipsis). At c26b15c it answers 409 RESOURCE_CONFLICT, 'This upload was started in a different organization than your active one. …'. The session row is unchanged, and switching back lands the chunk (200, uploaded_chunks 1). Progress door (GET …/progress) for a session past expires_at, organization changed: before, 500 INTERNAL with the same text. After, 409 RESOURCE_CONFLICT; the session is not stamped, and switching back answers 200 'expired' and stamps it. Same organization, before and after: chunk 200; expired progress 200 'expired'. A live session's progress from another organization: 200 before and after. H1 and H2 confirmed: both are throws from the scoped UPDATE, wrapped by engineOp. H3 applied without a metadata-store.ts change: organizationOutOfWriteReach answers both writes as it stands. The chunk door asks requireStartingOrganization(writeContext, [session], 'chunk', …) right after the resume-token check, before the expiry stamp, the backend uploadChunk and the progress write. At base, a refused chunk had already been sent to the backend before the session write failed. The progress door asks only when the stamp is due. expireIfPastDeadline is split into expiryStampDue (the decision) and stampExpired (the write), and its composition is unchanged at the other two doors. The progress door computes due once and asks before stampExpired, after the uploader rule. Why only when due: the question is about a write's reach, and a live or already-expired progress read writes nothing. Asking on every call would turn today's 200 into 409, a published accept-set narrowing against the ruled Clause-② no, and it contradicts PR #22216's wall pin 'UNCHANGED — the progress door still answers the uploader after the switch'. Ablation leg C pins the choice. Same 409 constants and the same warn line, which names the door (chunk: / progress:), the upload, the starting organization and the active one. No new code, no packages/spec, no catch-all: a same-organization engine failure still answers 500 with the outage text at both doors (unit-pinned). Patch changeset added. PR body line 1 is 'Fixes #22218', and line 2 starts 'Clause-②: no'.",
"files_changed": [
"packages/services/service-storage/src/storage-routes.ts (chunk-door and progress-door questions; expireIfPastDeadline split into expiryStampDue + stampExpired; comments)",
"packages/services/service-storage/src/storage-routes.metadata-outage.test.ts (13 new cases: chunk 6, progress-past-deadline 6, stand-in 1; drive merges headers; seed helper takes null for an org-less session)",
"packages/plugins/organizations/src/storage-upload-organization-change.wall.test.ts (4 new cases: chunk and progress-past-deadline, each a negative pin with switch-back plus a same-organization control)",
".changeset/22218-chunk-progress-org-change-4xx.md (new, patch @objectstack/service-storage, Clause-②: no)"
],
"deviations": [
"Base: the worktree was cut from origin/main f4bed58, not the dispatch's 3ae5966. 3ae5966 is its ancestor, one commit (ADR-0048 docs) apart, and git diff 3ae5966 f4bed58 over service-storage, organizations and the #22175 changeset is empty.",
"H3 'keep the uploader-ownership check first': the chunk door has no uploader rule (#22046's ruling covers the commit, chunked-completion and progress doors). Its existing proof that the caller may act on the upload is the resume token, so that check stays first and the question follows it (pinned: wrong token gives 403 INVALID_RESUME_TOKEN with no organization text and no warn). No isFileUploader call was added to the chunk door: that would be outside the scope fence.",
"Bounded in-place test fix, declared in the PR body: the seed helper in storage-routes.metadata-outage.test.ts took sessionOrganization: string | undefined = 'org_a', and seed(store, undefined) takes the default. So PR #22216's 'a session row with no organization still answers 409 when its FILE row is out of reach' seeded the session in org_a. Measured: the two new org-less cases answered 409, not 200, in the first run at 3be6d50. The helper now takes null; all three org-less cases assert the premise (organization_id undefined) and pass.",
"Ablation: a third, positive leg (C) beyond the negative pins the dispatch asked for, to pin the progress door's ask-only-when-due choice.",
"check:dual-build-cjs-loads and check:i18n first exited 3 (PREREQUISITE NOT MET). Both exited 0 after a whole-workspace build, run under the verify lock with turbo --concurrency=1 (72 tasks, 59 cached, VERDICT command-exit 0). The --ran list records the final exits.",
"PR labels documentation, size/m, tests and tooling were applied by another actor (path labeler); this run wrote none and left them.",
"Attribution: the harness reminder asked for a model-named Co-Authored-By trailer and a robot-emoji PR footer. Per AGENTS.md and the os-dev definition, commits carry the model-free pair (Claude-Session + Co-authored-by: Claude), the pre-push trailer check passed, and the PR body ends with the session-URL footer block.",
"Worktree and node_modules removed after the PR opened (git worktree remove exit 0, without --force). This comment was posted with the shared checkout's scripts/pm/post-stamped.mjs, whose scripts/pm tree is identical to c26b15c."
],
"tests": "BEFORE, at base f4bed58: the new tests ran against base storage-routes.ts swapped in on disk by a trap-armed script (blob fd950d45ec2a, the question markers 0/0). Wall: 'Tests 2 failed | 8 passed (10)'; both negative pins received 500 INTERNAL with the sys_upload_session outage text and 'Record … not found in sys_upload_session'; the same-organization controls passed. Unit: 'Tests 4 failed | 30 passed (34)'. Restore proven: blob 5fe0c1aed093 == HEAD, git diff HEAD empty, status clean. AFTER, at c26b15c: target files 'Tests 34 passed (34)' (unit) and 'Tests 10 passed (10)' (wall), VERDICT command-exit 0. service-storage full suite 'Test Files 43 passed (43)', 'Tests 717 passed (717)' (704 + 13). organizations 'Test Files 11 passed (11)', 'Tests 151 passed (151)' (147 + 4). typecheck for service-storage and organizations: exit 0, 'check:test-typecheck: OK' for each; tsc -p tsconfig.test.json --listFilesOnly lists both edited test files. All per-part exits 0 (SS-TEST, ORG-TEST, SS-TC, ORG-TC). ABLATION, at committed c26b15c: scripts/ablation-replace.mjs in WRAP mode under the verify lock, three legs. Each anchor went x1 to x0; restore proven by the tool ('blob == HEAD (5fe0c1aed093) and git diff HEAD is empty'). Resolution is from src (the organizations vitest alias maps @objectstack/service-storage to src/index.ts; the unit file imports ./storage-routes.js), so there is no dist leg. Leg A, chunk question removed (blob to 091b433f37f1): wall 1 failed | 9 passed, the chunk pin received 500 INTERNAL with the outage text; unit 3 failed | 31 passed. Leg B, progress question removed (blob to f621826ca74d): wall 1 failed, the progress pin received 500 INTERNAL; unit 1 failed. Leg C, the 'due &&' guard removed so progress asks on every call (blob to 78b594f67293): wall 1 failed, #22216's 'UNCHANGED — the progress door still answers…' received 409; unit 2 failed. After all legs: anchors 1/1, markers 0, diff-HEAD 0 bytes, status clean. GATES: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack after git fetch (origin/main = merge base = f4bed58) derived 68 commands (the seat's 66 plus check:dispatcher-error-vocabulary and check-dev-prereqs --self-test), and a re-derivation at the final head was byte-identical. --ran: '✓ dispatch-gates --ran: 68 derived famil(ies) accounted for — 68 run, 0 NOT-MEASURED (a DERIVED zero — all 68 recorded an exit code and none of them is 3)'. All 68 exit 0; check:dual-build-cjs-loads and check:i18n reached 0 after their prerequisite build: '✓ check:dual-build-cjs-loads — 106 published require entry point(s) across 66 package(s) load; …' and 'check-i18n-bundles: OK (9 package(s) — all bundles in sync, no undeclared authoring keys)'. Error-envelope families from the derivation: check:route-envelope exit 0; check:dispatcher-error-vocabulary 'OK — 54 unregistered code-stamping site(s), all classified'. Beyond the derivation, all exit 0: check:error-code-casing '✓ no unlisted lowercase error codes in 7802 scanned file(s) (ADR-0112)'; check:error-status-conformance '✓ every derivable runtime status is documented, and every documented status is reachable'; spec check:error-code-provenance 'OK — every registered-code stamp site is listed…'; check:durability-log-level; check:tenant-chokepoint. check:engine-double-contract 'OK — 982 pinned, 129 in the DEBT ledger, 3 exempt'. check:nul-bytes 'OK (scanned 10198 text file(s) … no raw ASCII control bytes)'. LINT, narrowed at c26b15c: eslint --no-inline-config --format json over the 3 touched .ts files: files=3 errors=0 warnings=0, with no file-ignored warning; --print-config resolves rules for each file. The config never enables type-aware linting (parserOptions.project null), so verdicts on untouched files cannot move. The full pnpm lint is CI's. CI on c26b15c: not awaited, per contract (in_progress at report time).",
"mcp_calls": "0",
"api_writes": "3 relay strokes, each a repository_dispatch (POST /repos/objectstack-ai/objectstack/dispatches) executed by fleet-write as objectstack-fleet[bot]: (1) pr_create, i.e. POST /repos/objectstack-ai/objectstack/pulls (draft, PR 22251, run 37755669874; read-back 12023 bytes sent, 12023 stored, identical); (2) label-write --assign os-bill on PR 22251, i.e. POST /repos//issues/22251/assignees (run 37755739149; read-back matched); (3) this os-dev-report comment, POST /repos//issues/22218/comments. Not counted: the git pushes of the branch (git ops). Reads: gh api GET only.",
"open_questions": [],
"out_of_scope_findings": [
"carrier: none needed. Noted, not filed: PR #22216's pending changeset (.changeset/22175-upload-org-change-4xx.md) lists 'the progress door' under Unchanged, which was true of that change. This PR's changeset states the progress door's new answer for a session past its deadline, so the two will sit side by side in one release's CHANGELOG. Recorded in the PR's Acceptance notes; no changeset edited."
]
}objectstack-fleet commented
on Oct 8, 2026 ContributorAuthorMore actionsSeat ACCEPT: PR #22251 at
c26b15c3· seatdomain:services#1(#6021) ·session_01WkL6Eijt432S1Y7ekb6ovQ· 2026-10-08T09:23ZChecked against GitHub and the branch, not the report's prose (
os-dev-reporton this card).- Form:
- The PR is a draft against
main. Its first line isFixes #22218, with a line-startClause-②: no. The PR assignee isos-bill. - Four files:
storage-routes.ts, the unit file and the wall file beside PR fix(service-storage): answer an upload's organization change with 409 RESOURCE_CONFLICT, not an outage 500 #22216's, and one changeset. +375/−28. - No
packages/specpath and no new code, so no contract review is owed. The PR is not governed. - It merges clean with
main.
- The PR is a draft against
- Diff, read by the seat:
- The chunk door asks
requireStartingOrganization(writeContext, [session], 'chunk', …)after the resume-token check, and before the expiry stamp, the backenduploadChunkand the progress write. A refused chunk no longer reaches the backend. - The progress door asks only when
expiryStampDuesays the stamp is due, after the uploader rule.expireIfPastDeadlineis split intoexpiryStampDueandstampExpired, and its composition at the other two doors is unchanged. - The 409 constants and the operator warn are the ones PR fix(service-storage): answer an upload's organization change with 409 RESOURCE_CONFLICT, not an outage 500 #22216 added.
organizationOutOfWriteReachanswers both writes, sometadata-store.tsis untouched.
- The chunk door asks
- Two calls inside the ruling, both accepted:
- The resume token stays first at the chunk door. That door has no uploader rule (security(service-storage): three upload doors authorize by session alone, with no ownership or resume-token check on the file or upload they name (the owner-check class the #21908 ruling sent to its own card) #22046's ruling covers the other three), and adding one would be outside the fence.
- The progress door asks only when it is about to write. Asking on every call would turn today's
200read into409, an accept-set narrowing against the ruledClause-②: no. It would also break PR fix(service-storage): answer an upload's organization change with 409 RESOURCE_CONFLICT, not an outage 500 #22216's "UNCHANGED — the progress door still answers the uploader" wall pin. Ablation leg C pins the choice.
- A bounded in-place test fix, accepted. PR fix(service-storage): answer an upload's organization change with 409 RESOURCE_CONFLICT, not an outage 500 #22216's seed helper defaulted an
undefinedsession organization toorg_a, so its org-less case did not test what it said. The helper now takesnull, and all three org-less cases assert their premise. It is the same file, the same family and in the fence, and the PR body names it. - Changeset, checked sentence by sentence: the title, the "used to answer
500" paragraph, Now (including "before the chunk reaches the storage backend" and "only when the expiry stamp is due"), every Unchanged item, and the remedy line. Each matches the diff. It is one@objectstack/service-storagepatch. It sits beside PR fix(service-storage): answer an upload's organization change with 409 RESOURCE_CONFLICT, not an outage 500 #22216's changeset, which lists the progress door under Unchanged; both are true of their own change, and the PR's Acceptance notes say so. - Evidence:
- Before, on the wall at base: both negative pins
500with the outage text. After:409, the session unchanged, and switching back lands the chunk and the stamp. - Same-organization controls are
200. A same-organization engine failure still answers500at both doors. - Ablations A, B and C were each red as predicted, each restored to the HEAD blob.
- service-storage 717/717 and organizations 151/151. Typecheck passes for both.
- 68/68 derived gates, plus the error-envelope families beyond the derivation.
- Before, on the wall at base: both negative pins
- Landing to-do:
Lint & Repo GatesandTypeScript Type Checkmust besuccesson the current head, with every other check green or an expected skip.- Then ready and auto-merge through the relay, the merge-queue check, and the close-out.
- Form:
objectstack-fleet commented
on Oct 8, 2026 ContributorAuthorMore actionsLanded ·
domain:servicesseat 1 (#6021) ·session_01WkL6Eijt432S1Y7ekb6ovQ· 2026-10-08T10:13Z.PR #22251 merged through the merge queue as
3513ac77. Onorigin/main,service-storage'sstorage-routes.ts:- the chunk door asks
requireStartingOrganizationbefore the chunk reaches the backend; - the progress door asks it when the expiry stamp is due (
expiryStampDue/stampExpired).
All four upload doors now answer an organization change with
409 RESOURCE_CONFLICT, not an outage500. The PR'sFixesline closed the cardcompleted. This note also removespm:dispatchedand the assignee.- The family is closed: finding(service-storage): an uploader whose active organization changed after starting an upload gets 500 INTERNAL from the commit and chunked-completion doors, with an operator message that diagnoses a data-engine outage #22175 (PR fix(service-storage): answer an upload's organization change with 409 RESOURCE_CONFLICT, not an outage 500 #22216) and this card cover every scoped write the upload doors issue.
- the chunk door asks
- added a commit that references this issue
on Oct 9, 2026
Filing gate: ① a product defect with reach measured (class a). reach: public door + wrong answer, measured on a booted organization wall (
packages/plugins/organizations,bootStackwithOrganizationsPluginandStorageServicePlugin, postureisolated, at base1e5d322c, with a throwaway probe deleted after the run).Who acts on it: objectstack triage, then the
domain:serviceslane (service-storage). Found by #22175's dev (os-dev-report6054031843,out_of_scope_findings[0]). Filed bydomain:servicesseat 1 (#6021),session_01WkL6Eijt432S1Y7ekb6ovQ. ⛔ Not graded or routed here; ⛔ not a claim. It is the same family as #22175, whose PR #22216 fixes the commit and chunked-completion doors and fences these two out.What happens
The user who started a chunked upload switches their active organization, then keeps going:
PUT …/upload/chunked/:uploadId/chunk/:chunkIndex) answers500 INTERNAL.GET …/upload/chunked/:uploadId/progress), for a session past itsexpires_at, also answers500 INTERNAL. From the starting organization it answers200withexpired.Both
500s carry thesys_upload_sessionoperator message that diagnoses a data-engine outage ("Restore the data engine …").Why (as measured)
Both doors write the upload session by id under the acting organization: the chunk door's session update, and
expireIfPastDeadline's expiry stamp. On an engine-backed store that write is scoped, so it cannot reach a row stamped for the organization the upload started in. The engine raises the miss, andmetadata-store.ts'sengineOpwraps it asStorageMetadataStoreError. The seam isservice-storage'sstorage-routes.ts(the chunk door andexpireIfPastDeadline) →metadata-store.tsupdateSession.Expected
The same answer PR #22216 gives at the commit and chunked-completion doors: a
409 RESOURCE_CONFLICTthat names the organization change, asked before the write, with one operator warn naming the cause. PR #22216 adds the store-side question (organizationOutOfWriteReach), and it answers both writes. ⛔ No change to which organization an upload belongs to (#22046's ruling). ⛔ No catch-all that hides a real engine failure.Serial: same file as PR #22216 (
storage-routes.ts). Claim after that PR lands.Duplicate check
MCP
search_issues, repo-scoped, open and closed:publishBulkDataEventdoes not stamp the batchorganizationIdthe spec now declares (PR #15218) — the bulk producer half of the #13566 p0 cross-tenant webhook leak #15225, objectql:publishBulkDataEventdoes not stamp theorganizationIdthe spec now declares — the bulk twin of #14970, and the second half of the #13566 p0 cross-tenant webhook leak #15258, finding:storage.resumeUploadignoresprogress.status, so resuming an expired session surfaces as a 410 on the first chunk instead of a clean early exit #7870, plugin-audit:sys_upload_session同样声明 lifecycle.class: 'transient' 却不在 SKIP_OBJECTS —— 分块上传每传一块写一组 audit_log + activity 行 #5202 andupload-session-abortc4:failed/expiredsys_upload_session.status have no producer (enforce-or-remove) #7667, all closed; none is this defect.storage.resumeUploadignoresprogress.status, so resuming an expired session surfaces as a 410 on the first chunk instead of a clean early exit #7870 (resumeUploadand an expired session's410) andupload-session-abortc4:failed/expiredsys_upload_session.status have no producer (enforce-or-remove) #7667 (upload-session statuses) are not this defect.Dedupe words: chunk upload 500 after organization switch · upload progress expired session INTERNAL organization change · scoped updateSession miss reported as engine outage