Repository navigation
[Decision] Split the platform's largest, fastest-growing source files (protocol.ts 32,428 lines, +10,002 in 30 days) the way rest-server.ts was split, or leave them #24586
Description
Filing gate ②, a decision only the maintainer can make. The maintainer's word in domain:cli seat 1's session chat, verbatim: 「应该立决策卡研究如何分解这些大的文件」. It followed this seat's measurement of packages/metadata-protocol/src/protocol.ts while #24535 waited on a same-function PR.
Filed by domain:cli seat 1 (seat post #6024) · zhuangjianguo · session session_01SYEJqDL3NXxrJRBVqaMu9T. ⛔ Not a claim. Reader who acts: the maintainer, who decides here; then the owning lanes' seats, one card per file.
维护者速读
- 问题: 平台最核心、改得最勤的几块代码,集中在少数几个超大文件里,而且还在快速变大。不同车道的改动频繁撞进同一个文件、甚至同一个函数,只能排队一个一个落地。今天的实例是:p1 的读性能修复 rest: every GET /api/v1/data/OBJECT still runs a full zod safeParse of the object's definition (the API-exposure gate's by-name read is decorated with diagnostics) — 11.8% of a HotCRM read's CPU #24535,在等 engine 车道一个无关 PR 改完同一个函数。
- 规模:
protocol.ts32,428 行,30 天内增加 10,002 行、184 次提交;objectql/engine.ts22,232 行(+6,195);conversions/registry.ts17,722 行(+7,760)。 - 先例: 2026-10 按您的指示拆过
rest-server.ts([maintainer] refactor(rest): splitpackages/rest/src/rest-server.ts(13,701 lines, 70 commits in 30 days) along itsregister*Endpointsseams, one move-only slice per PR,/metaafter #22877 stage 2 #23050):18 个只搬代码的分片 PR,约 6 天,13,701 行降到 6,196 行,期间没有出过事故。 - 推荐 A: 照 [maintainer] refactor(rest): split
packages/rest/src/rest-server.ts(13,701 lines, 70 commits in 30 days) along itsregister*Endpointsseams, one move-only slice per PR,/metaafter #22877 stage 2 #23050 的做法,逐个文件、按车道立卡分片拆,先从protocol.ts开始。不新增门禁。 - 您要做的一个动作: 回一个字母(A / B / C / D)。
Measured (on origin/main ba4afcc23, 2026-10-19)
| file | lines | change in 30 days | commits 7d / 30d | open PRs on it now | owning lane |
|---|---|---|---|---|---|
packages/metadata-protocol/src/protocol.ts |
32,428 | +10,002 (from 22,426) | 67 / 184 | PR #24533 | domain:engine |
packages/objectql/src/engine.ts |
22,232 | +6,195 | 20 / 111 | none | domain:engine |
packages/drivers/driver-sql/src/sql-driver.ts |
21,570 | +2,193 | 4 / 52 | none | domain:engine |
packages/spec/src/conversions/registry.ts |
17,722 | +7,760 | 23 / 77 | none | domain:spec |
packages/services/service-automation/src/engine.ts |
13,530 | +2,067 | 11 / 44 | PR #24527 | domain:services |
packages/rest/src/rest-server.ts (split by #23050) |
6,196 | −7,816 | — | — | domain:cli |
- Eleven more non-test source files sit between 5,000 and 10,000 lines, among them
plugin-security/src/security-plugin.ts(9,705),plugin-auth/src/auth-manager.ts(9,641) andspec/src/ui/component.zod.ts(8,823, 101 commits in 30 days). - Re-check:
git show origin/main:<path> | wc -l;git log --since='30 days ago' --oneline origin/main -- <path> | wc -l; and the same atgit rev-list -1 --before='30 days ago' origin/mainfor growth. - The collision measured today: PR perf(core,metadata-protocol): an authenticated data request pays 6 warm serial rounds instead of 8, and 3 with the landed opt-ins #24533 (
Fixes #10757) rewritesreadMetaItem's item-read cache inprotocol.ts(hunks about:11141–:11436). rest: every GET /api/v1/data/OBJECT still runs a full zod safeParse of the object's definition (the API-exposure gate's by-name read is decorated with diagnostics) — 11.8% of a HotCRM read's CPU #24535 needs an item-level execution read in the same function (:11300–:11920). By the seats' hard-serial rule, rest: every GET /api/v1/data/OBJECT still runs a full zod safeParse of the object's definition (the API-exposure gate's by-name read is decorated with diagnostics) — 11.8% of a HotCRM read's CPU #24535 waits for perf(core,metadata-protocol): an authenticated data request pays 6 warm serial rounds instead of 8, and 3 with the landed opt-ins #24533's merge (record6269601173). - What an agent can read:
protocol.tsis 1.9 MB. A dev agent's file reader returns 2,000 lines per call, so no agent ever reads this file whole; every edit is made with a partial view of a 32,000-line class.
Governing text: none found. git grep -n -i -E "max-lines|file size|god (file|class)|split (the|a) (file|module)|monolith" over AGENTS.md, docs/NORTH-STAR.md, docs/adr and the ESLint configs finds no source-file size rule. The hits are about package and ownership monoliths (ADR-0028, ADR-0029), not file size. No protocol (spec) declaration changes in any option.
Prior rulings read: #23050, the maintainer's 「这个文件经常改,为什么不分拆?rest-server.ts」, then 「立卡,等 #22877 后 (推荐)」. One move-only slice per PR, with gates and doc anchors migrated in the same PR; /meta was held until the conflicting engine PR merged. It landed 18 slices (#23116 … #24304, d1f082d94), +17,258 / −9,553 including the moved tests and anchors.
Options
| what it does | what a customer notices | workload | |
|---|---|---|---|
| A | One [maintainer] card per file over 10,000 lines, in its owning lane, split along the file's existing seams by move-only slices, #23050's rules. protocol.ts first, then objectql/engine.ts and conversions/registry.ts, then the other two. Each slice is serial with the file's in-flight PRs. |
Nothing directly. Fixes stop queuing behind unrelated work in the same file sooner, and agents' edits are made with the whole module in view. | Measured precedent: #23050, 18 PRs in about 6 days for 13,701 → 6,196 lines (one lane). protocol.ts is 2.4× rest-server's starting size, so there is no precedent at that size; expect several lanes in parallel over weeks, with each lane's queue share going to move-only PRs. |
| B | A, plus a new gate: no non-test source file may grow past a line ceiling (a ratchet, as check-skill-line-ratchet does for SKILL.md). |
The same as A, and the files cannot grow back. | A, plus one gate script, its ceiling table and its CI wiring. 「新增门禁默认否」: this needs the maintainer to name it. |
| C | Split only where it hurts: when a card is held by a same-file or same-function serial wait, its seat first moves that seam out in a move-only PR, then does the fix. No program. | Slower relief: only the hot spots get smaller, as they are hit. | No upfront program. Each such card grows by one move-only PR. No precedent. |
| D | Leave the files as they are. | Same-file waits keep growing with the files: protocol.ts gained 10,002 lines in 30 days. |
None now. |
四轴分析
- 项目长远合理性: 两年后,一个成熟的元数据平台,其核心协议层应当是按职责划分的模块,而不是一个 3 万行的类。主流框架的做法都是一类职责一个模块,例如 NestJS 的 module、Django 的 app、Salesforce 平台内部按服务划分。A 一次付清;C 只还热点上的债,冷区会继续长;D 让债以每月约一万行的速度累积。[maintainer] refactor(rest): split
packages/rest/src/rest-server.ts(13,701 lines, 70 commits in 30 days) along itsregister*Endpointsseams, one move-only slice per PR,/metaafter #22877 stage 2 #23050 证明了"只搬代码、逐片落地"在这个仓库行得通,而且没有出过事故。 - 实际业务拉动: 有实测拉动。今天一个 p1 修复就排在无关 PR 后面;
protocol.ts7 天 67 次提交,意味着多个车道每天都在同一个文件上串行。拉动是开发吞吐,不是客户功能;客户感受到的是修复变慢。 - 防 AI 犯错: 这是 A 最强的一条理由。AI 开发者每次只能读到 2,000 行,在 3 万行的类里改代码,看不到同一个类里的其他调用点和不变量,出错时往往是静默的(例如漏改一个兄弟分支)。拆成按职责的模块后,一个模块能整篇读完,改动面和评审面都变小。B 的门禁能防止文件再长回去,但它是新增门禁。
- 创业阶段不扩散: A 不新增任何对外声明、键或接口,纯内部重构;代价是车道队列份额,[maintainer] refactor(rest): split
packages/rest/src/rest-server.ts(13,701 lines, 70 commits in 30 days) along itsregister*Endpointsseams, one move-only slice per PR,/metaafter #22877 stage 2 #23050 期间 cli 车道约有 6 天主要在做搬迁。B 新增一个永久门禁,违背「新增门禁默认否」,除非您点名。C 最省,但债不会减少。
os-decision-facets
- ① 项目长远合理性:A 一次按职责拆清;C 只拆热点、冷区照长;D 每月约万行累积;B 同 A 并防反弹。
- ② 实际业务拉动:有——今天 p1 rest: every GET /api/v1/data/OBJECT still runs a full zod safeParse of the object's definition (the API-exposure gate's by-name read is decorated with diagnostics) — 11.8% of a HotCRM read's CPU #24535 因同函数串行而等待;protocol.ts 7 天 67 次提交。
- ③ 防 AI 犯错:A 让一个模块能被整篇读完,结构上更难改错;D 让每次改动都在部分视野下进行。
- ④ 创业阶段不扩散:A 不新增声明面与门禁;B 新增永久门禁(默认否);C 最省但不还债。
Prior rulings read: "split" "rest-server.ts" "large file" "decompose" → [maintainer] refactor(rest): splitpackages/rest/src/rest-server.ts(13,701 lines, 70 commits in 30 days) along itsregister*Endpointsseams, one move-only slice per PR,/metaafter #22877 stage 2 #23050 (maintainer, move-only slices), ADR-0076: remaining objectql/protocol layering work (cross-repo window) #2462 (ADR-0076 layering, closed); ADR none on file size; thread: none
自检:只看①选 A;②③④ 是否翻转:否(④ 只调时序:先 protocol.ts,其余随车道余量分期)。
推荐:A。 回退:C(若车道余量不足以支撑分片计划)。置信缺口: protocol.ts 内部的接缝还没有像 #23050 那样逐段实测(哪些方法簇可以按职责独立搬出、彼此的私有依赖有多深);规模是 rest-server 起点的 2.4 倍,分片数和周期是估计,不是实测。
裁后执行
- A: this seat files one
[maintainer]card per file over 10,000 lines into its owning lane, quoting the ruling.- Each card starts with [maintainer] refactor(rest): split
packages/rest/src/rest-server.ts(13,701 lines, 70 commits in 30 days) along itsregister*Endpointsseams, one move-only slice per PR,/metaafter #22877 stage 2 #23050's measurement: the file's sections, the lines each holds, the lines each gained in the last 30 days, and the scripts, anchors and docs that read the file by path. - The first slice of
protocol.tsfollows PR perf(core,metadata-protocol): an authenticated data request pays 6 warm serial rounds instead of 8, and 3 with the landed opt-ins #24533's merge, and rest: every GET /api/v1/data/OBJECT still runs a full zod safeParse of the object's definition (the API-exposure gate's by-name read is decorated with diagnostics) — 11.8% of a HotCRM read's CPU #24535 is dispatched before it. - Each slice is move-only, migrates its gates and doc anchors in the same PR, and is serial with the file's in-flight PRs.
- Each card starts with [maintainer] refactor(rest): split
- B: as A, plus one
domain:skills/toolingcard for the line-ceiling gate, quoting the maintainer's naming of it. - C: a line in each lane's seat charter: a card held by a same-file serial wait moves its seam out first, as its own move-only PR.
- D: this card closes
not_planned. Reopening it is free.
Generated by Claude Code
Activity
objectstack-fleet commentedon Oct 19, 2026
Ruled: A · recorded by
domain:cliseat 1 (seat post #6024) ·zhuangjianguo· sessionsession_01SYEJqDL3NXxrJRBVqaMu9T· 2026-10-19T14:49ZThe maintainer's words, verbatim, in this session's chat: 「24586 同意A,是否建议这波分拆等 v18 发布之后再处理。」
On the timing question, the seat recommends: yes, start the splits after 18.0.0 GA is published. Read in this act:
.changeset/pre.jsononorigin/mainreads"mode": "exit"(chore(release): exit Changesets pre mode (next), so the next Version Packages PR versions 18.0.0 GA #24528 closed at 14:44Z today), so the next Version Packages PR versions 18.0.0 GA.@objectstack/specdist-tags readlatest17.7.0 andnext18.0.0-next.5.target:v18cards. Five aredomain:engine(Seed replay rewrites unchanged rows on every boot:seedValueEqualscompares time and ISO-datetime strings literally, so09:15vs09:15:00and…Zvs….000Zbumpupdated_at(5 showcase rows per restart) #24562, Active metadata save stores an object without its package namespace prefix that publish-drafts then refuses (NAMESPACE_PREFIX) #24547, platform-objects: the identity objects declare their default row scope (the literals the shipped sets copy today), and the registry refuses anextendcontributor that loosens a platform object's default (#24164 ruling E′) #24341,sys_business_unit_memberis unadjudicated inPLATFORM_OBJECT_TENANCY, so seed-replayed and system-written membership rows land organization-less #14570, and feat(spec,drivers,objectql,plugin-security):organization_idNOT NULL per cleared table; one predicate for Layer 0 and every driver; bothorWhereNullarms, the__global__sentinel and the #13491 ledger retire (ADR-0131 D1/D8/D9) — protocol 18 #15212 shared with spec), the lane that ownsprotocol.ts,objectql/engine.tsandsql-driver.ts.protocol.tsin the last 7 days.git blamein code that has not moved.After the restart:
[maintainer]card per file over 10,000 lines into its owning lane:protocol.ts,objectql/engine.tsandsql-driver.tstodomain:engine,conversions/registry.tstodomain:spec, andservice-automation/engine.tstodomain:services. Each card quotes this ruling.packages/rest/src/rest-server.ts(13,701 lines, 70 commits in 30 days) along itsregister*Endpointsseams, one move-only slice per PR,/metaafter #22877 stage 2 #23050's shape: sections, lines, lines gained in 30 days, and the scripts, anchors and docs that read the file by path.protocol.tsgoes first.Restart-when:
npm view @objectstack/spec dist-tags.latestprints 18.0.0 or a later 18.x versionIn the stroke after this comment,
needs-user-decisionbecomespm:on-hold. If the maintainer prefers to start before GA, a word here reopens it.Generated by Claude Code
objectstack-fleet commentedon Oct 20, 2026
Hold released: the restart condition holds. Triage seat ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-10-20T13:15Z6270097840):npm view @objectstack/spec dist-tags.latestprints 18.0.0 or a later 18.x version.latest=18.0.0, and the tag@objectstack/spec@18.0.0exists (08420c33). 18.0.0 was published by Release run 39421537775.pm:on-hold→pm:queue,domain:cli(the seat whose ruling record holds the "After the restart" act), p2.domain:cliseat 1 files one card per file over 10,000 lines, into its owning lane:protocol.ts,objectql/engine.tsandsql-driver.ts→domain:engine;conversions/registry.ts→domain:spec;service-automation/engine.ts→domain:services.Each card starts with a fresh seam measurement. The slices are move-only and serial with the file's in-flight PRs, and
protocol.tsgoes first.Generated by Claude Code