Skip to content

feat(spec)!: 退役五个「lint 永远无法告警」的键,并接通 doc.tags (#4509) - #4664

Merged
os-zhuang merged 2 commits into
mainfrom
claude/auth-gate-disconnect-issues-6wz8ew
Aug 2, 2026
Merged

feat(spec)!: 退役五个「lint 永远无法告警」的键,并接通 doc.tags (#4509)#4664
os-zhuang merged 2 commits into
mainfrom
claude/auth-gate-disconnect-issues-6wz8ew

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

收尾 #4509 正文的「顺带的三个小清理候选」—— #4558 落地四条主线之后剩下的部分。

为什么这批必须赶在 v17

把这五个键归到一起的不是它们所在的类型,而是为什么它们必须走 major 而不能先弃用一轮:五个里有四个带 schema 默认值,默认值在 parse 时物化,所以活性 lint 分不清「作者写的值」和「schema 填的值」。给它们打 authorWarn 会对现存的每一个 mapping、每一个 selector 都告警 —— 这就是账本里记的是 _authorWarnSkipped 而不是 authorWarn 的原因。

对处于这种状态的键,移除不是告警之后的升级手段,而是唯一能触达作者的通道。spec 现在 17.0.0-rc.1、pre 模式仍开着,这个通道在 changeset pre exit 时关闭,下次开启是 v18。

移除(strict 删除 + guidance 处方,账本行删除)

FROM 它声称的 实际
mapping.extractQuery "Query to run for export only" 没有任何 exporter 读过 mapping 工件
mapping.errorPolicy skip / abort / retry 三个值是同一个行为;错误处理属于 import 请求
mapping.batchSize 批大小 写路径自己定批大小
app.contextSelectors[].includeAll 加一个 "All" 行 见下
app.contextSelectors[].placement sidebar_header / topbar 没有渲染器读它;'topbar' 什么也没放

os migrate meta --from 16 自动改写既有源码。

includeAll 值得看两遍

它不是没人读,是被故意无视,而且理由是安全的。context selector 是 mandatory-scope 语义,一个 "All" 行会把一个「存在就是为了被限定」的表面解除限定 —— 对 Studio 的 package 选择器而言,那等于把平台自己的 system/cloud 内核包列给一个明明已经限定到自己包的开发者看。渲染器无论这个 flag 取什么值都从不渲染 All 行,所以 includeAll: false 没加固任何东西,includeAll: true 也没解锁任何东西。

STUDIO_APP 就这么写着 includeAll: true,对着一个忽略它的渲染器 —— 这个 authoring 点在本 PR 里跟着键一起走。

batchSize 刻意不提供改名建议

bulkActionDef.batchSizeconnector.batchSizesync.batchSizeoffline.batchSize、seed-loader 的、NoSQL driver cursor 的 —— 全都是活的、被强制执行的,但每一个都是另一个类型上的另一个键、各自定各自的批。「removed」再加上一行之外一个眼熟的名字,正是死设置被洗成活设置的路径。和一个 issue 之前 datasource.retryPolicy 要防的 hook/job retryPolicy(延迟拼作 backoffMs)是同一个陷阱。有 pin 测试钉住报错文案必须把它们说成 DIFFERENT 键。

退役的 alias 拼法(queryonErrorerrorHandlingerrorModebatchchunkSizeskipErrorsshowalllocation)路由到同样的处方,而不是建议改名到一个同样已经没了的键。

接通,不移除 —— doc.tags

BookGroup.include 一直接受 { tag },而它在任何 stack 里都匹配不中任何一个 doc。不是因为 matcher 缺失:

  • matchesInclude 一直在比对 doc.tags —— packages/spec/src/system/book.zod.ts:213
  • book 路由一直在转发 tags: d.tags —— packages/rest/src/rest-server.ts:3218
  • ResolverDoc 一直声明着 tags?: string[],注释写着 (P3d; absent today)

缺口在授权端的一行:DocSchema.strict() 且没有 tags 键,所以在 doc 上写 tags: 是解析错误,每个 doc 到达 resolver 时 tags 都是 undefined。

ADR-0049 说功能存在时 enforce 胜出。移除变体的代价还包括:union 成员没有干净的墓碑(retiredKey 只覆盖 object 键),作者只会拿到一条不带处方的 union 报错,而且要丢掉一份能用的 matcher 代码。

ADR-0087

  • 新增 conversion mapping-inert-keys-removed —— 刻意限定到 mappings 集合:全局 strip 会把 connector / sync / bulk-action / offline 上被强制执行的 batchSize 一起删掉
  • 扩展既有的 app-dead-authoring-keys-removed 下钻 contextSelectors 数组(同 major 同类型,新开一条会撞 fixture 互斥契约),expectedNotices 4 → 6
  • 两条都接进 protocol-17 的 D3 链步,step17.rationale 已扩写

连带核验:allValue

它的账本 note 明写着 "re-verify if includeAll is ever removed" —— 照做了。仍然 live(shell 真的读它,用于自动选中和 query 参数默认化),但它的 .describe() 不再把它叫做「All 被选中时发出的值」:那个事件不可能发生,也从来没可能过。

顺带发现(已另行记录,未在此修)

核验 selector 键时确认了 filterAppForUser 只走顶层 navigation 树,从不读 item.areasrest-server.ts:1823navigation 缺失时提前返回),客户端 area 切换器也照渲染所有 area。所以 area 级的 visible / requiredPermissionsfail-open 的闸门,不只是没人读 —— 一个「隐藏的」或「需要权限的」area 对所有人可见。

账本里已如实记录,并另开 #4651 走 enforce-or-remove 决策。没有在这里顺手修:在一个退役 PR 里就地发明授权机制,正是 #4583 对 managed 只读闸门拒绝做的事(#4584)。

跨仓:objectui#3208 记录了下次抬 spec pin 时会断的两条 parity 断言(objectui 的 selector schema 按引用派生自 spec,漂移守卫会如期触发)。

验证

  • 全量 pnpm build 71/71、pnpm typecheck 122/122、pnpm lint 干净
  • 全量 pnpm test 132/132(dogfood 单独复跑 453 passed / 3 skipped —— 首轮它拿到 SIGTERM 是我并发跑 typecheck 造成的资源争用,非本 PR 问题)
  • spec 十三道闸门全绿:liveness / empty-state / authorable-surface / docs / api-surface / spec-changes / upgrade-guide / skill-refs / skill-docs / skill-examples / variant-docs / strictness-ledger / generated,外加 check:i18n
  • check:livenessmapping 死键 3 → 0(继 datasource 账本判定的 20 条死键至今无人处置:三个块整块无人读,其中 readOnly 让一个 shipped 示例的「只读副本」可写(ADR-0049 enforce-or-remove) #4583 的 datasource 之后第二个归零的类型),app 6 → 4,doc 新增 tags 行为 live
  • 新增 pin 测试:三条 mapping 拒绝(batchSize 那条同时断言文案含 "relocating" 与 connector.batchSize|sync.batchSize)、两条 selector 拒绝、alias 路由、allValue 存活、book tag 端到端匹配
  • 回归钉:bulk-action / sync / connector / offline / seed-loader 的 batchSize 未被 conversion 误伤
  • authorable-surface.json 五行消失是 strict 删除的预期特征(闸门 (a) 的绊线),本 PR 刻意删除

🤖 Generated with Claude Code

https://claude.ai/code/session_01E5CYr5SDwe85gH2Jr5KSgu


Generated by Claude Code

…d connect doc.tags (#4509)

Closes the "顺带的三个小清理候选" section of #4509 — the part left over after
#4558 landed the four structural disconnects.

What groups the five retirements is not the type they sit on but WHY they had
to go out in a major rather than after a deprecation cycle: four of the five
carry schema DEFAULTS, and a default materialises at parse time, so the
liveness advisory lint cannot tell a value the author wrote from one the schema
supplied. Marking them would have warned on every mapping and every selector in
existence — which is why the ledger recorded `_authorWarnSkipped` instead of
`authorWarn`. For a key in that state, removal is not the escalation after a
warning; it is the only channel that ever reaches the author. With spec at
17.0.0-rc.1 and pre-mode still open, that channel closes at `changeset pre
exit` and reopens in v18.

Removed (strict deletion + `guidance` prescriptions, ledger rows deleted):

  mapping.extractQuery    promised an export path no exporter implements
  mapping.errorPolicy     error handling belongs to the import REQUEST
  mapping.batchSize       the write path sizes its own batches
  app.contextSelectors[].includeAll
  app.contextSelectors[].placement

`includeAll` is the one worth reading twice: not unread but deliberately
DISOBEYED, and for a security reason. Context selectors are mandatory-scope, so
an "All" row would clear a scope that exists to be scoped — on Studio's package
selector that means listing the platform's own system/cloud kernel packages to a
developer who scoped to their own package. STUDIO_APP shipped authoring
`includeAll: true` against a renderer that ignored it; that authoring site goes
with the key here.

`batchSize` deliberately offers no rename. bulkActionDef/connector/sync/offline
/seed-loader/NoSQL-cursor `batchSize` are all live and enforced, but each is a
different key on a different type sizing its own path. "Removed" plus a familiar
name one line away is exactly how a dead setting gets laundered into a
live-looking one — the same trap datasource.retryPolicy had to defuse against
hook/job retryPolicy (which spell the delay `backoffMs`) in #4583. A pin test
asserts the message names them as DIFFERENT keys.

Retired ALIAS spellings (query, onError, errorHandling, errorMode, batch,
chunkSize, skipErrors, showall, location) route to the same prescriptions rather
than suggesting a rename onto a key that is also gone.

Connected, not removed — doc.tags:

`BookGroup.include` has always accepted `{ tag }`, and it could never match a
single doc in any stack. Not because the matcher was missing: `matchesInclude`
compares `doc.tags`, the book route already forwards `tags: d.tags`, and
`ResolverDoc` already declared `tags?: string[]` annotated "(P3d; absent
today)". The gap was one line at the AUTHORING end — DocSchema is strict and had
no `tags` key, so writing one was a parse error and every doc reached the
resolver with tags undefined. ADR-0049 says enforcement wins when the feature
exists; removing the variant would also have discarded working matcher code and
left authors a bare union error carrying no prescription.

ADR-0087: new conversion `mapping-inert-keys-removed` (scoped to the `mappings`
collection deliberately — a stack-wide strip would delete an enforced batchSize
from connector/sync/bulk-action/offline) plus an extension of
`app-dead-authoring-keys-removed` to drill the contextSelectors array; both
wired into the protocol-17 D3 chain step.

`allValue` was re-verified as its ledger note required: still live (the shell
reads it for auto-selection and query-param defaulting), but its describe() no
longer calls it "the value emitted when All is selected" — an event that cannot
occur and never could.

Incidental, from confirming the area gates while working the selector keys:
filterAppForUser walks only the top-level `navigation` tree and never reads
`item.areas`, so area-level visible/requiredPermissions are FAIL-OPEN, not
merely unread. Recorded accurately in the ledger and filed as #4651 rather than
fixed here — inventing an authorization mechanism inside a retirement PR is
exactly what #4583 declined to do for managed read-only.

mapping joins datasource at zero dead keys.

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

vercel Bot commented Aug 2, 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 2, 2026 3:21pm

Request Review

…onnect-issues-6wz8ew

# Conflicts:
#	docs/protocol-upgrade-guide.md
#	packages/spec/src/migrations/registry.ts
@github-actions

github-actions Bot commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 2 package(s): @objectstack/platform-objects, @objectstack/spec.

107 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 @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/spec)
  • 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 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 packages/spec)
  • content/docs/concepts/north-star.mdx (via packages/spec)
  • content/docs/data-modeling/analytics.mdx (via @objectstack/spec)
  • content/docs/data-modeling/drivers.mdx (via @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/troubleshooting.mdx (via @objectstack/spec)
  • content/docs/deployment/validating-metadata.mdx (via @objectstack/spec)
  • 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/spec)
  • content/docs/kernel/cluster.mdx (via @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/authorization.mdx (via @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/platform-objects, @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/i18n-standard.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/index.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/lifecycle.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/plugin-spec.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/runtime-capabilities.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/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/spec)
  • content/docs/releases/v9.mdx (via @objectstack/spec)
  • content/docs/ui/actions.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/platform-objects, @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.

@os-zhuang
os-zhuang marked this pull request as ready for review August 2, 2026 15:32
@os-zhuang
os-zhuang added this pull request to the merge queue Aug 2, 2026
Merged via the queue into main with commit 5966c2a Aug 2, 2026
22 checks passed
@os-zhuang
os-zhuang deleted the claude/auth-gate-disconnect-issues-6wz8ew branch August 2, 2026 15:44
os-zhuang pushed a commit that referenced this pull request Aug 2, 2026
#4664 (spec key retirements + doc.tags) landed on main and touched the same
five files. Resolution:

- authorable-surface.json / spec-changes.json / docs/protocol-upgrade-guide.md
  REGENERATED from source (gen:schema / gen:spec-changes / gen:upgrade-guide),
  never hand-merged — hand-editing the authorable surface is forbidden (#4650).
  The regenerated surface differs from main by exactly this branch's delta:
  `automation/RetryPolicy:backoffMs` added, `:retryDelayMs` relabelled
  [RETIRED], and system/RetryPolicy gaining jitter / maxRetryDelayMs / the
  tombstone.
- conversions/registry.ts auto-merged; verified 40 entries, zero duplicate ids,
  every declared conversion grouped, both `mappingInertKeysRemoved` (#4664) and
  `retryPolicyConverged` (#4661) present in the major-17 block.
- migrations/registry.ts hand-resolved: both sides appended a paragraph to
  step17's `rationale` and an entry to `conversionIds`. Kept both. #4664's
  paragraph ended the string literal, so the concatenation was repaired and this
  branch's opener reworded ("Finally" -> "The same window") to avoid two
  "Finally"s in one rationale.

Re-verified after the merge that exactly ONE conversion clause still ends in
`.retryDelayMs` and it is `retry-policy-converged` — #4664 added five retired
leaves (extractQuery / errorPolicy / batchSize / includeAll / placement), none
of which collide with this cluster under the #4659 leaf-name match.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M9uWvoEp9CoLzYjNExj9sL
os-zhuang pushed a commit that referenced this pull request Aug 2, 2026
…y convergence

#4664 retires `mapping.errorPolicy` in the same 17.0.0 window and its values
included 'retry', so a reader could plausibly assume it migrates into a
`retryPolicy` block. It does not — different type, inert enum, and its own
prescription points at the import REQUEST's options.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M9uWvoEp9CoLzYjNExj9sL
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Aug 3, 2026
… 的区分说明 (objectstack-ai#4653) (objectstack-ai#4679)

PR objectstack-ai#4662 合并时,这段说明还在未提交状态(worktree 被清理时一并丢失),
故补一个 changeset-only 的跟进。无代码改动。

同窗口的 objectstack-ai#4664 退休了 `app.contextSelectors[].placement`,而它的退休说明
里写着「`location` 曾是 `placement` 的别名」。Studio 插件的面板贡献点恰好
也有一个 `location` 键(`studio/PanelContribution.location`),两者在不同
schema 上、取值域不同、互不相关 —— 但对着 v17 release notes 逐条读的作者
很容易把两件事连起来,以为 `contributes.panels[].location` 也要改。

changeset 里主动写清这个区分,挡掉误解;同时记下另外四个退休键与 objectstack-ai#4668
与 `activationEvents` 均无语义交叉。


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

Co-authored-by: Claude <noreply@anthropic.com>
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Aug 3, 2026
…i#4661, C8) (objectstack-ai#4670)

* feat(spec)!: converge RetryPolicy onto one declaration (objectstack-ai#4661)

`@objectstack/spec/automation` and `@objectstack/spec/system` both exported
`RetryPolicySchema` / `RetryPolicy` resolving to DIFFERENT declarations, so the
shape a consumer got depended only on the import path (the objectstack-ai#4411 trap). They
were never two concepts: the `try_catch` node's `retry` region and
`job.retryPolicy` both compute `delay = base * multiplier^(retry-1)`, and both
executors implemented that identical formula.

One declaration now lives in `shared/retry-policy.zod.ts`, re-exported by both
entries, carrying the union of what the two sides could express. Because the
published def key is derived from the entry namespace, both
`automation/RetryPolicy` and `system/RetryPolicy` survive with an identical key
set — so the convergence costs exactly ONE authorable key instead of eight.

Authorable surface: `automation/RetryPolicy:retryDelayMs` is the single
casualty, TOMBSTONED (`retiredKey`) rather than deleted because neither owning
schema is `.strict()` — a plain removal would have Zod swallow the authored
number and silently fall back to the 1000ms default.

Defaults are the half no gate can see: the authorable-surface ratchet compares
key sets, and a default is not a key. `job.retryPolicy` defaulted
`maxRetries: 3` / `backoffMultiplier: 2` where automation defaulted 0 / 1. The
merged declaration takes 0 / 1 (retry replays side effects, so it is opt-in),
and the `retry-policy-converged` conversion writes the pre-17 numbers explicitly
into every existing `job.retryPolicy` that omitted them — deployed stacks keep
their exact behaviour; only a newly authored omission changes meaning. Filed
separately as objectstack-ai#4666 (gates are blind to default/constraint changes).

`job.retryPolicy` gains `maxRetryDelayMs` and `jitter`, both now enforced in
`runWithPolicy` rather than merely declared (ADR-0049).

Baseline: 22 -> 20.

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

* docs(changeset): clarify mapping.errorPolicy is unrelated to the retry convergence

objectstack-ai#4664 retires `mapping.errorPolicy` in the same 17.0.0 window and its values
included 'retry', so a reader could plausibly assume it migrates into a
`retryPolicy` block. It does not — different type, inert enum, and its own
prescription points at the import REQUEST's options.

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

* fix(spec): regenerate spec-changes.json after the main merge

上一个合并提交用集合合并解决 spec-changes.json 的冲突,但该文件是
对象数组而非字符串数组,集合合并退化成「取变了的那一侧」,丢掉了
main 侧 objectstack-ai#3355 的 object-managed-by-system-to-system-data 条目,
check:spec-changes 因此报 stale。

改为真的跑 gen:spec-changes(+ gen:upgrade-guide 确认无漂移)。
现三条条目齐备:retry-policy-converged、
object-managed-by-system-to-system-data、
job-retry-policy-constraints-tightened。

check:spec-changes / check:upgrade-guide / check:skill-docs 均绿。

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

---------

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants