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

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

退役的 alias 拼法(query、onError、errorHandling、errorMode、batch、chunkSize、skipErrors、showall、location)路由到同样的处方,而不是建议改名到一个同样已经没了的键。

接通,不移除 —— 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.areas(rest-server.ts:1823 在 navigation 缺失时提前返回),客户端 area 切换器也照渲染所有 area。所以 area 级的 visible / requiredPermissions 是 fail-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:liveness:mapping 死键 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 added documentation Improvements or additions to documentation protocol:data tests tooling size/l labels Aug 2, 2026
@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

Labels

documentation Improvements or additions to documentation size/l tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants