Skip to content

feat(spec): 为 action body 的 ctx.session 立契约 schema(按现状声明,#5697) - #5721

Merged
os-zhuang merged 1 commit into
mainfrom
claude/issue-5697-action-ctx-session-schema
Aug 6, 2026
Merged

feat(spec): 为 action body 的 ctx.session 立契约 schema(按现状声明,#5697)#5721
os-zhuang merged 1 commit into
mainfrom
claude/issue-5697-action-ctx-session-schema

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Fixes #5697

#5613 契约先行裁定的第一阶段:给 action body 的 ctx.session 立契约 schema,按现状声明。零行为变更是硬边界——运行时产出的对象一个字节都没变。

前提核实(对 origin/main @ 6ddb2ec4a)

派单断言 结果
buildActionSession()packages/runtime/src/action-execution.ts ✅ 已重定位到 754 行(派单说的 689-695 已过时,正如正文预告)
产出 { userId?, organizationId?, roles? },三键条件展开 ✅ 逐字成立
调用点两处 domains/actions.ts:333action-execution.ts:985;http-dispatcher.ts 里的同名注释是 D11③ 抽取后的残留文档,无第三处生产者
两处 actionContext 都是裸 any const actionContext: any = { ×2
ScriptContext.session?: unknownsandbox/script-runner.ts ✅(行号漂到 71 附近,内容一致)
「已有 schema?」 ⚠️ 见下——正文的「完全无 schema」需要一处精确化

一处需要精确化的前提(不推翻裁定,反而加强它)

#5613 正文第 3 条说 action ctx「没有任何 schema 声明」。Zod schema 确实一个都没有,但 spec 侧存在一个纯 TypeScript 接口的声明,而且形状完全吻合:

packages/spec/src/ui/action-params.zod.ts:167(改动前)

/** Caller session (active org / roles), mirroring the hook `ctx.session`. */
session?: { userId?: string; organizationId?: string; roles?: string[]; [k: string]: unknown };

它是 ActionHandlerContext 的一个字段。三仓(objectstack / objectui / cloud)grep 确认 ActionHandlerContext 模块外零导入者

这处发现改变的不是结论而是落点:action ctx 的运行时声明已经有家了,只是那个家里只有类型没有 parse——而且带 [k: string]: unknown 兜底,正是「declared ≠ enforced」的教科书形态。所以裁定「裸 any 面是漂移根因」成立且被这处证据加强:一个连兜底通配都写进去的类型面,和没有面在闸门眼里是一回事。

schema 落点判定(premise-first,不是按 PM 提示)

派单提示「kernel/contracts 面」,实测后落在 packages/spec/src/ui/action-params.zod.ts,三条证据:

  1. 同一对象的兄弟声明已经全在这里:ActionHandlerContextActionEngineFacadeActionHandler。把 Zod schema 放到别处,等于给同一个对象立第二个家——正是本单要终结的漂移形态。
  2. 先例是「挨着它所属的元数据类型」,不是 kernel:HookContextSchema 住在 data/hook.zod.ts(挨着 HookSchema),不在 kernel。action 的对应位置就是 ui/ 的 action 族。
  3. 消费端已经从这里进货:action-execution.ts 第 18 行本来就 import { validateActionParams } from '@objectstack/spec/ui'——同一个模块。新类型跟着同一条 import 进来,没有新增依赖面。

kernel/execution-context.zod.ts生产者的信封(平台内部受众),把面向 action 作者的契约放那儿会把它和 ActionHandlerContext 拆开两处。

改了什么

spec(新增契约)

  • ActionSessionSchema / ActionSession,按现状声明 { userId?: string, organizationId?: string, roles?: string[] }
  • 故意 NOT strict,与 HookContextSchema 同姿态同理由:这是平台交给 body 的运行时形状、无人书写;关掉门会把将来引擎侧的一次补字段变成「拿到上下文的人 parse 失败」。
  • roles.describe() 按派单要求标注 deprecated:值实为 ExecutionContext.positions(ADR-0090 D3 词汇),声明现状不等于背书拼法;更名 + 弃用窗口 + ADR-0087 语义迁移属 [runtime] action body 的 ctx.session 仍在生产 roles(值是 ec.positions)—— 自称「mirroring hook ctx.session」,而 hook 侧该键已按 ADR-0049 退役 #5613 第二阶段;现在不引入 positions——在迁移前铸第二个拼法就是缺陷本身。同时写清:不要拿它判权限,判权限问 security service(ADR-0095)。
  • ActionHandlerContext.session 改指 ActionSession,顺带去掉 [k: string]: unknown 兜底(三仓零导入者,narrow 不影响任何调用点;schema 仍非 strict,运行时多余键照样 parse,只是类型不再邀请作者去发明键)。
  • 模块头 JSDoc 补成「入站校验 + 出站上下文」两半,否则生成的参考页开头会自称只做 param 校验。
  • build-docs.ts:把 action-params 放进 Apps & Navigation 分组,挨着 action,而不是掉进 More

runtime(仅类型标注 + pin,零行为变更)

  • buildActionSession() 返回类型 any | undefinedActionSession | undefined
  • ScriptContext.session 维持 unknown,只补文档:这一个字段上同时走 hook 和 action 两种不同形状,收窄它会逼所有消费端去判别一个该类型并不携带的 body kind——那笔账不属于「声明现状」这一单,已在注释里点名留给后续。
  • buildActionSession() 的文档注释里那两句错话(「mirroring the hook ctx.session shape」、roles)故意留着不改,按裁定属第二阶段;但加了 ⚠️ 标注说明它们已知为错,免得在第二阶段落地前继续误导读者。

pin 与先证红(方向:预测为红,实测为红)

pin 落在 packages/runtime/src/action-session-shape-contract.test.ts——参照 #5668hook-input-shape-contract.test.ts:必须执行真 producer,而 spec 不能反向依赖 runtime。

非 strict 的 schema 上,safeParse().success 单独是废的(多余键会被吞掉还判绿)。所以承重断言是 parse(built) 深等于 built:非 strict parse 会 strip 未声明键,深等式因此正好等价于「producer 产出的每一个键都被契约声明了」。

先按错误形状写并运行,预测两处红:

FAIL  > builds exactly the declared keys, and the declaration covers all of them
AssertionError: expected [ 'organizationId', 'roles', 'userId' ] to deeply equal [ 'organizationId', 'positions', …(1) ]
-   "positions"
+   "roles"

FAIL  > omits `organizationId` entirely when the context carries no tenant
AssertionError: expected false to be true

 Test Files  1 failed | 98 passed (99)
      Tests  2 failed | 1444 passed (1446)

恰好两处,且同批其余断言全绿(它们是正控:pin 确实在跑,不是整文件炸掉)。翻正后全绿。

条件展开语义是实测的,不是假设

派单要求「measure, don't assume」。实测结论:

验证

命令 结果
--filter @objectstack/runtime test 1446 passed (99 files)
--filter @objectstack/spec test 8082 passed (317 files)
--filter @objectstack/spec typecheck 绿
--filter @objectstack/runtime typecheck 绿
--filter @objectstack/spec check:generated All 10 generated artifacts are up to date
check:exported-any / check:dual-source-exports / check:liveness 绿
node scripts/check-nul-bytes.mjs OK(另按 AGENTS.md 对本次改动文件做了控制字节自扫,干净)

全部在 flock /tmp/os-heavy-verify.lock 下串行,--max-old-space-size=4096

生成物审阅(逐个,不是「跑一下就提交」)

  • packages/spec/api-surface.json:+2 行(ActionSession (type) / ActionSessionSchema (const))。
  • packages/spec/authorable-surface.json:+3 键(ui/ActionSession:userId|organizationId|roles)。
  • packages/spec/json-schema.manifest.json:+ui/ActionSession
  • content/docs/references/ui/action-params.mdx:新页——这个面第一次有生成参考页,roles 的整段弃用处方直接渲染在表里。index.mdx / meta.json 各 +1 行。
  • strictness ledger:新 z.object 站点会被 check:strictness-ledger 双向要求登记,所以手写了两行(file-level triage 的 wire 判词 + remaining-strip 的 out of scope 行),counts.mdgen:strictness-ledger 重算:ui/ 170→171 站点、49→50 strip、wire/open 桶 +1。数字全部生成,判词手写。
  • ⚠️ 一处不相关漂移已排除:packages/spec/authorable-surface.base.json 会被 gen:schema 重锚到当前 merge base,顺带带进别人已合并改动的 api/Discovery:scoping 等 2 键。已 git checkout 还原、不进本 PR;check:authorable-surface 在还原后仍绿,并自己打印 “trails the merge base by 2 key(s) — expected right after a surface change lands”。

没做的事

changeset

.changeset/action-ctx-session-contract.md:@objectstack/spec minor(新增导出 schema + type),@objectstack/runtime patch(纯类型标注——但 buildActionSession() 的返回类型从 any 收窄为具体类型,对下游是可见的类型面变化,所以不按「无 changeset」处理)。


Generated by Claude Code

Phase 1 of #5613's contract-first ruling: declare what
`buildActionSession()` builds today, change nothing about what it builds.

- `ActionSessionSchema` / `ActionSession` in `@objectstack/spec/ui`
  (`ui/action-params.zod.ts`, beside the action runtime-context
  declarations it already carries): `{ userId?, organizationId?, roles? }`,
  non-strict, matching `HookContextSchema`'s wire posture.
- `roles` declared as built and marked DEPRECATED in `.describe()` + JSDoc:
  the value is `ExecutionContext.positions` under the spelling ADR-0090 D3
  forbids; the rename is #5613 phase 2 and no `positions` key is minted now.
- Type-only on the runtime side: `buildActionSession()` returns
  `ActionSession | undefined`, `ActionHandlerContext.session` is the
  inferred type. `ScriptContext.session` stays `unknown` (one seam, two
  body kinds) with the two shapes documented.
- Consistency pin executing the real producer:
  `packages/runtime/src/action-session-shape-contract.test.ts`.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018fxLGQdatPbBUvCgiVxg6D
@vercel

vercel Bot commented Aug 6, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectstack Ignored Ignored Aug 6, 2026 2:06am

Request Review

@github-actions github-actions Bot added the size/m label Aug 6, 2026
@github-actions

github-actions Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 2 package(s): @objectstack/runtime, @objectstack/spec.

115 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:

  • content/docs/ai/agents.mdx (via @objectstack/spec)
  • content/docs/ai/skills-reference.mdx (via @objectstack/spec)
  • content/docs/ai/skills.mdx (via @objectstack/spec)
  • content/docs/api/client-sdk.mdx (via packages/runtime, @objectstack/spec)
  • content/docs/api/environment-routing.mdx (via @objectstack/spec)
  • content/docs/api/error-catalog.mdx (via @objectstack/spec)
  • content/docs/api/error-handling-client.mdx (via @objectstack/spec)
  • content/docs/api/error-handling-server.mdx (via @objectstack/spec)
  • content/docs/api/index.mdx (via @objectstack/runtime, @objectstack/spec)
  • content/docs/api/wire-format.mdx (via @objectstack/runtime)
  • content/docs/automation/approvals.mdx (via @objectstack/spec)
  • content/docs/automation/connectors.mdx (via @objectstack/spec)
  • content/docs/automation/flows.mdx (via @objectstack/spec)
  • content/docs/automation/hook-bodies.mdx (via @objectstack/runtime, packages/spec)
  • content/docs/automation/hooks.mdx (via @objectstack/spec)
  • content/docs/automation/index.mdx (via @objectstack/spec)
  • content/docs/automation/webhooks.mdx (via @objectstack/spec)
  • content/docs/automation/workflows.mdx (via @objectstack/spec)
  • content/docs/concepts/architecture.mdx (via @objectstack/spec)
  • content/docs/concepts/design-principles.mdx (via packages/spec)
  • content/docs/concepts/index.mdx (via @objectstack/spec)
  • content/docs/concepts/metadata-driven.mdx (via @objectstack/spec)
  • content/docs/concepts/metadata-lifecycle.mdx (via @objectstack/runtime, packages/spec)
  • content/docs/concepts/north-star.mdx (via packages/runtime, @objectstack/spec)
  • content/docs/data-modeling/analytics.mdx (via @objectstack/spec)
  • content/docs/data-modeling/drivers.mdx (via @objectstack/runtime, @objectstack/spec)
  • content/docs/data-modeling/external-datasources.mdx (via @objectstack/spec)
  • content/docs/data-modeling/field-types.mdx (via @objectstack/spec)
  • content/docs/data-modeling/fields.mdx (via @objectstack/spec)
  • content/docs/data-modeling/formulas.mdx (via @objectstack/spec)
  • content/docs/data-modeling/index.mdx (via @objectstack/spec)
  • content/docs/data-modeling/objects.mdx (via @objectstack/spec)
  • content/docs/data-modeling/queries.mdx (via @objectstack/spec)
  • content/docs/data-modeling/schema-design.mdx (via @objectstack/spec)
  • content/docs/data-modeling/seed-data.mdx (via @objectstack/spec)
  • content/docs/data-modeling/validation-rules.mdx (via @objectstack/spec)
  • content/docs/data-modeling/validation.mdx (via @objectstack/spec)
  • content/docs/deployment/cli.mdx (via @objectstack/spec)
  • content/docs/deployment/index.mdx (via @objectstack/runtime)
  • content/docs/deployment/production-readiness.mdx (via @objectstack/runtime)
  • content/docs/deployment/single-project-mode.mdx (via @objectstack/runtime)
  • content/docs/deployment/tenancy-modes.mdx (via @objectstack/spec)
  • content/docs/deployment/troubleshooting.mdx (via @objectstack/spec)
  • content/docs/deployment/validating-metadata.mdx (via @objectstack/spec)
  • content/docs/deployment/vercel.mdx (via @objectstack/runtime)
  • content/docs/getting-started/build-with-claude-code.mdx (via @objectstack/spec)
  • content/docs/getting-started/common-patterns.mdx (via @objectstack/spec)
  • content/docs/getting-started/examples.mdx (via @objectstack/spec)
  • content/docs/getting-started/quick-reference.mdx (via @objectstack/spec)
  • content/docs/getting-started/quick-start.mdx (via @objectstack/spec)
  • content/docs/getting-started/your-first-project.mdx (via @objectstack/runtime, @objectstack/spec)
  • content/docs/kernel/cluster.mdx (via @objectstack/runtime, @objectstack/spec)
  • content/docs/kernel/contracts/auth-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/cache-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/data-engine.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/index.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/metadata-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/storage-service.mdx (via packages/spec)
  • content/docs/kernel/index.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/email-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/index.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/queue-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/sharing-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/sms-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/storage-service.mdx (via packages/spec)
  • content/docs/kernel/services-checklist.mdx (via @objectstack/spec)
  • content/docs/kernel/services.mdx (via @objectstack/spec)
  • content/docs/permissions/authentication.mdx (via @objectstack/runtime)
  • content/docs/permissions/authorization.mdx (via packages/runtime, @objectstack/spec)
  • content/docs/permissions/permission-sets.mdx (via @objectstack/spec)
  • content/docs/permissions/permissions-matrix.mdx (via @objectstack/spec)
  • content/docs/permissions/positions.mdx (via @objectstack/spec)
  • content/docs/permissions/rls.mdx (via @objectstack/spec)
  • content/docs/permissions/sharing-rules.mdx (via @objectstack/spec)
  • content/docs/plugins/adding-a-metadata-type.mdx (via @objectstack/spec)
  • content/docs/plugins/development.mdx (via @objectstack/spec)
  • content/docs/plugins/index.mdx (via @objectstack/spec)
  • content/docs/plugins/packages.mdx (via @objectstack/runtime, @objectstack/spec)
  • content/docs/protocol/backward-compatibility.mdx (via @objectstack/spec)
  • content/docs/protocol/diagram.mdx (via packages/spec)
  • content/docs/protocol/kernel/config-resolution.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/http-protocol.mdx (via @objectstack/runtime, @objectstack/spec)
  • content/docs/protocol/kernel/i18n-standard.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/index.mdx (via @objectstack/runtime, @objectstack/spec)
  • content/docs/protocol/kernel/lifecycle.mdx (via @objectstack/runtime, @objectstack/spec)
  • content/docs/protocol/kernel/plugin-spec.mdx (via @objectstack/spec)
  • content/docs/protocol/knowledge.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/index.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/query-syntax.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/schema.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/security.mdx (via packages/spec)
  • content/docs/protocol/objectql/state-machine.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/actions.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/concept.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/index.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/layout-dsl.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/record-alert.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/widget-contract.mdx (via @objectstack/spec)
  • content/docs/releases/implementation-status.mdx (via @objectstack/runtime, @objectstack/spec)
  • content/docs/releases/index.mdx (via @objectstack/spec)
  • content/docs/releases/v12.mdx (via @objectstack/spec)
  • content/docs/releases/v13.mdx (via @objectstack/spec)
  • content/docs/releases/v16.mdx (via @objectstack/spec)
  • content/docs/releases/v17.mdx (via @objectstack/runtime, @objectstack/spec)
  • content/docs/releases/v9.mdx (via @objectstack/spec)
  • content/docs/ui/actions.mdx (via @objectstack/spec)
  • content/docs/ui/apps.mdx (via @objectstack/spec)
  • content/docs/ui/create-vs-edit-form.mdx (via @objectstack/spec)
  • content/docs/ui/dashboards.mdx (via @objectstack/spec)
  • content/docs/ui/forms.mdx (via @objectstack/spec)
  • content/docs/ui/index.mdx (via @objectstack/spec)
  • content/docs/ui/public-data-collection.mdx (via @objectstack/spec)
  • content/docs/ui/setup-app.mdx (via @objectstack/spec)
  • content/docs/ui/translations.mdx (via @objectstack/spec)
  • content/docs/ui/views.mdx (via @objectstack/spec)

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/main → pass the list as args.docs.

@github-actions github-actions Bot added documentation Improvements or additions to documentation tests protocol:ui tooling labels Aug 6, 2026
@os-zhuang
os-zhuang marked this pull request as ready for review August 6, 2026 02:29
@os-zhuang
os-zhuang added this pull request to the merge queue Aug 6, 2026
Merged via the queue into main with commit 9fe9c1d Aug 6, 2026
25 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-5697-action-ctx-session-schema branch August 6, 2026 02:41
os-zhuang pushed a commit that referenced this pull request Aug 6, 2026
…5679)

CI 上两条不同签名的红,都是合并 origin/main 之后才出现的:

1. `@objectstack/client` DTS build — TS2741: `getRoute()` 的约定回退表
   `Record<ApiRouteType, string>` 按设计对 `keyof ApiRoutes` **全键必填**,
   所以给 ApiRoutesSchema 新增一个已声明键就必须给出它的约定路径。这是我
   消费半径漏掉的包。补 `mcp: '/api/v1/mcp'` —— 不是猜的,正是两个 discovery
   生产者实测发出的值,所以回退值与被发现值一致而非互相打架;而且该表本就
   是 unscoped 约定表(每行都是 /api/v1/...),恰好符合 /mcp 裸挂载的事实。
   全仓复扫 `keyof ApiRoutes` / ApiRoutes 型字面量,确认没有第三处:其余站点
   都是 `Partial<ApiRoutes>` 或只填两个必填键,不受影响。

2. `check:authorable-surface` — 合并带进了 #5721 的 `ui/ActionSession` 源,
   但 authorable-surface.json 未在合并树上重生成。跑 gen:schema 重出,该文件
   现同时含本单的 `api/ApiRoutes:mcp` 与 main 的 ActionSession 三键。

`authorable-surface.base.json` 的 baseRev 重锚照 #5358 再次剔除 —— 那是 main
前移导致的机械重锚,不属本单。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018fxLGQdatPbBUvCgiVxg6D
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Aug 6, 2026
…bjectstack-ai#5701) (objectstack-ai#5752)

* feat(spec): declare $icontains (ASCII fold), pin the $contains family case-sensitive, retire $regex (objectstack-ai#5701)

The contract half of objectstack-ai#4706's maintainer ruling (B). Declaration only: no live
rejection, no driver code touched, every backend answers exactly what it
answered before.

- `$icontains` on StringOperatorSchema / FieldOperatorsSchema / Filter: contains,
  ignoring ASCII case (A-Z against a-z) and nothing else. The boundary is stated
  for authors — `café` does NOT match `CAFÉ` — because ASCII is the one fold all
  five backends can deliver: SQLite without ICU (so turso and sqlite-wasm too)
  folds ASCII only, and promising Unicode would repeat the defect this retires.

- `$contains` / `$notContains` / `$startsWith` / `$endsWith` are declared
  CASE-SENSITIVE, superseding the recorded "Case sensitivity should be handled at
  backend level" (Prime Directive objectstack-ai#13 — the old sentence is quoted in place so
  the reversal is findable from the sentence a reader remembers). What that
  non-guarantee bought, measured: three different answers across five backends,
  two of them inside driver-memory alone (query path folds full Unicode, the
  reference matcher is case-sensitive).

- `RETIRED_FILTER_OPERATORS`: pure data, no behaviour. `$regex` and `$options`
  with prescriptions naming `$icontains`. Nothing rejects them here — the five
  existing refusal sites are wired to it by objectstack-ai#5702, and objectstack-ai#5710 must flip the one
  live producer (plugin-auth's ObjectQL adapter, on the authentication path)
  first or sign-in breaks.

- `FILTER_TEXT_CASES` (`data/filter-text-conformance.ts`): the shared standard
  for case folding, literal comparands and the `$regex` refusal, with the
  `expectRejection` discriminant `filter-logic-conformance.ts` deliberately never
  grew. A sibling table rather than rows in that one, whose charter excludes both
  axes and warns against red rows for work nobody is dispatched to do. Registered
  in check:driver-conformance with one MEASURED DEBT row per driver pointing at
  objectstack-ai#5702 — 25 covered / 5 DEBT / 0 exempt, main stays green.

`$icontains` is deliberately NOT in `FILTER_OPERATORS` yet. That array is a
runtime allowlist, not a word list: driver-memory's shape gate derives its
ACCEPTED set from it while its matcher's `default:` arm breaks. Measured by
adding it early and rebuilding — the gate stopped refusing and
`match({name:'zzz'}, {name:{$icontains:'acme'}})` returned true, i.e. the
predicate was silently dropped and every row matched. A dropped predicate widens
rather than narrows, which on an RLS read scope is a permission bypass (objectstack-ai#3948).
It joins the array in the PR that implements it; `filter-operator-vocabulary.test.ts`
pins the difference at exactly {$icontains} so neither adding a second staged
operator nor clearing this one can happen silently.

Claude-Session: https://claude.ai/code/session_018fxLGQdatPbBUvCgiVxg6D

Co-authored-by: Claude <noreply@anthropic.com>

* chore(spec): regenerate api-surface + authorable-surface after merging main

The merge driver (`merge=os-regen`, AGENTS.md §11) deferred both artifacts
rather than text-merging them, and `pre-commit` collects that debt. Regenerated
from the MERGED source, so the union includes objectstack-ai#5721's ActionSession entries
alongside this branch's $icontains / FILTER_TEXT additions.

`authorable-surface.base.json` is deliberately NOT bumped: it is the deletion
gate's anchor, main only moves it when a key is REMOVED, and this branch only
adds. `check:authorable-surface` confirms it — 'trails the merge base by 5
key(s) — expected right after a surface change lands'.

Claude-Session: https://claude.ai/code/session_018fxLGQdatPbBUvCgiVxg6D

Co-authored-by: Claude <noreply@anthropic.com>

---------

Co-authored-by: Claude <noreply@anthropic.com>
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Aug 6, 2026
…ck-ai#5679) (objectstack-ai#5743)

* feat(spec)!: declare routes.mcp on ApiRoutesSchema, extend discovery gate to routes keys (objectstack-ai#5679)

`/discovery` advertises `routes.mcp`, objectui reads it, and `ApiRoutesSchema`
never declared it — objectstack-ai#4828's defect one level down, with the opposite
disposition: `endpoints` was retired for having no reader, `mcp` has two real
ones and is in fact the only `routes.*` key anything in objectui reads.

`ApiRoutesSchema` is a plain z.object, which strips unknown keys, so any
consumer parsing /discovery through the spec dropped `routes.mcp` silently.

- spec: declare `mcp: z.string().optional()` as MEASURED off both producers —
  a path string, always the unscoped base (/mcp is mounted bare), optional
  rather than nullable (the key is absent, never null, when MCP is off or
  unserveable).
- rest: drop the two `as any` casts at the emit site. Type-only; the emitted
  body is byte-identical. With the key undeclared, removing them produced two
  TS2339; with it declared, tsc returns to its ratcheted baseline of 2.
- gates: the objectstack-ai#4828 conformance tests now check `routes` keys as well as
  top-level ones in all three producer packages, deriving the allowance from
  ApiRoutesSchema. Extended one level, not recursed.

Corrects the issue's premise on one point: the runtime dispatcher's
getDiscoveryInfo() also emits `routes.mcp` (its routes literal always carries
the key, holding the path or undefined), so both producers were affected and
the new gate went red on both before the fix.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018fxLGQdatPbBUvCgiVxg6D

* fix(client,spec): 补 client 路由约定表的 mcp 项,并在合并树上重生成 authorable-surface (objectstack-ai#5679)

CI 上两条不同签名的红,都是合并 origin/main 之后才出现的:

1. `@objectstack/client` DTS build — TS2741: `getRoute()` 的约定回退表
   `Record<ApiRouteType, string>` 按设计对 `keyof ApiRoutes` **全键必填**,
   所以给 ApiRoutesSchema 新增一个已声明键就必须给出它的约定路径。这是我
   消费半径漏掉的包。补 `mcp: '/api/v1/mcp'` —— 不是猜的,正是两个 discovery
   生产者实测发出的值,所以回退值与被发现值一致而非互相打架;而且该表本就
   是 unscoped 约定表(每行都是 /api/v1/...),恰好符合 /mcp 裸挂载的事实。
   全仓复扫 `keyof ApiRoutes` / ApiRoutes 型字面量,确认没有第三处:其余站点
   都是 `Partial<ApiRoutes>` 或只填两个必填键,不受影响。

2. `check:authorable-surface` — 合并带进了 objectstack-ai#5721 的 `ui/ActionSession` 源,
   但 authorable-surface.json 未在合并树上重生成。跑 gen:schema 重出,该文件
   现同时含本单的 `api/ApiRoutes:mcp` 与 main 的 ActionSession 三键。

`authorable-surface.base.json` 的 baseRev 重锚照 objectstack-ai#5358 再次剔除 —— 那是 main
前移导致的机械重锚,不属本单。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018fxLGQdatPbBUvCgiVxg6D

---------

Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation protocol:ui size/m tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[spec] 为 action body 的 ctx.session 立契约 schema(按现状声明)—— #5613 契约先行裁定的第一阶段

2 participants