feat(spec): ratchet cross-entry dual-source exports — same name, different declaration, judged by symbol identity (#4446) - #4506
Merged
Conversation
…erent declaration, judged by symbol identity (#4446) api-surface.json records every export per entry point, so a name appearing on two entries was VISIBLE — but nothing distinguished the two ways that happens, and only one of them is fine: a re-export (one declaration, two import paths) versus a DUAL-SOURCE (each entry resolving the shared name to its own declaration, so which type a consumer gets depends on nothing but the import path). The dual-source case is the #4411 trap: eleven names declared twice across ./kernel and ./system, where the copy that LOOKED canonical was the dead one — a pick by name compiled and failed later, at an edge value. New pure check `check:dual-source-exports`: - Judged by SYMBOL IDENTITY, not name: every export of all 16 public entries is resolved through its alias chain to the original symbol; a name whose entries resolve to >=2 distinct symbols is dual-source. Name-based counting would drown the signal — the real surface carries 148 legitimately re-exported names next to the 63 real findings. - Shrink-only baseline (dual-source-exports.baseline.json) records the 63 existing dual-sources — including the MetadataFormat ./shared≠./system enum divergence, the ./contracts third-shape interfaces, and two type-vs-const cases (ShareRecipientType, TransformType) the name-level scan could not even see. A NEW dual-source fails with the fix at the declaration (converge + re-export, or rename); a resolved one fails until its line is deleted. The baseline is hand-edited under review, deliberately NOT generated: a `gen:` that rewrites it would admit new dual-sources via "run the fix command" instead of via a maintainer decision. - Self-tests first (the check-exported-any pattern), pinning both edges: a fixture dual-source (incl. type-vs-const) must be flagged, a re-export must not, and count assertions keep a resolution failure from reading as clean. - Wired everywhere a new check must be: package.json, the check:generated reconciliation ledger (NO_GENERATOR — it would fail the run unclassified), lint.yml's TypeScript Type Check job after the build step, and the AGENTS.md pure-checks paragraph. Also fixes a fresh flake this work kept tripping over: the #4491 parity tests spawn a tsx subprocess that loads the whole spec surface (~4.5s alone, 5-7s under turbo's parallel load) against vitest's 5s default timeout — three consecutive full-suite runs failed a DIFFERENT test of that file each time, every one a timeout, while the file alone stayed green. The six spawning tests now carry an explicit 60s timeout: a timeout there should mean "the script hung", not "the runner was busy". Closes #4446. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01M9uWvoEp9CoLzYjNExj9sL
…-event-dual-source-v3jw3v
…olved First contact with reality, one merge in: #4500 removed the connector "template" cluster, deleting the ./integration copies of ConsumerConfig, DatabaseProvider, MessageQueueProvider and MultipartUploadConfig (type + Schema each). Those 8 names are no longer dual-source, and the gate's stale-entry leg refused to pass until their baseline lines were deleted — the shrink-only ratchet ratcheting down exactly as designed. 63 → 55. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01M9uWvoEp9CoLzYjNExj9sL
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
Contributor
📓 Docs Drift CheckThis PR changes 1 package(s): 107 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
|
github-merge-queue
Bot
removed this pull request from the merge queue due to failed status checks
Aug 1, 2026
…-event-dual-source-v3jw3v
…olved Second catch in one afternoon, this time inside the merge queue: the queue built this branch against a main that had just landed #4503 (trigger-registry Connector cluster removal), which deleted the ./automation copies of Connector, ConnectorSchema and ConnectorTriggerSchema — and the gate refused the queue build until their baseline lines were gone. 55 → 52. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01M9uWvoEp9CoLzYjNExj9sL
os-zhuang
enabled auto-merge
August 1, 2026 14:10
This was referenced Aug 2, 2026
akarma-synetal
pushed a commit
to akarma-synetal/framework
that referenced
this pull request
Aug 2, 2026
…y enum declarations (objectstack-ai#4537) (objectstack-ai#4557) Two enum vocabularies were declared on both ./shared and ./system and had diverged on their VALUES — which accepted value set you got depended on nothing but the import path (the objectstack-ai#4411 trap; objectstack-ai#4535 A2, objectstack-ai#4506 baseline). Value-level divergence is the silent kind: a mismatched value never fails to compile. MetadataFormat / MetadataFormatSchema — shared declaration is the single source: - system/metadata-persistence.zod.ts no longer declares its own 7-member copy; it re-exports shared/metadata-types.zod (the MetadataManagerConfig pattern, in reverse). The extension-style aliases yml/ts/js it carried had zero producers in this repo, objectui and cloud: FilesystemLoader.detectFormat normalizes at the boundary (.yml -> 'yaml', .ts -> 'typescript', .js -> 'javascript') and the database/remote/memory loaders always emit 'json'. - Direction: shared, not system — kernel/metadata-loader.zod has imported the shared schema since objectstack-ai#4411 (MetadataManagerConfig.formats needs no change), and system already re-exports MetadataManagerConfigSchema FROM kernel, so a system-side source would close a module cycle (shared -> system -> kernel -> shared). CacheStrategy — system/cache.zod.ts (CacheStrategySchema) is the single declaration: - shared/enums.zod.ts CacheStrategyEnum (+ its CacheStrategy type export) removed: zero importers in all three repos, while the system schema is the one CacheTier.strategy gates on. Same disposition as AggregationFunctionEnum in the same file (objectui#2945): removed rather than reconciled. - 'adaptive', declared only on the system side with zero producers, is dropped — the enum carries the four values both declarations agreed on (lru | lfu | fifo | ttl). Bookkeeping: - dual-source-exports.baseline.json: the three converged rows deleted (shrink-only ratchet). - json-schema.manifest.json: shared/CacheStrategyEnum key deleted (deliberate retirement, objectstack-ai#2978 flow). - Pin tests: yml/ts/js rejected on the system entry; 'adaptive' rejected on CacheStrategySchema. - api-surface.json, references docs, skill refs regenerated via check:generated --fix. - Changeset (major) carries the FROM -> TO migration lines. - No ADR-0087 conversion / tombstone: loader envelope + config vocabulary with no authorable-metadata producers (the objectstack-ai#4411/objectstack-ai#4536 route). Claude-Session: https://claude.ai/code/session_01M9uWvoEp9CoLzYjNExj9sL Co-authored-by: Claude <noreply@anthropic.com>
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 #4446(#4411 遗留裁决 2)。
api-surface.json记录每个入口的每个导出,所以一个名字出现在两个入口上是可见的 —— 但没有任何东西区分这件事的两种成因,而只有一种是无害的:re-export(一个声明、两条 import 路径)vs 双源(两个入口把同一个名字各自解析到自己的声明,消费者拿到哪个类型只取决于 import 路径)。后者就是 #4411 的陷阱:11 个名字在./kernel与./system各一份,而看起来更正统的那份是死的。新 gate:
check:dual-source-exportsdual-source-exports.baseline.json):记录存量双源,含MetadataFormat./shared≠./system枚举分歧、./contracts第三形状 interface,以及两个 type-vs-const 跨形态同名(ShareRecipientType、TransformType)—— 名字级扫描根本看不见的形态。新增双源即红,修复指引在声明侧(收敛 + re-export,或改名);已解决的条目不删基线行也红。基线刻意不做成生成物 —— 有gen:就等于"跑一下修复命令"绕过维护者裁决。check:exported-any模式):fixture 钉死两条边 —— 真双源(含 type-vs-const)必须报、re-export 必须不报,计数断言防解析静默失败装绿。check:generatedreconciliation 分类账(不注册它自己会红)、lint.yml 的 TypeScript Type Check job(build 之后,与两个读 dist 的兄弟 gate 同排)、AGENTS.md 纯检查段落。上线第一次接触现实就干活了
开发期间 main 落了 #4500(移除 connector template 家族),
./integration侧 8 个名字消失 —— gate 的 stale 分支当场拒绝通过,直到基线删掉那 8 行(63 → 55)。棘轮首日就向下拧了 8 格,单独一个 commit 可查。顺带:拆掉 #4491 刚埋的超时炸弹
开发期间连续三轮
pnpm test全量跑,check-react-blocks-declaration-parity.test.ts每轮红一个不同的测试、全部是超时,单跑永远绿。根因:每个测试 spawn 一个加载整个 spec 面的 tsx 子进程(单独 ~4.5s、turbo 并发下 5–7s),顶着 vitest 默认 5 秒超时掷骰子 —— 这会随机打红所有 PR 的 Test Core 和队列构建。6 个 spawn 型测试补了显式 60 秒超时:超时应该表示"脚本挂了",不是"runner 忙"。验证
check:generated八件套 up to date、reconciliation 确认新脚本已分类pnpm typecheck122 tasks、pnpm test132 tasks 全绿Generated by Claude Code