refactor(metadata)!: 删除 artifact-api artifact source——零消费者,owner 已裁决不保留 (#4246) - #4259
Merged
os-zhuang merged 2 commits intoJul 31, 2026
Merged
Conversation
…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
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
Contributor
📓 Docs Drift CheckThis PR changes 2 package(s): 107 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
|
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
artifact-api 按「已实现」声明,并修掉它那条自 v5.0 起就失效的 URL 判据 (#4246)artifact-api artifact source——零消费者,owner 已裁决不保留 (#4246)
os-zhuang
marked this pull request as ready for review
July 31, 2026 02:42
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #4246
第一个 commit(5597fab)按 issue 的选项 1 落地——判定实现有效、把注释和文档改成「已实现」,顺带修了一条死掉的 URL 判据。随后跨仓库的消费者审计推翻了保留它的前提,owner 裁决删除,第二个 commit(660124f)执行。最终 diff 以删除为准;下面按最终状态描述。
裁决依据:三条证据
mode: 'artifact-api'的生产调用。「从云端拉 artifact」的两条真实路径各有归属:cloud 运行时用自己的ArtifactApiClient(TTL 缓存、singleflight、hostname 解析、runtime 配置注入——本 option 永远不该长成的超集);OSS 运行时装包走@objectstack/cloud-connection(os package install,ADR-0008)。/api/v{n}/cloud/projects/段被 v5.0 改名删掉后永不匹配,「完整 URL」形态一律被二次拼接后 404。这种 bug 的沉默期本身就是消费者数量的证据。local-file做不到的唯一一件事(local-file的 URL 分支不发鉴权头)。owner 确认「私有 artifact 密封部署」当前不是需要支持的场景。删了什么
artifactSource的artifact-apiunion 成员{ mode: 'local-file', path, fetchTimeoutMs? }_loadFromArtifactApi()environmentId前置检查_fetchJson的token参数artifact-api分支local-file路径删除是响亮的,不是静默的
TS 层面 union 已收窄,但 JS 调用方 /
any管道仍可能塞进旧配置。旧分派的 fall-through 会把「不支持的 source」当成「没有 source」——eager下就是静默转去扫文件系统,boot 出跟调用方点名的 artifact 毫无关系的内容。因此start()现在前置拒绝任何非local-file的 mode,artifact-api得到指名道姓的迁移消息(指向/pub路由的local-fileURL 形态和@objectstack/cloud-connection)。迁移
公开 / 按 commit 固定的 artifact 走既有的
local-fileURL 形态(所有 bootstrap mode 都认):private环境仍可通过同一/pub路由的精确 commit 深链分发;完全私有的拉取无替代——是决策,不是疏漏。装包进运行中的实例用os package install。测试
artifact-only和eager下对已删 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.mdx、metadata-service.mdx、packages/metadata/ROADMAP.md、spec 里 bootstrap 的注释——全部改为描述单一local-filesource 并记录删除决策。#4246 立案的起因(每轮文档审计都重新撞上注释与实现的矛盾)随之终结。hook-bodies.mdx的cloud-artifact-api指 cloud 侧控制面插件(另一仓库的服务端,不受影响),ADR-0005 属历史记录,均未动。Breaking
@objectstack/metadatamajor changeset。删除 public API 成员;仓库内所有local-file调用方(standalone-stack.ts、serve.ts、start.ts)不受影响。