Skip to content

refactor(metadata)!: 删除 artifact-api artifact source——零消费者,owner 已裁决不保留 (#4246) - #4259

Merged
os-zhuang merged 2 commits into
mainfrom
claude/metadata-plugin-artifact-api-loader-0r6eco
Jul 31, 2026
Merged

refactor(metadata)!: 删除 artifact-api artifact source——零消费者,owner 已裁决不保留 (#4246)#4259
os-zhuang merged 2 commits into
mainfrom
claude/metadata-plugin-artifact-api-loader-0r6eco

Conversation

@os-zhuang

@os-zhuang os-zhuang commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Closes #4246

⚠️ 本 PR 中途转向:从「声明它」改为「删除它」

第一个 commit(5597fab)按 issue 的选项 1 落地——判定实现有效、把注释和文档改成「已实现」,顺带修了一条死掉的 URL 判据。随后跨仓库的消费者审计推翻了保留它的前提,owner 裁决删除,第二个 commit(660124f)执行。最终 diff 以删除为准;下面按最终状态描述。

裁决依据:三条证据

  1. 两个仓库、零调用点。 本仓库和 cloud 里都不存在 mode: 'artifact-api' 的生产调用。「从云端拉 artifact」的两条真实路径各有归属:cloud 运行时用自己的 ArtifactApiClient(TTL 缓存、singleflight、hostname 解析、runtime 配置注入——本 option 永远不该长成的超集);OSS 运行时装包走 @objectstack/cloud-connectionos package install,ADR-0008)。
  2. 一半输入契约自 v5.0 起就是死的,一年多无人报。 URL 判据里的 /api/v{n}/cloud/projects/ 段被 v5.0 改名删掉后永不匹配,「完整 URL」形态一律被二次拼接后 404。这种 bug 的沉默期本身就是消费者数量的证据。
  3. 唯一不可替代的能力被 owner 明确放弃。 Bearer 鉴权拉取私有环境 artifact 是 local-file 做不到的唯一一件事(local-file 的 URL 分支不发鉴权头)。owner 确认「私有 artifact 密封部署」当前不是需要支持的场景。

删了什么

说明
artifactSourceartifact-api union 成员 类型收窄为单成员 { mode: 'local-file', path, fetchTimeoutMs? }
_loadFromArtifactApi() 连同其 environmentId 前置检查
_fetchJsontoken 参数 唯一调用方随上者消失
三个 bootstrap 分派点的 artifact-api 分支 收敛为单一 local-file 路径

删除是响亮的,不是静默的

TS 层面 union 已收窄,但 JS 调用方 / any 管道仍可能塞进旧配置。旧分派的 fall-through 会把「不支持的 source」当成「没有 source」——eager 下就是静默转去扫文件系统,boot 出跟调用方点名的 artifact 毫无关系的内容。因此 start() 现在前置拒绝任何非 local-file 的 mode,artifact-api 得到指名道姓的迁移消息(指向 /pub 路由的 local-file URL 形态和 @objectstack/cloud-connection)。

迁移

公开 / 按 commit 固定的 artifact 走既有的 local-file URL 形态(所有 bootstrap mode 都认):

artifactSource: {
  mode: 'local-file',
  path: 'https://cloud.example.com/pub/v1/environments/env_42/artifact?commit=cmt_1a2b',
}

private 环境仍可通过同一 /pub 路由的精确 commit 深链分发;完全私有的拉取无替代——是决策,不是疏漏。装包进运行中的实例用 os package install

测试

  • 钉住 artifact-onlyeager 下对已删 mode 的响亮拒绝(eager 额外断言没有退化去扫文件系统——守卫要防的正是这个)
  • 钉住迁移目标本身:local-file 拉 http(s) URL、解析信封、注册进 manager——错误消息指的路必须真实存在
  • 顺带汰换了那条「碰巧过」的旧测试:rejects.toThrow(/artifact-api/) 匹配的是缺 environmentId 的消息恰好含这串字符,对「是否实现」什么也没证明
  • pnpm --filter @objectstack/metadata test — 13 files / 281 tests 全绿;build + tsc --noEmit(plugin.ts)干净

文档

implementation-status.mdxmetadata-service.mdxpackages/metadata/ROADMAP.md、spec 里 bootstrap 的注释——全部改为描述单一 local-file source 并记录删除决策。#4246 立案的起因(每轮文档审计都重新撞上注释与实现的矛盾)随之终结。hook-bodies.mdxcloud-artifact-api 指 cloud 侧控制面插件(另一仓库的服务端,不受影响),ADR-0005 属历史记录,均未动。

Breaking

@objectstack/metadata major changeset。删除 public API 成员;仓库内所有 local-file 调用方(standalone-stack.tsserve.tsstart.ts)不受影响。

…fix its dead URL guard (#4246)

`MetadataPluginOptions.artifactSource` said "Only `local-file` is implemented
now; `artifact-api` is reserved for M3/M4" while `_loadFromArtifactApi` and all
three `start()` dispatch branches (`eager` / `lazy` / `artifact-only`) already
shipped. That is Prime Directive #10's declared ≠ enforced running backwards —
the declaration understating the runtime — and it is not self-correcting:
`implementation-status.mdx` copied the claim, so each docs accuracy audit
rediscovers the contradiction and can only re-file it.

Resolved toward option 1 of the issue: the implementation is valid, so the
declaration moves. The option doc, the docs bullet, the metadata-service page
and the package ROADMAP now describe what `start()` does — both modes, the
`environmentId` requirement, `?commit=` pinning and the Bearer token.

The test guarding this passed for the wrong reason. "artifact-only bootstrap
rejects the not-yet-implemented artifact-api source" asserted
`rejects.toThrow(/artifact-api/)`, but the only throw on that path is the
missing-`environmentId` pre-flight guard, whose message merely contains the
string — it matched while proving nothing. Replaced with a suite pinning URL
construction for both accepted input shapes, commit pinning, the Authorization
header, dispatch from each bootstrap mode, a loud failure on a non-OK response,
and the `environmentId` guard asserted on its own message.

Auditing the loader to justify "valid" surfaced one real defect. The URL builder
chose between appending the canonical path and using the URL as-is by testing
for a `/api/v{n}/cloud/projects/` segment — a path the v5.0 `project →
environment` rename deleted, leaving the guard unmatchable. Every
already-resolved artifact URL therefore had the canonical path appended a second
time and 404'd, so half of the option's documented input shape was dead. The
check is now "does the path already name an artifact endpoint", restoring the
intended dual form and keeping the `unlisted` public route
(`/pub/v1/environments/:id/artifact`) addressable. Callers passing a
control-plane base URL — the only form that worked — are unaffected.

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

vercel Bot commented Jul 31, 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 Jul 31, 2026 2:22am

Request Review

@github-actions github-actions Bot added size/m documentation Improvements or additions to documentation tests tooling labels Jul 31, 2026
@github-actions

github-actions Bot commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 2 package(s): @objectstack/metadata, @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 packages/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 @objectstack/metadata, 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 packages/metadata, @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/metadata, @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/metadata, @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/metadata-service.mdx (via @objectstack/metadata)
  • 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/metadata, @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/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.

Flips this branch's resolution of #4246 from "declare the implementation"
to "remove it", on the owner's call: the one capability only artifact-api
provided — a Bearer-authenticated pull of a private environment artifact —
is not a supported need right now.

The consumer audit that motivated the flip: no mode:'artifact-api' call
site exists in this repo or in cloud. The cloud runtime pulls artifacts
through its own ArtifactApiClient (TTL cache, singleflight, hostname
resolution — a superset), and package distribution into a running OSS
instance goes through @objectstack/cloud-connection (os package install).
Public / commit-pinned artifact boot stays fully supported via the
existing local-file URL form (the control plane's public
/pub/v1/environments/:id/artifact route serves it verbatim).

Removed: the artifact-api union member on MetadataPluginOptions.
artifactSource, _loadFromArtifactApi, its environmentId pre-flight guard,
and _fetchJson's token parameter. The three bootstrap dispatch sites
collapse to the single local-file path, behind a new loud guard: a
still-configured artifact-api source (reachable via JS or any-typed
config) throws at start() with a migration pointer, because the old
fall-through would have treated "unsupported source" as "no source" —
under eager that silently scans the filesystem instead of loading the
artifact the caller named.

Tests pin the rejection in artifact-only and eager, and pin the migration
target (local-file fetching an http(s) URL and registering the envelope)
so the path the error message names stays real. Docs
(implementation-status, metadata-service, package ROADMAP, the spec
bootstrap comment) now describe the single local-file source, ending the
docs-audit loop #4246 was filed to stop.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FC9p9G2sHw1Tbf1wvLJhVK
@os-zhuang os-zhuang changed the title fix(metadata): artifact-api 按「已实现」声明,并修掉它那条自 v5.0 起就失效的 URL 判据 (#4246) refactor(metadata)!: 删除 artifact-api artifact source——零消费者,owner 已裁决不保留 (#4246) Jul 31, 2026
@os-zhuang
os-zhuang marked this pull request as ready for review July 31, 2026 02:42
@os-zhuang
os-zhuang merged commit ac6c0be into main Jul 31, 2026
18 checks passed
@os-zhuang
os-zhuang deleted the claude/metadata-plugin-artifact-api-loader-0r6eco branch July 31, 2026 02:43
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/m tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

MetadataPlugin 的 artifact-api loader:注释说「保留未实现」,实现却已存在并被引导流程调用(declared ≠ enforced,方向相反)

2 participants