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
[Decision] auth: an agent forward credential — a server-constructed, target-bound, single-use credential so an OAuth- or API-key-connected agent can run an api action whose target is an identity route (ADR-0090 D10 amendment) #28367
Ruled: 6545950368 · go · 2026-10-31T16:20Z
Ruling-ref: 6545950368
Filing gate: ③ a decision the maintainer asked to have filed as its own card (project chat, 2026-10-31, 「其他同意」 on the #28178 ruling batch). Filed by domain:server seat 3. ⛔ Not a claim. No grade: triage sets the lane and grade. ⛔ Do not start work before triage grades it.
Why this card exists
Under the #28178 ruling (option B), an api action is refused by name before any forward when the caller's credential kind cannot be accepted by the target door. One table, credential kind × target door, is shared by run_action and list_actions, and the #28217 refusal (an API key forwarding to an identity route) rides the same table. That keeps every agent fail-closed and located, but it also means an agent connected over OAuth or with an API key cannot run an api action whose target is an identity route at all.
This card is the other branch of that decision: whether, and how, such an agent may run those actions without being widened to the user's full reach.
The shape the maintainer agreed to file (option A of #28178, refined)
The server constructs the credential. It is not the caller's bearer or key, so the caller's token never travels to the target.
It is bound to its target. It is good for the one forward it was minted for, and only that one: one method, one route, one action run.
It is single-use and short-lived, and it cannot be replayed or reached from outside the process.
It keeps the agent's ceiling at the target. The target resolves it to the same agent principal: the scope-derived permissions, onBehalfOf, and the performedBy attribution. The agent is never given a human session. Option C, minting a human session, was rejected on the record.
It is an amendment to ADR-0090 D10. It also needs a rule for scope families at a target outside MCP, so that a target gated only by a system permission cannot be reached past the agent's consented tool families.
Size and gates
Size L. Tier H (ADR edit). A contract review is owed. The #28178 refusal table is its starting point, and the cells this card turns from "refused by name" to "accepted with the forward credential" are named in the ADR amendment before any code.
Triage's lean, given to the maintainer directly: go, as shaped. The server constructs the credential; it is target-bound, single-use and short-lived; the agent keeps its ceiling at the target; no human session is minted (C stays rejected). The ADR-0090 D10 amendment, naming exactly which cells flip, lands and is reviewed (Tier H) before any code.
p2: agents stay fail-closed and located meanwhile, which is not harmful.
Director seat (the maintainer's decisions thread, 2026-10-31). Presented to the maintainer in batch #434 (five items: #27767, #28345, #28313, #28367, and the any / or key question on PR #28380) with the latest four-facet letter on this card as the seat's reading; the seat's increment and the maintainer's letter follow in the ruling comment.
Director seat (the maintainer's decisions thread). Presented in batch #434 (brief-claimed on this card; detailed letter in the project's decision doc, batch 3). The maintainer answered the batch at 2026-10-31T16:12Z, verbatim: 「同意」 = every item at the seat's recommendation, which for this card is go, as the card and triage 6540849042 shaped it.
What the ruling binds: build the agent forward credential in the shape already agreed under #28178 A — a credential the server mints itself (never the caller's bearer or API key), bound to this one forward (one method, one route, one action run), single-use, short-lived, non-replayable, unreachable out of process, resolving at the target to the same agent principal (scope-derived permissions, onBehalfOf, performedBy attribution); an agent is never handed a human session (option C stays refused). ADR-0090 D10 is revised to name, cell by cell, which rows of the #28178 B refusal table flip from "refuse by name" to "accept with a forward credential"; every other cell keeps refusing by name. A rule for non-MCP targets limits them by scope family so a target that gates only on system permissions cannot exceed the tool families the agent consented to.
Order: ADR revision PR first (Tier H, maintainer approves via os-zhuang), contract review, then code. p2; no urgency change.
Workload:domain:server · ADR PR + code PR · L · Clause-② widening at the named cells only · no precedent.
Execution: triage writes Ruled: on the card with Ruling-ref: naming this comment; the server lane dispatches the ADR revision first. Label strokes are the dispatching seat's.
Claim: step 1, the ADR-0090 D10 revision · card thread · 2026-10-31T17:05Z
Session: session_01L9qibz3ZUbZ8Qcn1rDw9yc (the seat); the card thread is session_01L9qibyagSyLB5ckJsKghoa
Account: os-tesla (the seat's linked user; the card's assignee)
Branch: claude/project-thread-hem8y5 (the card thread's harness-pinned branch; it names no issue, so this claim is the identity record)
Worktree: the card thread's own container checkout
Domain: domain:server
Seat: domain:server#1
Ruling-ref: 6545950368 (go, as shaped; Tesla 「同意」 2026-10-31T16:12Z, batch #434 item 4). Order: the ADR revision first, then contract review, then code.
File surface: docs/adr/0090-permission-model-v2-concept-convergence.md only (the dated D10 amendment). ⛔ No packages/** in this step.
Container & model: M, mode:thread, model: opus, effort high. Tier H (os-zhuang approves). Contract review is owed.
Clause-②: yes (widening, at the named cells of the #28178 B refusal table only)
Metadata-protocol: none
Thread-read: 6545950368
Premise: the #28178 B refusal table and triage shaping 6540849042 are the starting point; the dev reads both before drafting
Serial constraints cleared: no in-flight PR edits ADR-0090
Resume-from: none (fresh from origin/main706461574)
Order: /mnt/project-files/server-seat-1/r2026-10-31/dispatch-28367-adr.md (project file, not public)
State: pm:queue → pm:dispatched, assigned to os-tesla, recorded by this claim.
{
"issue": 28367,
"status": "done",
"branch": "claude/project-thread-hem8y5",
"pr": "https://github.com/objectstack-ai/objectstack/pull/28455",
"head": "5e899ebd5b31cd902ba31cfe584dd41b3dfad2a8",
"draft": true,
"session": "session_01L9qibyagSyLB5ckJsKghoa",
"premise_still_valid": true,
"summary": "Step 1 of ruling 6545950368: ADR-0090 D10 gains rule 5, the agent forward credential (server-minted after the action door's gates, bound to one method/route/action run, single-use, short-lived, in-process only; resolves at the target to the same agent principal; no human session). The #28178 B table is reproduced with two cells flipped (oauth x identity, oauth x platform); api_key x identity keeps refusing by name because an API key is its owner's credential, not an agent principal (D10 2026-09-16 note). Adds the scope-family rule for non-MCP targets and names the code half's packages, doors and pins. No code.",
"tests": "n/a: ADR-only diff, no tested behaviour changed, so no ablation",
"gates": {
"derived": 21,
"run": 21,
"first_pass_nonzero": [
"8 families :: exit 3 (no node_modules in the fresh worktree) -> pnpm install --frozen-lockfile -> 7 rerun :: exit 0",
"pnpm --filter @objectstack/lint run check:doc-formula-expressions :: exit 3 (formula/lint dist absent) -> turbo build of both under the verify lock -> :: exit 0"
],
"final_nonzero": 0,
"ran_reconciliation": "21 derived, 21 run, 0 NOT-MEASURED (exit 0)",
"extra": "node scripts/check-role-word.mjs :: exit 0",
"lint": "n/a: no package touched",
"ci": "in_progress at report time; not waited on"
},
"dogfood_battery": "n/a: no ! in the PR title and no (narrowing) arm",
"line_budget": "192 changed lines (+190 / -2, 1 file) vs the 3000 human-merge threshold: under",
"files_changed": ["docs/adr/0090-permission-model-v2-concept-convergence.md"],
"file_surface": "exactly the claim's one file",
"deviations": [
"No independent model review run here: the seat runs the contract review per the order.",
"Clause-② line copied verbatim from claim 6546502251 (it reads as yes, arm widening, through clause2-line)."
],
"mcp_calls": "0",
"api_writes": "2: POST /repos/objectstack-ai/objectstack/dispatches (relay pr_create, run 41224200331, read-back identical 7636 bytes, label skip-changeset); this os-dev-report comment via post-stamped.mjs. git push x1 (not REST). Card and thread reads were unauthenticated public REST GETs.",
"open_questions": [
{
"question": "Should api_key x identity also flip? The card title names API-key-connected agents; the ruling binds the credential to an agent principal, which an API key is not (D10 2026-09-16 note).",
"options": ["A keep refusing by name, as the amendment says", "B flip it, carrying the key owner's principal"],
"recommendation": "A, because B carries the owner's whole reach at the identity mount, which is option C (a human session) under another name"
}
],
"out_of_scope_findings": []
}
git grep -c "Amendment (2026-10-31, #28367)" in ADR-0090 on origin/main
present
Landed: ADR-0090 D10 gains rule 5, the agent forward credential. The table is flipped at oauth × identity and oauth × platform under the scope-family rule. This is step 1 of the go ruling (6545950368): the ADR first.
Open for the maintainer: whether api_key × identity also flips is batch Release v0.7.1 #436 item 1 (director 6546726515). The seat and the lane both recommend that it keeps refusing. A flip would be a follow-up ADR edit.
Next: step 2, the code PR (runtime, rest, plugin-auth). It owes the amendment's pin list, an ablation of each refusal, and corrections to the two sentences the amendment names. It gets its own contract review. The seat dispatches it on the same claim.
The claim 6546502251 stays open for step 2. The card stays pm:dispatched.
Claim amendment to 6546502251 · 2026-10-31T20:41Z · step 2 (code)
Step 1 landed as a99de440 (landing record 6548823976). This claim now covers step 2 on the same thread and branch, restarted from origin/main.
File surface: the doors that ADR-0090 D10 rule 5 names (packages/runtime action doors and security/resolve-execution-context.ts, packages/rest context resolution, packages/plugins/plugin-auth identity mount), their tests, the two sentences in ADR-0090 the amendment names, and a .changeset/28367-*.md. ⛔ No packages/spec, no metadata-protocol/src/protocol.ts.
Tier: H if the PR carries the two ADR sentences (docs/adr/**). The seat requests the reviewer.
Clause-②: yes (widening, at the two named cells only)
Metadata-protocol: none
Order: /mnt/project-files/server-seat-1/r2026-10-31/dispatch-28367-code.md (project file, not public)
Everything else stands as claimed.
{
"issue": 28367,
"status": "done",
"branch": "claude/project-thread-hem8y5",
"pr": "https://github.com/objectstack-ai/objectstack/pull/28482",
"head": "6bbf2eeb5d7398a56d31ee3828457d2439c602a1",
"draft": true,
"session": "session_01L9qibyagSyLB5ckJsKghoa",
"premise_still_valid": true,
"summary": "Step 2 of ruling 6545950368, the code half of ADR-0090 D10 rule 5 (#28455). An OAuth agent's api-action forward now carries a server-minted forward credential instead of being refused: bound to method, route and action run, spent on first presentation, revoked when the forward answers, 30 s bound, redeemable only inside the forward's own in-process async chain. The runtime HTTP dispatcher and the REST face resolve it to the same agent principal through the shared assembler; the identity mount shows better-auth a request-scoped in-memory view of the delegating user (no session row, token or cookie) and strips credential response headers. Exactly two cells flip (oauth x identity, oauth x platform, the latter under the scope-family rule). api_key x identity keeps refusing by name per ruling 6548923676. The two named ADR sentences and the docs table row are corrected.",
"live_measure": "main 52306d40, before the code: both flipped cells refused by name, api_key x identity refused, api_key x platform served, outside OAuth token 401 at the data door and the identity mount (2 dogfood files, 16/16). Kept at rule level in out/28367-mechanism.md.",
"tests": "unit: forward-credential (9), rest-forward-credential-agent (5), resolve-execution-context.forward-credential (5), agent-forward-identity-view (5), api-action-forward #28367 describe (file 58); dogfood: mcp-oauth-api-action-forward (10), mcp-system-object-action-exposure (12). Ablations: 18 unit + 4 dogfood refusal/property pins each red with the forbidden behaviour put back, restored byte-identical. Sibling suites: @objectstack/rest 6884 passed, @objectstack/mcp 603 passed, @objectstack/runtime 8055 passed, @objectstack/plugin-auth 3938 passed (after updating the two plugin-list pins).",
"gates": {
"derived": 110,
"run": 110,
"first_pass_nonzero": [
"check-plugin-teardown-shape --self-test (shallow clone) -> fetch pinned sha -> exit 0",
"check:engine-double-contract -> ledger --write -> exit 0",
"check:objectql-double-limit -> doubles honour limit -> exit 0",
"check:where-matcher -> combinator refusal -> exit 0",
"check:test-source-alias -> plugin-auth alias reverted -> exit 0",
"rest test typecheck TS6133 -> unused binding removed -> exit 0",
"pnpm --filter @objectstack/plugin-auth test (sibling) 2 failed: plugin-list pins -> updated -> exit 0"
],
"final_nonzero": 0,
"ran_reconciliation": "110 derived, 110 run, 0 NOT-MEASURED (exit 0); list out/28367-code-gates-ran-6bbf2eeb.list",
"extra": "typecheck rest, runtime, plugin-auth, dogfood :: exit 0",
"lint": "eslint scoped to packages/rest, packages/runtime, packages/plugins/plugin-auth, packages/qa/dogfood/test :: exit 0",
"ci": "queued at report time; not waited on"
},
"line_budget": "2027 changed lines (+1898 / -129, 28 files) vs the 3000 human-merge threshold: under",
"file_surface": "the four doors' packages, their tests, the two ADR sentences, the docs api row, the changeset; no packages/spec, no metadata-protocol/src/protocol.ts",
"deviations": [
"Identity seam is a middleware on the auth base path (auth-plugin.ts) plus a better-auth plugin, not auth-manager-request-routing.ts#handleRequest: the platform's raw identity mounts bypass handleRequest.",
"Outside the listed surface: scripts/engine-double-contract.pinned.json (--write), five scripts/adr-anchors/*.json, packages/rest/src/index.ts (exports), packages/runtime/src/domains/actions.ts (recordId into the binding), plugin-auth auth-actor-attribution.ts and objectql-adapter.ts (performedBy).",
"No independent model review run here: the seat routes the contract review and os-zhuang (Tier H)."
],
"mcp_calls": "0 writes",
"api_writes": "2: relay pr_create (run 41250427233, read-back identical 12197 bytes); this os-dev-report via post-stamped.mjs. git push x2 (not REST).",
"open_questions": [
{
"question": "A target that returns a credential in its response body to an agent: the ADR bans header relay only.",
"options": ["A follow-up names it in the ADR and refuses such routes for agents", "B leave as is"],
"recommendation": "A"
},
{
"question": "An agent forward to the API-key object's data route is not refused by apiKeyManagementCredentialRefusal, which refuses only the api_key credential.",
"options": ["A follow-up refuses agent principals at the key-management door", "B leave to the agent's scope ceiling and the user's permissions"],
"recommendation": "A"
},
{
"question": "The identity view's session is fresh (createdAt = request time), so a fresh-session route accepts it.",
"options": ["A accept, the user's consent and better-auth's own checks stand", "B backdate the view so fresh-session routes refuse agents"],
"recommendation": "seat's contract review to rule"
}
],
"out_of_scope_findings": []
}
domain:server#1 · os-tesla · session_01L9qibyagSyLB5ckJsKghoa
os-dev-report: head moved on #28482
Reported: 2026-10-31T23:05Z
New head 684ebbfa9c65c491842c91a053a22625883803cc (was 6bbf2eeb). One commit, one file: packages/plugins/plugin-auth/src/auth-plugin.ts.
Why: CI red on Lint · docs & code guards, check:wildcard-fallthrough (终结式命名空间通配已修四次、发现四次都靠人手 —— 缺的是枚举,不是又一个 per-plugin 测试 #4116). The agent forward seam was registered on use('${basePath}/*') with an imported handler, so the guard could not resolve it to verify the declared { yields: true }.
Fix: the handler is inlined at the use() call as (c, next) => serveAgentForwardAtIdentityMount(c, next). The seam's behaviour is unchanged, and every branch still hands the request on to next.
Local:pnpm check:wildcard-fallthrough reproduced the red at 6bbf2eeb. At the new head it exits 0 (9 yielding / 0 ratcheted / 6 exempt). The guard is not in the dispatch-gates --commands derivation for these paths, which is why the local pass missed it.
Re-checked at the new head: plugin-auth typecheck exits 0 and eslint on the file exits 0. agent-forward-identity-view.test.ts and the auth-plugin* tests pass (4 files, 151 tests).
No other change. The branch is otherwise held still for the contract review.
An OAuth-connected agent's api action forward to the identity and platform doors is now served as that same agent, through a server-minted, single-use, forward-bound credential. Before this, it was refused by name.
Every other cell of the table is unchanged, api_key × identity included.
The scope-family rule and the metadata-door write refusal hold before any forward.
Patch changesets for @objectstack/rest, @objectstack/runtime and @objectstack/plugin-auth. The card closed via Fixes.
Follow-ups not filed (contract review 6550628773 ④, non-blocking; the server lane went off shift): (1) a forward target that answers a credential in its response body; (2) the key-management door as judged for an agent principal. They are left for the next server seat to file if it judges them worth it.
Activity
objectstack-fleet commentedon Oct 31, 2026
triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U
Path: 真挡得住的权限 | none | P②
Triage: p2 ·
security·domain:server·area:auth· decision · 2026-10-31T07:54Z⛔ Not a claim. Rule level only.
needs-user-decision, and no seat acts on it until aRuled:line appears.Generated by Claude Code
objectstack-fleet commentedon Oct 31, 2026
director#2 · os-tesla · session_01L9qibxdiybU8ehPKBroYE3
Brief-claimed-by: session_01L9qibxdiybU8ehPKBroYE3 · batch #434 · 2026-10-31T16:01Z
Director seat (the maintainer's decisions thread, 2026-10-31). Presented to the maintainer in batch #434 (five items: #27767, #28345, #28313, #28367, and the
any/orkey question on PR #28380) with the latest four-facet letter on this card as the seat's reading; the seat's increment and the maintainer's letter follow in the ruling comment.objectstack-fleet commentedon Oct 31, 2026
director#2 · os-tesla · session_01L9qibxdiybU8ehPKBroYE3
Ruling: batch #434 item 4 · go (as shaped) · maintainer 「同意」 2026-10-31T16:16Z
Director seat (the maintainer's decisions thread). Presented in batch #434 (brief-claimed on this card; detailed letter in the project's decision doc, batch 3). The maintainer answered the batch at 2026-10-31T16:12Z, verbatim: 「同意」 = every item at the seat's recommendation, which for this card is go, as the card and triage 6540849042 shaped it.
What the ruling binds: build the agent forward credential in the shape already agreed under #28178 A — a credential the server mints itself (never the caller's bearer or API key), bound to this one forward (one method, one route, one action run), single-use, short-lived, non-replayable, unreachable out of process, resolving at the target to the same agent principal (scope-derived permissions, onBehalfOf, performedBy attribution); an agent is never handed a human session (option C stays refused). ADR-0090 D10 is revised to name, cell by cell, which rows of the #28178 B refusal table flip from "refuse by name" to "accept with a forward credential"; every other cell keeps refusing by name. A rule for non-MCP targets limits them by scope family so a target that gates only on system permissions cannot exceed the tool families the agent consented to.
Order: ADR revision PR first (Tier H, maintainer approves via os-zhuang), contract review, then code. p2; no urgency change.
Workload:
domain:server· ADR PR + code PR · L ·Clause-②widening at the named cells only · no precedent.Execution: triage writes
Ruled:on the card withRuling-ref:naming this comment; the server lane dispatches the ADR revision first. Label strokes are the dispatching seat's.objectstack-fleet commentedon Oct 31, 2026
domain:server#1 · os-tesla · session_01L9qibz3ZUbZ8Qcn1rDw9yc
Claim: step 1, the ADR-0090 D10 revision · card thread · 2026-10-31T17:05Z
Session:
session_01L9qibz3ZUbZ8Qcn1rDw9yc(the seat); the card thread issession_01L9qibyagSyLB5ckJsKghoaAccount:
os-tesla(the seat's linked user; the card's assignee)Branch:
claude/project-thread-hem8y5(the card thread's harness-pinned branch; it names no issue, so this claim is the identity record)Worktree: the card thread's own container checkout
Domain:
domain:serverSeat:
domain:server#1Ruling-ref:
6545950368(go, as shaped; Tesla 「同意」 2026-10-31T16:12Z, batch #434 item 4). Order: the ADR revision first, then contract review, then code.File surface:
docs/adr/0090-permission-model-v2-concept-convergence.mdonly (the dated D10 amendment). ⛔ Nopackages/**in this step.Container & model:
M,mode:thread,model: opus, efforthigh. Tier H (os-zhuang approves). Contract review is owed.Clause-②: yes (widening, at the named cells of the #28178 B refusal table only)
Metadata-protocol: none
Thread-read: 6545950368
Premise: the #28178 B refusal table and triage shaping 6540849042 are the starting point; the dev reads both before drafting
Serial constraints cleared: no in-flight PR edits ADR-0090
Resume-from: none (fresh from
origin/main706461574)Order:
/mnt/project-files/server-seat-1/r2026-10-31/dispatch-28367-adr.md(project file, not public)State:
pm:queue→pm:dispatched, assigned toos-tesla, recorded by this claim.objectstack-fleet commentedon Oct 31, 2026
domain:server#1 · os-tesla · session_01L9qibyagSyLB5ckJsKghoa
os-dev-report
Reported: 2026-10-31T17:23Z
{ "issue": 28367, "status": "done", "branch": "claude/project-thread-hem8y5", "pr": "https://github.com/objectstack-ai/objectstack/pull/28455", "head": "5e899ebd5b31cd902ba31cfe584dd41b3dfad2a8", "draft": true, "session": "session_01L9qibyagSyLB5ckJsKghoa", "premise_still_valid": true, "summary": "Step 1 of ruling 6545950368: ADR-0090 D10 gains rule 5, the agent forward credential (server-minted after the action door's gates, bound to one method/route/action run, single-use, short-lived, in-process only; resolves at the target to the same agent principal; no human session). The #28178 B table is reproduced with two cells flipped (oauth x identity, oauth x platform); api_key x identity keeps refusing by name because an API key is its owner's credential, not an agent principal (D10 2026-09-16 note). Adds the scope-family rule for non-MCP targets and names the code half's packages, doors and pins. No code.", "tests": "n/a: ADR-only diff, no tested behaviour changed, so no ablation", "gates": { "derived": 21, "run": 21, "first_pass_nonzero": [ "8 families :: exit 3 (no node_modules in the fresh worktree) -> pnpm install --frozen-lockfile -> 7 rerun :: exit 0", "pnpm --filter @objectstack/lint run check:doc-formula-expressions :: exit 3 (formula/lint dist absent) -> turbo build of both under the verify lock -> :: exit 0" ], "final_nonzero": 0, "ran_reconciliation": "21 derived, 21 run, 0 NOT-MEASURED (exit 0)", "extra": "node scripts/check-role-word.mjs :: exit 0", "lint": "n/a: no package touched", "ci": "in_progress at report time; not waited on" }, "dogfood_battery": "n/a: no ! in the PR title and no (narrowing) arm", "line_budget": "192 changed lines (+190 / -2, 1 file) vs the 3000 human-merge threshold: under", "files_changed": ["docs/adr/0090-permission-model-v2-concept-convergence.md"], "file_surface": "exactly the claim's one file", "deviations": [ "No independent model review run here: the seat runs the contract review per the order.", "Clause-② line copied verbatim from claim 6546502251 (it reads as yes, arm widening, through clause2-line)." ], "mcp_calls": "0", "api_writes": "2: POST /repos/objectstack-ai/objectstack/dispatches (relay pr_create, run 41224200331, read-back identical 7636 bytes, label skip-changeset); this os-dev-report comment via post-stamped.mjs. git push x1 (not REST). Card and thread reads were unauthenticated public REST GETs.", "open_questions": [ { "question": "Should api_key x identity also flip? The card title names API-key-connected agents; the ruling binds the credential to an agent principal, which an API key is not (D10 2026-09-16 note).", "options": ["A keep refusing by name, as the amendment says", "B flip it, carrying the key owner's principal"], "recommendation": "A, because B carries the owner's whole reach at the identity mount, which is option C (a human session) under another name" } ], "out_of_scope_findings": [] }objectstack-fleet commentedon Oct 31, 2026
domain:server#1 · os-tesla · session_01L9qibz3ZUbZ8Qcn1rDw9yc
Landing record · #28367 step 1 · PR #28455
domain:serverseat 1 ·session_01L9qibz3ZUbZ8Qcn1rDw9yc· 2026-10-31T20:36ZGET pulls/28455a99de440(enqueued by os-zhuang, Tier H)git merge-base --is-ancestor a99de440 origin/maingit grep -c "Amendment (2026-10-31, #28367)"in ADR-0090 onorigin/mainoauth × identityandoauth × platformunder the scope-family rule. This is step 1 of the go ruling (6545950368): the ADR first.api_key × identityalso flips is batch Release v0.7.1 #436 item 1 (director6546726515). The seat and the lane both recommend that it keeps refusing. A flip would be a follow-up ADR edit.The claim
6546502251stays open for step 2. The card stayspm:dispatched.objectstack-fleet commentedon Oct 31, 2026
domain:server#1 · os-tesla · session_01L9qibz3ZUbZ8Qcn1rDw9yc
Claim amendment to
6546502251· 2026-10-31T20:41Z · step 2 (code)Step 1 landed as
a99de440(landing record 6548823976). This claim now covers step 2 on the same thread and branch, restarted fromorigin/main.File surface: the doors that ADR-0090 D10 rule 5 names (
packages/runtimeaction doors andsecurity/resolve-execution-context.ts,packages/restcontext resolution,packages/plugins/plugin-authidentity mount), their tests, the two sentences in ADR-0090 the amendment names, and a.changeset/28367-*.md. ⛔ Nopackages/spec, nometadata-protocol/src/protocol.ts.Tier: H if the PR carries the two ADR sentences (
docs/adr/**). The seat requests the reviewer.Clause-②: yes (widening, at the two named cells only)
Metadata-protocol: none
Order:
/mnt/project-files/server-seat-1/r2026-10-31/dispatch-28367-code.md(project file, not public)Everything else stands as claimed.
wip(#28367): forward-credential pins across the doors and the flipped…
wip(#28367): ADR-0090 sentences, anchors, docs table line, changeset
wip(#28367): plugin-auth test alias for @objectstack/rest; revocation…
feat(runtime, rest, plugin-auth): an OAuth agent's api-action forward…
test(#28367): test doubles honour the ledgers (limit, combinators, en…
objectstack-fleet commentedon Oct 31, 2026
domain:server#1 · os-tesla · session_01L9qibyagSyLB5ckJsKghoa
os-dev-report
Reported: 2026-10-31T22:58Z
{ "issue": 28367, "status": "done", "branch": "claude/project-thread-hem8y5", "pr": "https://github.com/objectstack-ai/objectstack/pull/28482", "head": "6bbf2eeb5d7398a56d31ee3828457d2439c602a1", "draft": true, "session": "session_01L9qibyagSyLB5ckJsKghoa", "premise_still_valid": true, "summary": "Step 2 of ruling 6545950368, the code half of ADR-0090 D10 rule 5 (#28455). An OAuth agent's api-action forward now carries a server-minted forward credential instead of being refused: bound to method, route and action run, spent on first presentation, revoked when the forward answers, 30 s bound, redeemable only inside the forward's own in-process async chain. The runtime HTTP dispatcher and the REST face resolve it to the same agent principal through the shared assembler; the identity mount shows better-auth a request-scoped in-memory view of the delegating user (no session row, token or cookie) and strips credential response headers. Exactly two cells flip (oauth x identity, oauth x platform, the latter under the scope-family rule). api_key x identity keeps refusing by name per ruling 6548923676. The two named ADR sentences and the docs table row are corrected.", "live_measure": "main 52306d40, before the code: both flipped cells refused by name, api_key x identity refused, api_key x platform served, outside OAuth token 401 at the data door and the identity mount (2 dogfood files, 16/16). Kept at rule level in out/28367-mechanism.md.", "tests": "unit: forward-credential (9), rest-forward-credential-agent (5), resolve-execution-context.forward-credential (5), agent-forward-identity-view (5), api-action-forward #28367 describe (file 58); dogfood: mcp-oauth-api-action-forward (10), mcp-system-object-action-exposure (12). Ablations: 18 unit + 4 dogfood refusal/property pins each red with the forbidden behaviour put back, restored byte-identical. Sibling suites: @objectstack/rest 6884 passed, @objectstack/mcp 603 passed, @objectstack/runtime 8055 passed, @objectstack/plugin-auth 3938 passed (after updating the two plugin-list pins).", "gates": { "derived": 110, "run": 110, "first_pass_nonzero": [ "check-plugin-teardown-shape --self-test (shallow clone) -> fetch pinned sha -> exit 0", "check:engine-double-contract -> ledger --write -> exit 0", "check:objectql-double-limit -> doubles honour limit -> exit 0", "check:where-matcher -> combinator refusal -> exit 0", "check:test-source-alias -> plugin-auth alias reverted -> exit 0", "rest test typecheck TS6133 -> unused binding removed -> exit 0", "pnpm --filter @objectstack/plugin-auth test (sibling) 2 failed: plugin-list pins -> updated -> exit 0" ], "final_nonzero": 0, "ran_reconciliation": "110 derived, 110 run, 0 NOT-MEASURED (exit 0); list out/28367-code-gates-ran-6bbf2eeb.list", "extra": "typecheck rest, runtime, plugin-auth, dogfood :: exit 0", "lint": "eslint scoped to packages/rest, packages/runtime, packages/plugins/plugin-auth, packages/qa/dogfood/test :: exit 0", "ci": "queued at report time; not waited on" }, "line_budget": "2027 changed lines (+1898 / -129, 28 files) vs the 3000 human-merge threshold: under", "file_surface": "the four doors' packages, their tests, the two ADR sentences, the docs api row, the changeset; no packages/spec, no metadata-protocol/src/protocol.ts", "deviations": [ "Identity seam is a middleware on the auth base path (auth-plugin.ts) plus a better-auth plugin, not auth-manager-request-routing.ts#handleRequest: the platform's raw identity mounts bypass handleRequest.", "Outside the listed surface: scripts/engine-double-contract.pinned.json (--write), five scripts/adr-anchors/*.json, packages/rest/src/index.ts (exports), packages/runtime/src/domains/actions.ts (recordId into the binding), plugin-auth auth-actor-attribution.ts and objectql-adapter.ts (performedBy).", "No independent model review run here: the seat routes the contract review and os-zhuang (Tier H)." ], "mcp_calls": "0 writes", "api_writes": "2: relay pr_create (run 41250427233, read-back identical 12197 bytes); this os-dev-report via post-stamped.mjs. git push x2 (not REST).", "open_questions": [ { "question": "A target that returns a credential in its response body to an agent: the ADR bans header relay only.", "options": ["A follow-up names it in the ADR and refuses such routes for agents", "B leave as is"], "recommendation": "A" }, { "question": "An agent forward to the API-key object's data route is not refused by apiKeyManagementCredentialRefusal, which refuses only the api_key credential.", "options": ["A follow-up refuses agent principals at the key-management door", "B leave to the agent's scope ceiling and the user's permissions"], "recommendation": "A" }, { "question": "The identity view's session is fresh (createdAt = request time), so a fresh-session route accepts it.", "options": ["A accept, the user's consent and better-auth's own checks stand", "B backdate the view so fresh-session routes refuse agents"], "recommendation": "seat's contract review to rule" } ], "out_of_scope_findings": [] }objectstack-fleet commentedon Oct 31, 2026
domain:server#1 · os-tesla · session_01L9qibyagSyLB5ckJsKghoa
os-dev-report: head moved on #28482
Reported: 2026-10-31T23:05Z
New head
684ebbfa9c65c491842c91a053a22625883803cc(was6bbf2eeb). One commit, one file:packages/plugins/plugin-auth/src/auth-plugin.ts.check:wildcard-fallthrough(终结式命名空间通配已修四次、发现四次都靠人手 —— 缺的是枚举,不是又一个 per-plugin 测试 #4116). The agent forward seam was registered onuse('${basePath}/*')with an imported handler, so the guard could not resolve it to verify the declared{ yields: true }.use()call as(c, next) => serveAgentForwardAtIdentityMount(c, next). The seam's behaviour is unchanged, and every branch still hands the request on tonext.pnpm check:wildcard-fallthroughreproduced the red at6bbf2eeb. At the new head it exits 0 (9 yielding / 0 ratcheted / 6 exempt). The guard is not in thedispatch-gates --commandsderivation for these paths, which is why the local pass missed it.agent-forward-identity-view.test.tsand theauth-plugin*tests pass (4 files, 151 tests).objectstack-fleet commentedon Nov 1, 2026
domain:server#1 · os-tesla · session_01L9qibz3ZUbZ8Qcn1rDw9yc
Landing record · #28367 · PR #28482 (step 2 of 2)
domain:serverseat 1 ·session_01L9qibz3ZUbZ8Qcn1rDw9yc· 2026-11-01T03:49ZGET pulls/28482d3fdd46e, enqueued by os-zhuang (Tier H)git merge-base --is-ancestor d3fdd46e origin/maingit grep -c withForwardCredentialinrest/src/forward-credential.tsonorigin/maina99de440), under go ruling6545950368andapi_key×identityruling A6548923676.apiaction forward to the identity and platform doors is now served as that same agent, through a server-minted, single-use, forward-bound credential. Before this, it was refused by name.api_key×identityincluded.@objectstack/rest,@objectstack/runtimeand@objectstack/plugin-auth. The card closed viaFixes.6550628773④, non-blocking; the server lane went off shift): (1) a forward target that answers a credential in its response body; (2) the key-management door as judged for an agent principal. They are left for the next server seat to file if it judges them worth it.Release: claim
6546502251(amendment6548876208;session_01L9qibz3ZUbZ8Qcn1rDw9yc, branchclaude/project-thread-hem8y5) · both steps landed, card closed.pm:dispatchedremoved, assignee cleared.