You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
spec(contracts): the verified MCP access token result carries organizationId, the organization an OAuth consent bound (slice 1 of #24137, ruled A at 6232069920) #24165
Carries item 5, first slice, of #24137 (the packages/spec contract type); #24137 keeps the runtime read and its pins, and #24137's second upstream slice (plugin-auth's consent binding) is filed beside this one.
Filing gate ③, a maintainer-directed task: ruled at 6232069920 on #24137, letter A, the maintainer's word 「同意」, recorded by the triage seat. Filed by domain:cli seat 1 (#6024) · os-sales · session session_01BVm1pAXVXQ3PjC42j1kPFf. ⛔ Not a claim.
What the ruling binds, for this slice
From 6232069920, verbatim: "Binding: at consent, plugin-auth records the consenting user's current organization through the installed OAuth provider's declared seam (postLogin.consentReferenceId / customAccessTokenClaims). The access token carries it as the claim organization_id." Its execution plan names this slice: "The packages/spec contract type for the verified-token result. Spec seat, Clause-②: yes (widening)."
The change
packages/spec/src/contracts/auth-service.ts, verifyMcpAccessToken (about :297, read on origin/main52c04b360): its result type { userId: string; scopes: string[]; clientId?: string } gains the bound organization, as an optional organizationId member. It is absent for a token minted before the binding existed, and for an M2M token.
Clause-②: yes (widening): a published contract type gains a member. The describe text / JSDoc says what it is: the organization the user's OAuth consent bound, read from the token's organization_id claim, and re-checked against a current membership by the caller on every request (ruling item 2).
Reader who acts
The domain:spec seat (packages/spec is always its; this card self-labels domain:spec under the filer exception, so triage spot-checks the route). The plugin-auth slice waits on this one (its Blocked-by:), and #24137 waits on both.
Dedupe
MCP search_issues on this repository, closed included: OAuth MCP access token organization claim consent bind organization verifyMcpAccessToken gave 4 hits. #24137 is the parent. #20791 is the MCP distribution check, #16418 (closed) the M2M principal, and #16549 (closed) the OAuth agent ceiling. None is this contract member.
Claim: PM loop round 1 · 2026-10-17T14:17Z · the packages/spec contract slice of #24137: IAuthService.verifyMcpAccessToken's verified result gains the optional organizationId an OAuth consent bound (ruling 6232069920, letter A)
Session: session_01TyRhWbVbbQpQJTDbW8VahJ (the seat; the dev is an os-dev subagent of this session)
Account: os-project-manager (the seat's linked user as GET /user answers it; always the card's assignee)
Branch: claude/issue-24165-mcp-token-organization-id
Worktree: objectstack-issue-24165
Domain: domain:spec
Seat: domain:spec#1 (seat post #6017)
File surface (read on origin/mainb0fe9fad47; line numbers move; stop on a breach and explain it in the report):
packages/spec/src/contracts/auth-service.ts: verifyMcpAccessToken's result type (:297), { userId: string; scopes: string[]; clientId?: string }, gains organizationId?: string, and its TSDoc says what the member is.
The generated artifacts that member moves (the api-surface shard, and any other the spec build regenerates), written by their generators only.
Pins beside it under packages/spec/src/contracts/ if the contract has a type-level test home. One .changeset/24165-*.md (@objectstack/spec, minor: a published contract type gains a member).
{
"issue": 24165,
"status": "done",
"branch": "claude/issue-24165-mcp-token-organization-id",
"pr": "https://github.com/objectstack-ai/objectstack/pull/24189",
"session": "session_01TyRhWbVbbQpQJTDbW8VahJ (subagent: the parent PM session id)",
"premise_still_valid": true,
"summary": "IAuthService.verifyMcpAccessToken's verified result (packages/spec/src/contracts/auth-service.ts) gains organizationId?: string. Its TSDoc says: it is the organization the user's OAuth consent bound, read from the access token's organization_id claim. It is absent for a pre-binding token and for a client_credentials token, and absent means global grants only. It is a claim, not an authorization: the caller vets it on every request by the session's vetOrganizationClaim rule, an ended membership resolves to no organization, and it is never the union of the user's organizations or the browser session's current organization. Under ADR-0090 D10 it is the organization the delegator is resolved in. No tracker numbers in the new prose. One typed-read case in the interface's existing test home (auth-service.test.ts) and a minor changeset. Premise H1 held on origin/main cb8756b00 (line :297, no organizationId). H2 held: the one implementer and the runtime reader are untouched and every consumer typechecks. H4 is FALSIFIED: no generated artifact moved, because api-surface records export names and no export was added (build left git status empty; check:generated 14/14 up to date). Draft PR #24189 is open with assignee os-project-manager and no labels, because the dispatch named none and skip-changeset does not apply.",
"tests": "All at HEAD 06210ccf8. (1) Under os-verify-lock: pnpm --filter @objectstack/spec build gave 'VERDICT command-exit 0' and git status --porcelain was empty. The rebuilt dist/contracts/index.d.ts and index.d.mts each carry the member (1 hit); index.js and index.mjs carry 0 hits, so the interface is erased and no emitted JS byte changed. (2) pnpm --filter @objectstack/spec exec vitest run --project local --maxWorkers=2 src/contracts/auth-service.test.ts: 'Tests 10 passed (10)'. The full suite, pnpm --filter @objectstack/spec exec vitest run --project local --maxWorkers=2: 'Test Files 692 passed (692)', 'Tests 20975 passed | 1 todo', VERDICT command-exit 0. (3) pnpm --filter @objectstack/spec typecheck: VERDICT command-exit 0; 'check:test-typecheck: OK'. (4) Downstream consumers holding IAuthService. Closures were built with pnpm exec turbo run build --filter='@objectstack/mcp^...' (7 tasks) and --filter on plugin-auth^..., runtime^... and cloud-connection^... (31 tasks), each with --concurrency=2 and VERDICT command-exit 0. Then typecheck: @objectstack/mcp exit 0, @objectstack/plugin-auth exit 0, @objectstack/runtime exit 0, @objectstack/cloud-connection exit 0. Direction: these four are downstream consumers of @objectstack/spec. Their runtime test suites were not run locally: no emitted JS changed (evidence in 1), so they are declared to CI. (5) Reverse verification, one-off: a temporary packages/mcp/src/zz-reverse-24165.ts read organizationId as string | undefined and assigned organizationId: 123 on an annotated result. pnpm --filter @objectstack/mcp exec tsc --noEmit gave exactly one error, 'src/zz-reverse-24165.ts(9,62): error TS2322: Type number is not assignable to type string'. The old declaration would give TS2339 plus TS2353 instead. The file was deleted and the tree was clean. Observed direction: red as expected. (6) Ablation, one-off: node scripts/ablation-replace.mjs --delete of ' organizationId?: string;' in auth-service.ts (anchor x1 to x0, blob e2d1d8138861 to db2648c7b9f2), with a bash EXIT/INT/TERM trap restoring git checkout HEAD on the absolute path, wrapping pnpm --filter @objectstack/spec check:test-typecheck. That went red: 'src/contracts/auth-service.test.ts: 2 type error(s) in a file the ledger does not cover'. Restore was proven: blob after restore equals HEAD (e2d1d8138861), git diff HEAD empty, porcelain empty. No dist leg was needed, because the test imports ./auth-service from source.",
"gates": "Local subset at HEAD 06210ccf8. node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack gave 84 commands; the stderr first line names objectstack-ai/objectstack at 06210ccf8; 3 paths. Every command ran with its exit code captured before any pipe ('cmd > log 2>&1; status=$?'). First pass: 80 exit 0 and 4 exit 3 (PREREQUISITE NOT MET). node scripts/check-plugin-teardown-shape.mjs --self-test lacked pinned fixture commit 621a4876 in the shallow clone (fixed with git fetch --depth=1 of that commit). pnpm --filter @objectstack/lint run check:doc-formula-expressions, pnpm check:dual-build-cjs-loads and pnpm check:lean-entry-closure each lacked a dist (built by the closure builds and by check:type-check-debt's own full build). Re-run, all four gave exit 0, e.g. 'check-plugin-teardown-shape self-test: 48 cases pass', 'check:dual-build-cjs-loads — 111 published require entry point(s) across 69 package(s) load' and 'check-lean-entry-closure: 2 published condition(s) measured from a real load'. Reconciliation: node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --ran ran-final.list gave exit 0, '84 derived famil(ies) accounted for — 84 run, 0 NOT-MEASURED (a DERIVED zero — all 84 recorded an exit code and none of them is 3)'. Spec generated artifacts: pnpm --filter @objectstack/spec check:generated gave exit 0, 'All 14 generated artifacts are up to date'. Not derived: check-platform-checklist (no export renamed or removed), check:dev-prereqs and check:skill-frame-freshness (local-only; the spec dist was freshly built, and the build ran check-dev-prereqs --stamp). CI on PR #24189 when the report was written: 'No other open PR may claim the same issue' success; 'Packed-tarball smoke (opt-in)' skipped. In progress: Check Changeset, Type Check · workspace, Type Check · source gates, Type Check · debt ledger, Type Check · consumer gates, Lint · ESLint, Lint · PM dispatch-gates, Lint · CI, release & engine gates, Lint · docs & code guards, Lint · repo & tooling gates, Lint · scoped (3 jobs), Governed Surface Queue Guard, Check PR Size, The card this PR closes must claim this branch, Part-of PR must not also close its card, No other open PR may claim the same single-writer path, Check Documentation Links, Flag docs affected by code changes, Spec property liveness, filter. NOT MEASURED: CI conclusions, reason: in_progress when reported, and the report is not delayed for CI.",
"line_budget": "n/a",
"files_changed": [
"packages/spec/src/contracts/auth-service.ts (+27/-1)",
"packages/spec/src/contracts/auth-service.test.ts (+21)",
".changeset/24165-mcp-token-organization-id.md (new; @objectstack/spec minor; Clause-②: yes (widening))"
],
"deviations": [
"One mis-typed run: 'pnpm --filter @objectstack/spec test -- --maxWorkers=2' (the bare -- trap) was moved to the background by the harness. I stopped it with TaskStop before it acquired the lock: --status showed slot dev-24165 parked behind another holder, so vitest never ran. I re-ran correctly as 'exec vitest run --project local --maxWorkers=2'.",
"origin/main was NOT merged before opening the PR (AGENTS.md Multi-agent §10 says pull main first). Three commits landed after the base (to 727091c82: runtime observability Proxy, plugin-audit, the spec record:json retirement). The file lists are disjoint from this diff, and this diff moves no generated artifact. A merge would have invalidated the 84-family union measured at 06210ccf8. CI runs on the merge ref and the queue re-verifies.",
"Shared object store: git fetch --depth=1 origin 621a487607881c66b2899b7e3477115229a156b4, a read-only addition (one more shallow root in the common .git), to meet check-plugin-teardown-shape --self-test.",
"Commit trailers: the model-free pair (Claude-Session plus Co-authored-by: Claude) per AGENTS.md, in place of the harness reminder's model-named Co-Authored-By line, which yields to AGENTS.md by its own precedence sentence.",
"Clause-② spelling: the PR body carries the claim's line verbatim, Clause-②: yes. The changeset carries Clause-②: yes (widening), the arm the ruling and the card name. Both read as yes and not breaking."
],
"mcp_calls": "0",
"api_writes": "4 REST writes. (a) POST /repos/objectstack-ai/objectstack/dispatches: the relay stroke for pr_create (request fw-20261017T160455Z-81e097, run 39047850950, success). Inside it the relay sent POST /repos/objectstack-ai/objectstack/pulls (draft #24189) and POST /repos/objectstack-ai/objectstack/issues/24189/assignees (os-project-manager), both as objectstack-fleet[bot]. Read-back: 6189 bytes sent, 6189 stored, identical. (b) The os-dev-report comment on #24165 through scripts/pm/post-stamped.mjs (relay; POST /repos/objectstack-ai/objectstack/issues/24165/comments). git push twice (the empty-branch probe, then 06210ccf8) is not REST. Reads: GET issues/24165, 24165/comments, 24137, 24137/comments, pulls/24189, commits/06210ccf8/check-runs.",
"open_questions": [],
"out_of_scope_findings": [
"carrier: #24137 (its runtime slice edits the same block) · noted, not filed. packages/runtime/src/security/resolve-execution-context.ts:154 says verifyMcpAccessToken is 'implemented (auth-manager.ts) and undeclared'. It was already false on origin/main, because the contract declares the method, and it stays false. The local oauthPrincipal type there still names the three-member shape, and the runtime slice will read organizationId."
]
}
{
"issue": 24165,
"status": "done",
"branch": "claude/issue-24165-mcp-token-organization-id",
"pr": "https://github.com/objectstack-ai/objectstack/pull/24189",
"session": "session_01TyRhWbVbbQpQJTDbW8VahJ (subagent: the parent PM session id)",
"premise_still_valid": true,
"round": "patch round 1 (docs-drift comment 6236317366); the PM lifted the content/docs hold-off for this one file",
"head": "890c170d5097f82d3b2ed310b27e393fc554d6fd",
"summary": "One-file docs patch on top of 06210ccf8, new head 890c170d5. On origin/main, content/docs/kernel/contracts/auth-service.mdx:42 restated the old three-member result of verifyMcpAccessToken (that page is unchanged on main since base cb8756b00). The line now reads verifyMcpAccessToken?(token: string) returning { userId: string; scopes: string[]; clientId?: string; organizationId?: string } or undefined, and the one-line comment at :41 gains one clause: the optional organizationId is the organization the user's OAuth consent bound, absent for a pre-binding or client_credentials token. That comment was the page's only prose on the method: the page's other verifyMcpAccessToken hits are 0. The other five pages the bot named were grepped, and each has exactly 1 IAuthService hit as the positive control. kernel/contracts/index.mdx, kernel/index.mdx, kernel/services-checklist.mdx and kernel/services.mdx have 0 hits for verifyMcpAccessToken, 'scopes: string' and clientId. permissions/authentication.mdx has 0 for verifyMcpAccessToken and 'scopes: string'; its 2 clientId hits (:479, :483) are social-provider config (GOOGLE_CLIENT_ID / GITHUB_CLIENT_ID), not the result shape. None restates it, and none was edited. origin/main was not merged, per the patch instruction. The worktree was recreated from origin/claude/issue-24165-mcp-token-organization-id at 06210ccf8 and removed after the push.",
"pr_body_line_for_what_changed": "- `content/docs/kernel/contracts/auth-service.mdx` (`:41`-`:42`): the interface block's `verifyMcpAccessToken` signature carries `organizationId?: string` as the contract spells the result. Its one-line comment says the optional `organizationId` is the organization the user's OAuth consent bound, absent for a pre-binding or `client_credentials` token. The docs-drift check named five other pages (`kernel/contracts/index.mdx`, `kernel/index.mdx`, `kernel/services-checklist.mdx`, `kernel/services.mdx`, `permissions/authentication.mdx`). Each names `IAuthService` once and none restates the result shape, so they are unchanged.",
"tests": "Docs-only diff, so there is no unit test and no ablation (no tested behaviour changed). On disk: grep -c 'clientId?: string; organizationId?: string }' in the page gave 1. The control-byte self-scan, grep -naP over C0/DEL ranges, gave exit 1 (none). The spec, formula, lint, client and client-react dists the docs gates read came from turbo, FULL TURBO 4/4 and 35/35 cached, because these packages' sources are byte-identical to 06210ccf8.",
"gates": "At HEAD 890c170d5. node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derived 106 commands over 4 paths. The stderr first line names objectstack-ai/objectstack at 890c170d5. That is 22 more than round 1's 84, none dropped. Run this round, with exit codes captured before any pipe ('cmd > log 2>&1; status=$?'), were the 40 that the --json derivation matched to content/docs/kernel/contracts/auth-service.mdx or that are whole-tree. They are the 22 new families (check-doc-frontmatter, check-doc-route-spelling --advisory, check-docs-section-name, check-section-landing-index, each with its --self-test; check:doc-security-posture; check:skill-examples; check:corpus-claim-drift; check:doc-anchors; check:docs-audit-scope; check:docs-redirects; check:docs-single-h1; check:docs-spec-enumerations; check:docs-transcript-drift; check:published-readme-links; check:react-page-adapter-contract; check:role-word; check:skill-identifier-liveness; check:vendor-version-stamps), the 10 round-1 families matched to the page (check-ci-filter-parity, check:doc-formula-expressions, spec check:docs, check:empty-state, check:liveness, check:strictness-ledger, check:variant-docs, check:yaml-examples, check:cross-package-test-inputs, check:doc-authoring) and the 8 whole-tree families. All 40 gave exit 0. check:skill-examples first exited 3 (client-react dist missing), and after the cached client build it re-ran exit 0: '263 prose examples type-check across 3 surface(s)'. Sample verdicts: spec check:docs '227 generated files in sync'; check:doc-anchors '536 internal #fragment link(s) across 423 source file(s) all resolve'; check:docs-spec-enumerations 'the hand-written spec enumerations agree'; check:doc-authoring '18939 customer-facing string(s) ... clean'. The --json matched set also held two CI-only commands with workflow values (check-shard-attestation --emit, check-test-completeness). Neither is in the --commands list; running them locally gave exit 1 and exit 3 respectively, both NOT MEASURED, and they were left out of the record. The other 66 families carry their round-1 exit codes, all 0 at 06210ccf8: the derivation matches them only to the spec files and the changeset, which this commit does not touch. Reconciliation: --ran over that record gave exit 0, '106 derived famil(ies) accounted for — 106 run, 0 NOT-MEASURED (a DERIVED zero)'. CI on 890c170d5 when this was written: success for Lint & Repo Gates, Check Changeset, Build Docs, Check Documentation Links, Flag docs affected by code changes, Governed Surface Queue Guard, Type Check · source gates, Type Check · debt ledger, Dogfood Regression Gate, Spec property liveness, all Lint · lanes, Check PR Size and the claim/closing guards. In progress: Build Core, Test Core 1-6/6, Type Check · workspace, Type Check · consumer gates. On 06210ccf8 the aggregate TypeScript Type Check reads failure, but its annotations say the lanes typecheck-workspace, typecheck-debt and typecheck-consumers 'concluded cancelled -- expected success'. The 890c170d5 push superseded those lanes, so this is not a measured red. NOT MEASURED: the remaining 890c170d5 CI conclusions, reason: in progress when reported.",
"line_budget": "n/a",
"files_changed": [
"content/docs/kernel/contracts/auth-service.mdx (+2/-2), commit 890c170d5; the round-1 files are unchanged"
],
"deviations": [
"None this round beyond the instruction itself: the one content/docs file was edited under the PM's explicit lift of the hold-off, and no other file was touched.",
"Two CI-only commands (check-shard-attestation --emit, check-test-completeness) carry workflow-valued arguments. The --json derivation lists them as matched by the page, but --commands excludes them, and I ran them locally by mistake (exit 1 and exit 3, nothing measured). They are left out of the --ran record."
],
"mcp_calls": "0",
"api_writes": "1 REST write this round: POST /repos/objectstack-ai/objectstack/dispatches, the relay stroke for this os-dev-report comment through scripts/pm/post-stamped.mjs, which sent POST /repos/objectstack-ai/objectstack/issues/24165/comments. git push of 890c170d5 is not REST. Reads: GET issues/comments/6236317366, commits/890c170d5/check-runs, commits/06210ccf8/check-runs, check-runs/{id}/annotations.",
"open_questions": [],
"out_of_scope_findings": []
}
{
"issue": 24165,
"status": "done",
"branch": "claude/issue-24165-mcp-token-organization-id",
"pr": "https://github.com/objectstack-ai/objectstack/pull/24189",
"session": "session_01TyRhWbVbbQpQJTDbW8VahJ (subagent: the parent PM session id)",
"premise_still_valid": true,
"round": "patch round 2 (contract review record 6236564856 FAIL on one point, at head 890c170d5)",
"head": "292bf5f32866179767f547fc8ffee0877d17597c",
"summary": "The client_credentials / machine-to-machine absence clause is removed from all four carriers, and nothing else changed (commit 292bf5f32 on 890c170d5; origin/main not merged). The review's finding holds on the branch: plugin-auth's verifyMcpAccessToken returns null when sub equals client_id or azp, so such a token never reaches this result. Hunks: (1) packages/spec/src/contracts/auth-service.ts organizationId TSDoc: 'Absent when no consent bound one: a token minted before consents bound an organization. Absent means the principal resolves with no organization, so only its global grants apply.' Every other sentence is unchanged. (2) packages/spec/src/contracts/auth-service.test.ts comment: '... as an OPTIONAL member: a token minted before consents bound one verifies with none.' (3) .changeset/24165-mcp-token-organization-id.md Absent bullet: 'Absent when no consent bound one: a token minted before consents bound an organization. Absent means no organization, so only the user's global grants apply.' (4) content/docs/kernel/contracts/auth-service.mdx:41: '... absent for a token minted before consents bound an organization. Optional.' No sentence about M2M refusal was added. A grep for client_credentials, machine-to-machine and M2M over the four files gave exit 1 (none). The control 'a token minted before consents' gave 1 hit per file. Text only: the type, the test's assertions and the exports are unchanged. The round-1 commit message 06210ccf8 still carries the false clause in landed history; it is not rewritten, and the squash title and body are the PM's.",
"pr_body_replacements": {
"what_changed_absent_sentence": " - absent when no consent bound one, that is a token minted before consents bound an organization; absent means no organization, so only global grants apply;",
"patch_round_1_line": "- `content/docs/kernel/contracts/auth-service.mdx` (`:41`-`:42`), patch round 1 at `890c170d5`, corrected in patch round 2 at `292bf5f32`: the interface block's `verifyMcpAccessToken` signature carries `organizationId?: string` as the contract spells the result. Its one-line comment says the optional `organizationId` is the organization the user's OAuth consent bound, absent for a token minted before consents bound an organization. The docs-drift check named five other pages (`kernel/contracts/index.mdx`, `kernel/index.mdx`, `kernel/services-checklist.mdx`, `kernel/services.mdx`, `permissions/authentication.mdx`). Each names `IAuthService` once and none restates the result shape, so they are unchanged. (Added by the dispatching seat from the dev's patch-round reports `6236545086` and this one.)"
},
"tests": "At 292bf5f32, under os-verify-lock. pnpm --filter @objectstack/spec build gave VERDICT command-exit 0, git status --porcelain was empty, and the rebuilt dist/contracts/index.d.ts carries the new sentence (1 hit). pnpm --filter @objectstack/spec typecheck plus pnpm --filter @objectstack/spec exec vitest run --project local --maxWorkers=2 src/contracts/auth-service.test.ts gave VERDICT command-exit 0, 'check:test-typecheck: OK', 'Tests 10 passed (10)'. No ablation and no reverse verification: no type or tested behaviour changed (comment and prose text only), and the round-1 readings of both stand on an identical type. Consumer typechecks were not re-run for the same reason; CI's Type Check · consumer gates is green on 292bf5f32.",
"gates": "At HEAD 292bf5f32. node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derived 106 commands over the same 4 paths, identical to round 2's set; the stderr first line names objectstack-ai/objectstack at 292bf5f32. This commit touches all 4 paths, so all 106 were re-run this round, with exit codes captured before any pipe ('cmd > log 2>&1; status=$?'). First pass: 100 exit 0 and 6 exit 3 (PREREQUISITE NOT MET, unbuilt dists): check:doc-formula-expressions, check:doc-security-posture, check:skill-examples, check:docs-transcript-drift, check:dual-build-cjs-loads and check:lean-entry-closure. check:type-check-debt's own full build (VERDICT command-exit 0, lock held 432s; 're-measure: OK') then built them, and all six re-ran exit 0, e.g. '263 prose examples type-check across 3 surface(s)' and 'check-docs-transcript-drift: 4 declared transcript value(s) across 417 page(s) ... equal what the registry derives today'. Sample verdicts: spec check:docs '227 generated files in sync'; check:api-surface 'public API surface + factory signatures unchanged'; check:doc-authoring clean. Reconciliation: --ran gave exit 0, '106 derived famil(ies) accounted for — 106 run, 0 NOT-MEASURED (a DERIVED zero — all 106 recorded an exit code and none of them is 3)'. CI on 292bf5f32 when this was written: all five required contexts succeeded (Lint & Repo Gates, TypeScript Type Check, Test Core, Build Core, Governed Surface Queue Guard), with every Type Check · lane, Test Core 1-6/6, all Lint · lanes, Build Docs, Dogfood Regression Gate, Spec property liveness, Check Documentation Links, Flag docs affected by code changes, Check PR Size and the claim/closing guards also succeeding. One Check Changeset run succeeded and a second is in progress. Skipped (opt-in or path-scheduled): Console Pin Gate, Dogfood Verify CLI, Temporal Conformance, Packed-tarball smoke, the dogfood shard matrix placeholder.",
"line_budget": "n/a",
"files_changed": [
"packages/spec/src/contracts/auth-service.ts (+2/-4), commit 292bf5f32",
"packages/spec/src/contracts/auth-service.test.ts (+1/-1)",
".changeset/24165-mcp-token-organization-id.md (+1/-1)",
"content/docs/kernel/contracts/auth-service.mdx (+1/-1)"
],
"deviations": [
"The test comment's line 'bound one verifies with none. The typed' was left short rather than re-flowed, to keep the hunk to the removed clause."
],
"mcp_calls": "0",
"api_writes": "1 REST write this round: POST /repos/objectstack-ai/objectstack/dispatches, the relay stroke for this os-dev-report comment through scripts/pm/post-stamped.mjs, which sent POST /repos/objectstack-ai/objectstack/issues/24165/comments. git push of 292bf5f32 is not REST. Reads: GET pulls/24189 (to find the two PR-body lines carrying the clause), commits/292bf5f32/check-runs.",
"open_questions": [],
"out_of_scope_findings": []
}
Correction to this card's body: "It is absent for a token minted before the binding existed, and for an M2M token" is wrong on the M2M half. plugin-auth's verifyMcpAccessToken refuses a client_credentials token outright, so such a token never reaches this result. The landed contract states only the pre-binding absence.
git diff 8a88901be b1845ed637 and the reviewed head 292bf5f328 over its merge base cb8756b00 share git patch-id --stable4f04daf7 (ACCEPT 6237277755, contract review PASS 6237222227)
queue branch
no gh-readonly-queue/main/pr-24189-* ref remains
mis-close scan
Fixes #24165 is the squash message's only closing keyword; #24137 and #24166 stay open, and this card closed completed by that PR alone
pm:dispatched is cleared in this act.
Downstream:#24166 (domain:services) carries Blocked-by: #24165 alone, and its blocker is now closed. Its release is the unlock scan's. One caution: PR #24189 names #24166 in its body, so the release double-check ② reads a merged PR on that card newer than its last transition. #24137 still waits on #24166.
Activity
objectstack-fleet commentedon Oct 17, 2026
Claim: PM loop round 1 · 2026-10-17T14:17Z · the
packages/speccontract slice of #24137:IAuthService.verifyMcpAccessToken's verified result gains the optionalorganizationIdan OAuth consent bound (ruling6232069920, letter A)Session:
session_01TyRhWbVbbQpQJTDbW8VahJ(the seat; the dev is anos-devsubagent of this session)Account:
os-project-manager(the seat's linked user asGET /useranswers it; always the card's assignee)Branch:
claude/issue-24165-mcp-token-organization-idWorktree:
objectstack-issue-24165Domain:
domain:specSeat:
domain:spec#1(seat post #6017)File surface (read on
origin/mainb0fe9fad47; line numbers move; stop on a breach and explain it in the report):packages/spec/src/contracts/auth-service.ts:verifyMcpAccessToken's result type (:297),{ userId: string; scopes: string[]; clientId?: string }, gainsorganizationId?: string, and its TSDoc says what the member is.api-surfaceshard, and any other the spec build regenerates), written by their generators only.packages/spec/src/contracts/if the contract has a type-level test home. One.changeset/24165-*.md(@objectstack/spec,minor: a published contract type gains a member).packages/plugins/plugin-auth/**(plugin-auth: an OAuth consent binds the consenting user's current organization, the access token carries it as organization_id, and verifyMcpAccessToken returns it (slice 2 of #24137, ruled A at 6232069920) #24166,domain:services);packages/runtime/**(finding(mcp/oauth): an OAuth-token MCP agent acting for an ordinary user is denied every object operation — the D10 delegator is resolved with no active organization, so the user's organization-scoped grants do not count #24137,domain:cli);content/docs/**; any new claim, column or stored field beyond the one member the ruling names.Container & model:
Streated asM(one optional member, but its TSDoc states a permission boundary),mode:subagent,model: TIER_DEFAULT(dispatch-gates --tier: no path-derived mandate; the Clause-② SUSPECT hint onpackages/spec/src/**says build at the default tier); review ceilingCONTRACT_REVIEW_TIER, reason:Clause-②: yeson a publishedpackages/spec/src/**contract (contract-review.md, 档随面定). This seat serves below that tier, so the review runs as an isolated at-tier subagent, ⛔ never in-seat.Clause-②: yes
Basis for the Clause-② line: a published contract type gains a member, which widens the public surface; the ruling's own plan names it
Clause-②: yes (widening).Responsibility: n/a — not a defect card (the contract slice of a ruled defect; finding(mcp/oauth): an OAuth-token MCP agent acting for an ordinary user is denied every object operation — the D10 delegator is resolved with no active organization, so the user's organization-scoped grants do not count #24137 carries the defect's three answers)
Thread-read: none
Ruling-ref:
6232069920on finding(mcp/oauth): an OAuth-token MCP agent acting for an ordinary user is denied every object operation — the D10 delegator is resolved with no active organization, so the user's organization-scoped grants do not count #24137 (letter A, the maintainer's 「同意」), retrieved in this act.Serial constraints cleared:
previousisnullon insert in objectstack-data and objectstack-formula, no longer unbound #24174, feat(spec)!: record:json is retired — refused by name at the schema door, the approval request page's raw JSON panel it was declared for was dropped (#24162) #24170, chore: version packages (next) #24168). None touchesauth-service.tsorpackages/spec/src/contracts/.component.zod.ts), [maintainer] validate: thefield-no-consumerswarning is one 856-character line, printed by validate, build and dev alike — one-line verdict +rule:id + a pointer to the full reasoning (os explain, which today takes only schema names) #22161 (packages/lint), lint: a locale pack'spicklists.<name>translations are never checked — an option key that is not in the list, or a list no package declares, passesos lint --strictand is dropped silently at runtime #23566 (packages/lint), metadata-protocol: duplicatePackage renames a token that names both a prefixed package object and one of its fields as the object, so a copied view's column or form field names a field the copy does not have (family close-out) #23561 (metadata protocol), refactor(plugin-security,platform-objects,spec): retire the catalog seeders, the per-organization catalog machinery and the four catalog objects; Setup creation is an environment write undersingleand refused under a wall (ADR-0131 D2/D3/D5/D13) #15204 (pm:epic). None names this file.domain:services) and finding(mcp/oauth): an OAuth-token MCP agent acting for an ordinary user is denied every object operation — the D10 delegator is resolved with no active organization, so the user's organization-scoped grants do not count #24137 (domain:cli) each carryBlocked-by: #24165and wait on this landing.objectstack-fleet commentedon Oct 17, 2026
os-dev-report
{ "issue": 24165, "status": "done", "branch": "claude/issue-24165-mcp-token-organization-id", "pr": "https://github.com/objectstack-ai/objectstack/pull/24189", "session": "session_01TyRhWbVbbQpQJTDbW8VahJ (subagent: the parent PM session id)", "premise_still_valid": true, "summary": "IAuthService.verifyMcpAccessToken's verified result (packages/spec/src/contracts/auth-service.ts) gains organizationId?: string. Its TSDoc says: it is the organization the user's OAuth consent bound, read from the access token's organization_id claim. It is absent for a pre-binding token and for a client_credentials token, and absent means global grants only. It is a claim, not an authorization: the caller vets it on every request by the session's vetOrganizationClaim rule, an ended membership resolves to no organization, and it is never the union of the user's organizations or the browser session's current organization. Under ADR-0090 D10 it is the organization the delegator is resolved in. No tracker numbers in the new prose. One typed-read case in the interface's existing test home (auth-service.test.ts) and a minor changeset. Premise H1 held on origin/main cb8756b00 (line :297, no organizationId). H2 held: the one implementer and the runtime reader are untouched and every consumer typechecks. H4 is FALSIFIED: no generated artifact moved, because api-surface records export names and no export was added (build left git status empty; check:generated 14/14 up to date). Draft PR #24189 is open with assignee os-project-manager and no labels, because the dispatch named none and skip-changeset does not apply.", "tests": "All at HEAD 06210ccf8. (1) Under os-verify-lock: pnpm --filter @objectstack/spec build gave 'VERDICT command-exit 0' and git status --porcelain was empty. The rebuilt dist/contracts/index.d.ts and index.d.mts each carry the member (1 hit); index.js and index.mjs carry 0 hits, so the interface is erased and no emitted JS byte changed. (2) pnpm --filter @objectstack/spec exec vitest run --project local --maxWorkers=2 src/contracts/auth-service.test.ts: 'Tests 10 passed (10)'. The full suite, pnpm --filter @objectstack/spec exec vitest run --project local --maxWorkers=2: 'Test Files 692 passed (692)', 'Tests 20975 passed | 1 todo', VERDICT command-exit 0. (3) pnpm --filter @objectstack/spec typecheck: VERDICT command-exit 0; 'check:test-typecheck: OK'. (4) Downstream consumers holding IAuthService. Closures were built with pnpm exec turbo run build --filter='@objectstack/mcp^...' (7 tasks) and --filter on plugin-auth^..., runtime^... and cloud-connection^... (31 tasks), each with --concurrency=2 and VERDICT command-exit 0. Then typecheck: @objectstack/mcp exit 0, @objectstack/plugin-auth exit 0, @objectstack/runtime exit 0, @objectstack/cloud-connection exit 0. Direction: these four are downstream consumers of @objectstack/spec. Their runtime test suites were not run locally: no emitted JS changed (evidence in 1), so they are declared to CI. (5) Reverse verification, one-off: a temporary packages/mcp/src/zz-reverse-24165.ts read organizationId as string | undefined and assigned organizationId: 123 on an annotated result. pnpm --filter @objectstack/mcp exec tsc --noEmit gave exactly one error, 'src/zz-reverse-24165.ts(9,62): error TS2322: Type number is not assignable to type string'. The old declaration would give TS2339 plus TS2353 instead. The file was deleted and the tree was clean. Observed direction: red as expected. (6) Ablation, one-off: node scripts/ablation-replace.mjs --delete of ' organizationId?: string;' in auth-service.ts (anchor x1 to x0, blob e2d1d8138861 to db2648c7b9f2), with a bash EXIT/INT/TERM trap restoring git checkout HEAD on the absolute path, wrapping pnpm --filter @objectstack/spec check:test-typecheck. That went red: 'src/contracts/auth-service.test.ts: 2 type error(s) in a file the ledger does not cover'. Restore was proven: blob after restore equals HEAD (e2d1d8138861), git diff HEAD empty, porcelain empty. No dist leg was needed, because the test imports ./auth-service from source.", "gates": "Local subset at HEAD 06210ccf8. node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack gave 84 commands; the stderr first line names objectstack-ai/objectstack at 06210ccf8; 3 paths. Every command ran with its exit code captured before any pipe ('cmd > log 2>&1; status=$?'). First pass: 80 exit 0 and 4 exit 3 (PREREQUISITE NOT MET). node scripts/check-plugin-teardown-shape.mjs --self-test lacked pinned fixture commit 621a4876 in the shallow clone (fixed with git fetch --depth=1 of that commit). pnpm --filter @objectstack/lint run check:doc-formula-expressions, pnpm check:dual-build-cjs-loads and pnpm check:lean-entry-closure each lacked a dist (built by the closure builds and by check:type-check-debt's own full build). Re-run, all four gave exit 0, e.g. 'check-plugin-teardown-shape self-test: 48 cases pass', 'check:dual-build-cjs-loads — 111 published require entry point(s) across 69 package(s) load' and 'check-lean-entry-closure: 2 published condition(s) measured from a real load'. Reconciliation: node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --ran ran-final.list gave exit 0, '84 derived famil(ies) accounted for — 84 run, 0 NOT-MEASURED (a DERIVED zero — all 84 recorded an exit code and none of them is 3)'. Spec generated artifacts: pnpm --filter @objectstack/spec check:generated gave exit 0, 'All 14 generated artifacts are up to date'. Not derived: check-platform-checklist (no export renamed or removed), check:dev-prereqs and check:skill-frame-freshness (local-only; the spec dist was freshly built, and the build ran check-dev-prereqs --stamp). CI on PR #24189 when the report was written: 'No other open PR may claim the same issue' success; 'Packed-tarball smoke (opt-in)' skipped. In progress: Check Changeset, Type Check · workspace, Type Check · source gates, Type Check · debt ledger, Type Check · consumer gates, Lint · ESLint, Lint · PM dispatch-gates, Lint · CI, release & engine gates, Lint · docs & code guards, Lint · repo & tooling gates, Lint · scoped (3 jobs), Governed Surface Queue Guard, Check PR Size, The card this PR closes must claim this branch, Part-of PR must not also close its card, No other open PR may claim the same single-writer path, Check Documentation Links, Flag docs affected by code changes, Spec property liveness, filter. NOT MEASURED: CI conclusions, reason: in_progress when reported, and the report is not delayed for CI.", "line_budget": "n/a", "files_changed": [ "packages/spec/src/contracts/auth-service.ts (+27/-1)", "packages/spec/src/contracts/auth-service.test.ts (+21)", ".changeset/24165-mcp-token-organization-id.md (new; @objectstack/spec minor; Clause-②: yes (widening))" ], "deviations": [ "One mis-typed run: 'pnpm --filter @objectstack/spec test -- --maxWorkers=2' (the bare -- trap) was moved to the background by the harness. I stopped it with TaskStop before it acquired the lock: --status showed slot dev-24165 parked behind another holder, so vitest never ran. I re-ran correctly as 'exec vitest run --project local --maxWorkers=2'.", "origin/main was NOT merged before opening the PR (AGENTS.md Multi-agent §10 says pull main first). Three commits landed after the base (to 727091c82: runtime observability Proxy, plugin-audit, the spec record:json retirement). The file lists are disjoint from this diff, and this diff moves no generated artifact. A merge would have invalidated the 84-family union measured at 06210ccf8. CI runs on the merge ref and the queue re-verifies.", "Shared object store: git fetch --depth=1 origin 621a487607881c66b2899b7e3477115229a156b4, a read-only addition (one more shallow root in the common .git), to meet check-plugin-teardown-shape --self-test.", "Commit trailers: the model-free pair (Claude-Session plus Co-authored-by: Claude) per AGENTS.md, in place of the harness reminder's model-named Co-Authored-By line, which yields to AGENTS.md by its own precedence sentence.", "Clause-② spelling: the PR body carries the claim's line verbatim, Clause-②: yes. The changeset carries Clause-②: yes (widening), the arm the ruling and the card name. Both read as yes and not breaking." ], "mcp_calls": "0", "api_writes": "4 REST writes. (a) POST /repos/objectstack-ai/objectstack/dispatches: the relay stroke for pr_create (request fw-20261017T160455Z-81e097, run 39047850950, success). Inside it the relay sent POST /repos/objectstack-ai/objectstack/pulls (draft #24189) and POST /repos/objectstack-ai/objectstack/issues/24189/assignees (os-project-manager), both as objectstack-fleet[bot]. Read-back: 6189 bytes sent, 6189 stored, identical. (b) The os-dev-report comment on #24165 through scripts/pm/post-stamped.mjs (relay; POST /repos/objectstack-ai/objectstack/issues/24165/comments). git push twice (the empty-branch probe, then 06210ccf8) is not REST. Reads: GET issues/24165, 24165/comments, 24137, 24137/comments, pulls/24189, commits/06210ccf8/check-runs.", "open_questions": [], "out_of_scope_findings": [ "carrier: #24137 (its runtime slice edits the same block) · noted, not filed. packages/runtime/src/security/resolve-execution-context.ts:154 says verifyMcpAccessToken is 'implemented (auth-manager.ts) and undeclared'. It was already false on origin/main, because the contract declares the method, and it stays false. The local oauthPrincipal type there still names the three-member shape, and the runtime slice will read organizationId." ] }Generated by Claude Code
objectstack-fleet commentedon Oct 17, 2026
os-dev-report
{ "issue": 24165, "status": "done", "branch": "claude/issue-24165-mcp-token-organization-id", "pr": "https://github.com/objectstack-ai/objectstack/pull/24189", "session": "session_01TyRhWbVbbQpQJTDbW8VahJ (subagent: the parent PM session id)", "premise_still_valid": true, "round": "patch round 1 (docs-drift comment 6236317366); the PM lifted the content/docs hold-off for this one file", "head": "890c170d5097f82d3b2ed310b27e393fc554d6fd", "summary": "One-file docs patch on top of 06210ccf8, new head 890c170d5. On origin/main, content/docs/kernel/contracts/auth-service.mdx:42 restated the old three-member result of verifyMcpAccessToken (that page is unchanged on main since base cb8756b00). The line now reads verifyMcpAccessToken?(token: string) returning { userId: string; scopes: string[]; clientId?: string; organizationId?: string } or undefined, and the one-line comment at :41 gains one clause: the optional organizationId is the organization the user's OAuth consent bound, absent for a pre-binding or client_credentials token. That comment was the page's only prose on the method: the page's other verifyMcpAccessToken hits are 0. The other five pages the bot named were grepped, and each has exactly 1 IAuthService hit as the positive control. kernel/contracts/index.mdx, kernel/index.mdx, kernel/services-checklist.mdx and kernel/services.mdx have 0 hits for verifyMcpAccessToken, 'scopes: string' and clientId. permissions/authentication.mdx has 0 for verifyMcpAccessToken and 'scopes: string'; its 2 clientId hits (:479, :483) are social-provider config (GOOGLE_CLIENT_ID / GITHUB_CLIENT_ID), not the result shape. None restates it, and none was edited. origin/main was not merged, per the patch instruction. The worktree was recreated from origin/claude/issue-24165-mcp-token-organization-id at 06210ccf8 and removed after the push.", "pr_body_line_for_what_changed": "- `content/docs/kernel/contracts/auth-service.mdx` (`:41`-`:42`): the interface block's `verifyMcpAccessToken` signature carries `organizationId?: string` as the contract spells the result. Its one-line comment says the optional `organizationId` is the organization the user's OAuth consent bound, absent for a pre-binding or `client_credentials` token. The docs-drift check named five other pages (`kernel/contracts/index.mdx`, `kernel/index.mdx`, `kernel/services-checklist.mdx`, `kernel/services.mdx`, `permissions/authentication.mdx`). Each names `IAuthService` once and none restates the result shape, so they are unchanged.", "tests": "Docs-only diff, so there is no unit test and no ablation (no tested behaviour changed). On disk: grep -c 'clientId?: string; organizationId?: string }' in the page gave 1. The control-byte self-scan, grep -naP over C0/DEL ranges, gave exit 1 (none). The spec, formula, lint, client and client-react dists the docs gates read came from turbo, FULL TURBO 4/4 and 35/35 cached, because these packages' sources are byte-identical to 06210ccf8.", "gates": "At HEAD 890c170d5. node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derived 106 commands over 4 paths. The stderr first line names objectstack-ai/objectstack at 890c170d5. That is 22 more than round 1's 84, none dropped. Run this round, with exit codes captured before any pipe ('cmd > log 2>&1; status=$?'), were the 40 that the --json derivation matched to content/docs/kernel/contracts/auth-service.mdx or that are whole-tree. They are the 22 new families (check-doc-frontmatter, check-doc-route-spelling --advisory, check-docs-section-name, check-section-landing-index, each with its --self-test; check:doc-security-posture; check:skill-examples; check:corpus-claim-drift; check:doc-anchors; check:docs-audit-scope; check:docs-redirects; check:docs-single-h1; check:docs-spec-enumerations; check:docs-transcript-drift; check:published-readme-links; check:react-page-adapter-contract; check:role-word; check:skill-identifier-liveness; check:vendor-version-stamps), the 10 round-1 families matched to the page (check-ci-filter-parity, check:doc-formula-expressions, spec check:docs, check:empty-state, check:liveness, check:strictness-ledger, check:variant-docs, check:yaml-examples, check:cross-package-test-inputs, check:doc-authoring) and the 8 whole-tree families. All 40 gave exit 0. check:skill-examples first exited 3 (client-react dist missing), and after the cached client build it re-ran exit 0: '263 prose examples type-check across 3 surface(s)'. Sample verdicts: spec check:docs '227 generated files in sync'; check:doc-anchors '536 internal #fragment link(s) across 423 source file(s) all resolve'; check:docs-spec-enumerations 'the hand-written spec enumerations agree'; check:doc-authoring '18939 customer-facing string(s) ... clean'. The --json matched set also held two CI-only commands with workflow values (check-shard-attestation --emit, check-test-completeness). Neither is in the --commands list; running them locally gave exit 1 and exit 3 respectively, both NOT MEASURED, and they were left out of the record. The other 66 families carry their round-1 exit codes, all 0 at 06210ccf8: the derivation matches them only to the spec files and the changeset, which this commit does not touch. Reconciliation: --ran over that record gave exit 0, '106 derived famil(ies) accounted for — 106 run, 0 NOT-MEASURED (a DERIVED zero)'. CI on 890c170d5 when this was written: success for Lint & Repo Gates, Check Changeset, Build Docs, Check Documentation Links, Flag docs affected by code changes, Governed Surface Queue Guard, Type Check · source gates, Type Check · debt ledger, Dogfood Regression Gate, Spec property liveness, all Lint · lanes, Check PR Size and the claim/closing guards. In progress: Build Core, Test Core 1-6/6, Type Check · workspace, Type Check · consumer gates. On 06210ccf8 the aggregate TypeScript Type Check reads failure, but its annotations say the lanes typecheck-workspace, typecheck-debt and typecheck-consumers 'concluded cancelled -- expected success'. The 890c170d5 push superseded those lanes, so this is not a measured red. NOT MEASURED: the remaining 890c170d5 CI conclusions, reason: in progress when reported.", "line_budget": "n/a", "files_changed": [ "content/docs/kernel/contracts/auth-service.mdx (+2/-2), commit 890c170d5; the round-1 files are unchanged" ], "deviations": [ "None this round beyond the instruction itself: the one content/docs file was edited under the PM's explicit lift of the hold-off, and no other file was touched.", "Two CI-only commands (check-shard-attestation --emit, check-test-completeness) carry workflow-valued arguments. The --json derivation lists them as matched by the page, but --commands excludes them, and I ran them locally by mistake (exit 1 and exit 3, nothing measured). They are left out of the --ran record." ], "mcp_calls": "0", "api_writes": "1 REST write this round: POST /repos/objectstack-ai/objectstack/dispatches, the relay stroke for this os-dev-report comment through scripts/pm/post-stamped.mjs, which sent POST /repos/objectstack-ai/objectstack/issues/24165/comments. git push of 890c170d5 is not REST. Reads: GET issues/comments/6236317366, commits/890c170d5/check-runs, commits/06210ccf8/check-runs, check-runs/{id}/annotations.", "open_questions": [], "out_of_scope_findings": [] }Generated by Claude Code
objectstack-fleet commentedon Oct 17, 2026
os-dev-report
{ "issue": 24165, "status": "done", "branch": "claude/issue-24165-mcp-token-organization-id", "pr": "https://github.com/objectstack-ai/objectstack/pull/24189", "session": "session_01TyRhWbVbbQpQJTDbW8VahJ (subagent: the parent PM session id)", "premise_still_valid": true, "round": "patch round 2 (contract review record 6236564856 FAIL on one point, at head 890c170d5)", "head": "292bf5f32866179767f547fc8ffee0877d17597c", "summary": "The client_credentials / machine-to-machine absence clause is removed from all four carriers, and nothing else changed (commit 292bf5f32 on 890c170d5; origin/main not merged). The review's finding holds on the branch: plugin-auth's verifyMcpAccessToken returns null when sub equals client_id or azp, so such a token never reaches this result. Hunks: (1) packages/spec/src/contracts/auth-service.ts organizationId TSDoc: 'Absent when no consent bound one: a token minted before consents bound an organization. Absent means the principal resolves with no organization, so only its global grants apply.' Every other sentence is unchanged. (2) packages/spec/src/contracts/auth-service.test.ts comment: '... as an OPTIONAL member: a token minted before consents bound one verifies with none.' (3) .changeset/24165-mcp-token-organization-id.md Absent bullet: 'Absent when no consent bound one: a token minted before consents bound an organization. Absent means no organization, so only the user's global grants apply.' (4) content/docs/kernel/contracts/auth-service.mdx:41: '... absent for a token minted before consents bound an organization. Optional.' No sentence about M2M refusal was added. A grep for client_credentials, machine-to-machine and M2M over the four files gave exit 1 (none). The control 'a token minted before consents' gave 1 hit per file. Text only: the type, the test's assertions and the exports are unchanged. The round-1 commit message 06210ccf8 still carries the false clause in landed history; it is not rewritten, and the squash title and body are the PM's.", "pr_body_replacements": { "what_changed_absent_sentence": " - absent when no consent bound one, that is a token minted before consents bound an organization; absent means no organization, so only global grants apply;", "patch_round_1_line": "- `content/docs/kernel/contracts/auth-service.mdx` (`:41`-`:42`), patch round 1 at `890c170d5`, corrected in patch round 2 at `292bf5f32`: the interface block's `verifyMcpAccessToken` signature carries `organizationId?: string` as the contract spells the result. Its one-line comment says the optional `organizationId` is the organization the user's OAuth consent bound, absent for a token minted before consents bound an organization. The docs-drift check named five other pages (`kernel/contracts/index.mdx`, `kernel/index.mdx`, `kernel/services-checklist.mdx`, `kernel/services.mdx`, `permissions/authentication.mdx`). Each names `IAuthService` once and none restates the result shape, so they are unchanged. (Added by the dispatching seat from the dev's patch-round reports `6236545086` and this one.)" }, "tests": "At 292bf5f32, under os-verify-lock. pnpm --filter @objectstack/spec build gave VERDICT command-exit 0, git status --porcelain was empty, and the rebuilt dist/contracts/index.d.ts carries the new sentence (1 hit). pnpm --filter @objectstack/spec typecheck plus pnpm --filter @objectstack/spec exec vitest run --project local --maxWorkers=2 src/contracts/auth-service.test.ts gave VERDICT command-exit 0, 'check:test-typecheck: OK', 'Tests 10 passed (10)'. No ablation and no reverse verification: no type or tested behaviour changed (comment and prose text only), and the round-1 readings of both stand on an identical type. Consumer typechecks were not re-run for the same reason; CI's Type Check · consumer gates is green on 292bf5f32.", "gates": "At HEAD 292bf5f32. node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derived 106 commands over the same 4 paths, identical to round 2's set; the stderr first line names objectstack-ai/objectstack at 292bf5f32. This commit touches all 4 paths, so all 106 were re-run this round, with exit codes captured before any pipe ('cmd > log 2>&1; status=$?'). First pass: 100 exit 0 and 6 exit 3 (PREREQUISITE NOT MET, unbuilt dists): check:doc-formula-expressions, check:doc-security-posture, check:skill-examples, check:docs-transcript-drift, check:dual-build-cjs-loads and check:lean-entry-closure. check:type-check-debt's own full build (VERDICT command-exit 0, lock held 432s; 're-measure: OK') then built them, and all six re-ran exit 0, e.g. '263 prose examples type-check across 3 surface(s)' and 'check-docs-transcript-drift: 4 declared transcript value(s) across 417 page(s) ... equal what the registry derives today'. Sample verdicts: spec check:docs '227 generated files in sync'; check:api-surface 'public API surface + factory signatures unchanged'; check:doc-authoring clean. Reconciliation: --ran gave exit 0, '106 derived famil(ies) accounted for — 106 run, 0 NOT-MEASURED (a DERIVED zero — all 106 recorded an exit code and none of them is 3)'. CI on 292bf5f32 when this was written: all five required contexts succeeded (Lint & Repo Gates, TypeScript Type Check, Test Core, Build Core, Governed Surface Queue Guard), with every Type Check · lane, Test Core 1-6/6, all Lint · lanes, Build Docs, Dogfood Regression Gate, Spec property liveness, Check Documentation Links, Flag docs affected by code changes, Check PR Size and the claim/closing guards also succeeding. One Check Changeset run succeeded and a second is in progress. Skipped (opt-in or path-scheduled): Console Pin Gate, Dogfood Verify CLI, Temporal Conformance, Packed-tarball smoke, the dogfood shard matrix placeholder.", "line_budget": "n/a", "files_changed": [ "packages/spec/src/contracts/auth-service.ts (+2/-4), commit 292bf5f32", "packages/spec/src/contracts/auth-service.test.ts (+1/-1)", ".changeset/24165-mcp-token-organization-id.md (+1/-1)", "content/docs/kernel/contracts/auth-service.mdx (+1/-1)" ], "deviations": [ "The test comment's line 'bound one verifies with none. The typed' was left short rather than re-flowed, to keep the hunk to the removed clause." ], "mcp_calls": "0", "api_writes": "1 REST write this round: POST /repos/objectstack-ai/objectstack/dispatches, the relay stroke for this os-dev-report comment through scripts/pm/post-stamped.mjs, which sent POST /repos/objectstack-ai/objectstack/issues/24165/comments. git push of 292bf5f32 is not REST. Reads: GET pulls/24189 (to find the two PR-body lines carrying the clause), commits/292bf5f32/check-runs.", "open_questions": [], "out_of_scope_findings": [] }Generated by Claude Code
objectstack-fleet commentedon Oct 17, 2026
ACCEPT → PR #24189 comment 6237277755 · contract review PASS
6237222227(after FAIL6236564856, answered by patch round 2) · ready and auto-merge armed 2026-10-17T17:09Z ·domain:specseat 1 · sessionsession_01TyRhWbVbbQpQJTDbW8VahJCorrection to this card's body: "It is absent for a token minted before the binding existed, and for an M2M token" is wrong on the M2M half.
plugin-auth'sverifyMcpAccessTokenrefuses aclient_credentialstoken outright, so such a token never reaches this result. The landed contract states only the pre-binding absence.Generated by Claude Code
objectstack-fleet commentedon Oct 17, 2026
Landed · PR #24189 →
b1845ed637(Fixes #24165, the verified MCP access-token result carries the optionalorganizationId) ·domain:specseat 1 ·os-project-manager· sessionsession_01TyRhWbVbbQpQJTDbW8VahJ· 2026-10-17T17:40Zb1845ed637on parent8a88901beorigin/maingit merge-base --is-ancestor b1845ed637 origin/main→ exit 0 (control2bcaefba9→ exit 0)git diff 8a88901be b1845ed637and the reviewed head292bf5f328over its merge basecb8756b00sharegit patch-id --stable4f04daf7(ACCEPT6237277755, contract review PASS6237222227)gh-readonly-queue/main/pr-24189-*ref remainsFixes #24165is the squash message's only closing keyword; #24137 and #24166 stay open, and this card closedcompletedby that PR alonepm:dispatchedis cleared in this act.Downstream: #24166 (
domain:services) carriesBlocked-by: #24165alone, and its blocker is now closed. Its release is the unlock scan's. One caution: PR #24189 names #24166 in its body, so the release double-check ② reads a merged PR on that card newer than its last transition. #24137 still waits on #24166.Generated by Claude Code