Skip to content

spec: the documented emergency rollback OS_EAGER_SCHEMAS=1 crashes the published @objectstack/spec/api and /data entries at import, due to a filter.zod → strict-object → suggestions.zod → field.zod cycle #19930

Description

@objectstack-fleet

Ruled: 5814528240 · letter A (allowlisted pointers publish on existence) · 2026-09-24T12:59Z

Filed by the domain:spec seat 5 (session_01Sfe5YjBLwB9J3y8fvm2xq1, seat post #19357) from the out-of-scope findings of the #19101 dev (report 5805027961, class a). ⛔ Filed unassigned and unlabelled: routing and grading are triage's. ⛔ Not a claim.

The defect

content/docs/deployment/environment-variables.mdx:30 documents OS_EAGER_SCHEMAS=1 as "an emergency rollback of the lazy-schema memory optimization". With it set, the published @objectstack/spec/api and @objectstack/spec/data entries throw at import.

Measured by the #19101 dev at fdeeea0cc9, after an OS_SKIP_DTS=1 spec build:

  • OS_EAGER_SCHEMAS=1 node -e "require('./dist/data/index.js')" exits 1 with TypeError: Cannot read properties of undefined (reading 'optional') at dist/data/index.js:1989 (relatedListFilter: FilterConditionSchema.optional()).
  • The same happens for dist/api/index.js and both .mjs files.
  • Controls: the same entries without the flag exit 0. dist/index, kernel, ui, automation, system and shared exit 0 under the flag.
  • From source, tsx scripts/build-openapi.ts under the flag fails with ReferenceError: Cannot access 'FilterConditionSchema' before initialization (src/data/field.zod.ts:1529, reached through the eager lazySchema at :1000).

The cycle

This seat read the imports on origin/main fdeeea0cc9; the load-order trace is the dev's:

  • packages/spec/src/data/filter.zod.ts:4 imports closedObject from ../shared/strict-object;
  • packages/spec/src/shared/strict-object.ts:83 imports from ./suggestions.zod;
  • packages/spec/src/shared/suggestions.zod.ts:4 imports the FieldType value from ../data/field.zod;
  • the eager FieldSchema factory in field.zod.ts then reads FilterConditionSchema while filter.zod.ts is still in its import phase.

In lazy mode, the default, the factory runs later, so nothing crashes. Only the documented rollback path breaks, and it breaks for any host whose first spec import is /api or /data.

Why it matters

A rollback switch that crashes the process is worse than having none. The one time an operator reaches for it is when the lazy path is itself suspected. It also blocks #19101's option of generating openapi.json eagerly.

Seam: spec:lazySchema eager branch (src/shared/lazy-schema.ts:22) → runtime: any host whose first spec import is @objectstack/spec/api or /data.

Dedupe words: OS_EAGER_SCHEMAS crash import · FilterConditionSchema before initialization · emergency rollback eager schemas broken · suggestions.zod field.zod cycle · relatedListFilter undefined optional

Activity

  1. objectstack-fleet commented on Sep 24, 2026

    @objectstack-fleet
    ContributorAuthor

    定级 pm:queue · priority:p2 · bug · domain:spec —— 文档写的应急回退开关 OS_EAGER_SCHEMAS=1 一打开,spec 的 /api、/data 入口在导入时就崩

    Path: platform-core

    Triage: lands in packages/spec/src (an import cycle: data/filter.zod.ts:4 → shared/strict-object.ts:83 → shared/suggestions.zod.ts:4 → data/field.zod.ts, whose eager FieldSchema factory reads FilterConditionSchema before it is initialized) ⇒ domain:spec; rationale: the documented emergency rollback (environment-variables.mdx:30) crashes any host whose first spec import is @objectstack/spec/api or /data, which is exactly the moment an operator reaches for it; the default lazy path is unaffected ⇒ p2.

    分诊席(session_01Tw7jnJinGHvoGSi8aFkhPJ),2026-09-24T00:21Z。本席读完了卡面(本卡尚无评论)。

    本席的读数(origin/main fdeeea0cc9)

    • content/docs/deployment/environment-variables.mdx:30:OS_EAGER_SCHEMAS 「an emergency rollback of the lazy-schema memory optimization」。
    • 导入链逐行复读:filter.zod.ts:4 从 ../shared/strict-object 导入 closedObject;strict-object.ts:83 从 ./suggestions.zod 导入;suggestions.zod.ts:4 从 ../data/field.zod 导入 FieldType。
    • ⇒ 环是真的。导入时崩溃是开发实测的(带开关 exit 1、不带开关 exit 0,其余入口作对照),本席没有重跑。

    判定


    Generated by Claude Code

  2. objectstack-fleet commented on Sep 24, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 1
    Session: session_019c3Hi6ZMU1p6m6aA6Bz45d
    Branch: claude/issue-19930-eager-import-cycle
    Worktree: objectstack-issue-19930
    Domain: domain:spec
    Seat: domain:spec#4
    File surface: packages/spec/src/shared/suggestions.zod.ts (its value import of FieldType from ../data/field.zod, the edge that closes the cycle) and, only if the cycle cannot be broken there, the module suggestFieldType moves to · a pin that imports @objectstack/spec /api and /data (dist and source) under OS_EAGER_SCHEMAS=1 and requires success · .changeset/. ⛔ Not packages/spec/src/shared/lazy-schema.ts (held by #19101, seat 5). ⛔ Not the scale region of packages/spec/src/data/field.zod.ts (held by PR #19909) (stop on breach; explain in the report)
    Container & model: M, mode:subagent, model: opus — floor sonnet · default opus · ceiling fable; dispatch-gates --tier at fdeeea0cc9: no path-derived mandate, clause-② SUSPECT hint on packages/spec/src/**
    Clause-②: no
    Thread-read: 5805174208
    Serial constraints cleared: open-PR census 2026-09-24T00:55Z, 36 PRs: none touches shared/suggestions.zod.ts, shared/strict-object.ts or data/filter.zod.ts; PR #19909 touches data/field.zod.ts (the scale region only); PR #19882 touches data/filter-comparand-shape.ts (not in this surface). #19101 (seat 5, in flight) holds shared/lazy-schema.ts, which this card does not touch; the semantic coupling (its eager branch is the one that trips this cycle) goes into the dispatch as a named fence.

    Claimed 2026-09-24T00:55Z by seat post #18917. The dispatched os-dev runs under this same session id and writes ⛔ no second claim.


    Generated by Claude Code

  3. objectstack-fleet commented on Sep 24, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report
    {
    "issue": 19930,
    "status": "needs_decision",
    "branch": "claude/issue-19930-eager-import-cycle",
    "pr": "#19943",
    "session": "session_019c3Hi6ZMU1p6m6aA6Bz45d",
    "premise_still_valid": true,
    "summary": "Measured and fixed. The cycle is data/filter.zod -> shared/strict-object -> shared/suggestions.zod -> data/field.zod, with the throw at field.zod:1529 through the eager lazySchema at :1000. At fdeeea0, 22 of 1039 source modules threw as an eager first import, under two signatures (FilterConditionSchema x20, FIELD_KEY_GUIDANCE x2), and both run through the same suggestions.zod -> field.zod edge. suggestFieldType and its alias table moved to shared/field-type-suggestion.ts, so suggestions.zod.ts now imports no schema module. Public names and API surface are unchanged. After the fix, 0 of 1040 modules throw, all 17 source entries and all 44 dist JS files import clean under the flag, and a pin covers every exports subpath. STATUS needs_decision, not done: cutting the edge changes the textual closure gen:skill-refs walks, so check:skill-refs (and therefore check:generated) is red. The generator refuses on the objectstack-i18n TRANSITIVE_ALLOWLIST, and resolving that also rewrites 3-4 skills//references/_index.md files, a Tier H surface. See open_questions; no speculative code was written for it. Fences held: lazy-schema.ts and field.zod.ts are untouched. The assignee was not written (os-litant, unchanged). line_budget: +332 / -102 (PR 19943 readback). files_changed (11): .changeset/19930-eager-first-import-cycle.md; packages/spec/export-origins/root.json; packages/spec/export-origins/shared.json; packages/spec/src/data/type-compat.test.ts; packages/spec/src/eager-entry-import.test.ts; packages/spec/src/index.ts; packages/spec/src/shared/error-map.zod.ts; packages/spec/src/shared/field-type-suggestion.ts; packages/spec/src/shared/index.ts; packages/spec/src/shared/suggestions.test.ts; packages/spec/src/shared/suggestions.zod.ts. deviations: (1) The pin covers SOURCE entries only, not dist, although the claim said dist and source. Reason: @objectstack/spec#test depends on ^build, not its own build, so dist is not guaranteed at test time, and check:test-source-alias wants unit verdicts on source. Dist was measured once by hand instead (tests field). (2) The file surface grew past the claim by the move's required consequences: the 3 importers (src/index.ts, shared/index.ts, shared/error-map.zod.ts), 2 test imports (type-compat.test.ts, suggestions.test.ts) and the regenerated export-origins shards. (3) The pre-fix dist was not re-measured; the card's reading stands. (4) Commit trailers use the model-free pair AGENTS.md prescribes, not the model-named Co-Authored-By the harness reminder asked for (AGENTS.md takes precedence; reported, not repaired). (5) Mechanism assumption 3 is partly falsified: scripts/lib/refinement-projection.ts and src/shared/refinement-projection.ts exit 0 as an eager first import at fdeeea0 in both probe shapes, and no independent second cycle exists. Contract review: not owed. No accept/reject result changes, and check:api-surface reports the public surface and factory signatures unchanged. If the skill-refs resolution lands in this PR it becomes Tier H and needs a maintainer-speed-read section and the maintainer's word.",
    "tests": "All at HEAD fd9f55a unless noted. REPRO (fdeeea0, source): OS_EAGER_SCHEMAS=1 tsx src/data/index.ts and src/api/index.ts exit 1, ReferenceError: Cannot access 'FilterConditionSchema' before initialization (field.zod.ts:1529 via lazy-schema.ts:23 from field.zod.ts:1000). Unflagged controls exit 0. The Module._load trace names the chain above. SWEEP (each source module as first import of a fresh graph, tsx/cjs with the require cache cleared per file): before 1039 modules, 22 threw; after 1040 modules, 0 threw. Per-entry subprocess probes after the fix: 17/17 entries exit 0 both as the run entry and via an ESM dynamic-import probe. DIST (build of fd9f55a, .build-input-hash 60dad45334903e69): 44/44 exports-map JS files (main + browser, .js + .mjs) exit 0 with the flag and 44/44 without it. On the 8 /data and /api files the probe export reads object under the flag and function without it. PIN src/eager-entry-import.test.ts at 1b2a2bb: Test Files 1 passed, Tests 20 passed (1 mapping + 17 entries + 2 unflagged controls). ABLATION A (commit-first, scripts/ablation-replace.mjs wrap mode with a trap restore): re-closing the edge in suggestions.zod.ts (blob 4ed9b1d8 -> bb5a0192, anchor 1 -> 0) gave Tests 2 failed | 18 passed, the failures being ./data and ./api with ReferenceError: Cannot access 'FilterConditionSchema' before initialization. Restore proven: blob == HEAD 4ed9b1d8, git diff HEAD empty. The first attempt was a no-op the tool refused (the anchor was contained in the replacement, count 1 -> 1); nothing ran on it, and it was re-anchored. ABLATION B (env hand-off broken in the test, blob 9e2b7b0f -> 91e894be): Tests 2 failed | 18 passed on ./data and ./api with the message "was not built at import - the flag never took effect: expected function to be object". Restore proven: blob == HEAD 9e2b7b0f. The expected direction (red) was stated before both runs, and both were observed red. SUITES: vitest run --project local --maxWorkers=2 gave Test Files 527 passed | 1 skipped (528), Tests 15521 passed | 1 skipped | 1 todo, VERDICT command-exit 0. My first launch used a bare -- (maxWorkers dropped); I killed it by recorded PIDs after about 10 s, read no verdict from it, and reran with the exec spelling. vitest run --project repo --maxWorkers=2 gave Test Files 35 passed, Tests 602 passed, exit 0. pnpm --filter @objectstack/spec run typecheck exited 0 (tsc --noEmit + check:scripts-typecheck + check:test-typecheck OK: 53 files / 255 errors / 142 pinned signatures held). tsc -p tsconfig.test.json --listFilesOnly lists all 4 touched test/source files. BUILDS (under os-verify-lock): pnpm --filter @objectstack/spec build at 7d8a380 exited 0 (4m06s held). turbo build of spec, formula, lint and objectql at fd9f55a exited 0 (14/14 tasks, spec a cache miss). git status was clean after both. GATES: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derived 86 families on the actual diff, and every one was run at fd9f55a with its exit landed on disk. --ran reconciliation: "86 derived famil(ies) accounted for - 84 run, 2 NOT-MEASURED", exit 0. Of those, 82 exit 0 and 2 exit 1: check:skill-refs and check:generated, the named gap (check:generated is 14/15 current and only skill-refs is stale). NOT MEASURED: check:dual-build-cjs-loads and check:type-check-debt, reason: both need every workspace package built (exit 3 PREREQUISITE NOT MET twice; CI builds all). check:api-surface, check:dual-source-exports, check:entry-nameability, check:exported-any, check:lean-entry-closure and check:doc-formula-expressions first refused with exit 3 on stale or absent builds, and after the turbo build all exit 0. Key verdict lines: "public API surface + factory signatures unchanged", "export-origins/ is current: 5257 exports across 17 entry points", "no new dual-source exports", "check-nul-bytes: OK", "check-test-source-alias OK", "check:cross-package-test-inputs OK", "This diff introduces no major bump". NOT MEASURED: dist before the fix (cited from the card); the Tier H skills regeneration (blocked on the decision).",
    "mcp_calls": "0 - no MCP GitHub tool was called; all reads were plain REST GETs.",
    "api_writes": "2 - both via the fleet-write relay (scripts/pm, landing as objectstack-fleet[bot]): (1) pr_create through with-fleet.sh --via dispatch: POST /repos/objectstack-ai/objectstack/dispatches (seat) -> POST /repos/objectstack-ai/objectstack/pulls draft=true (relay run 35945407966, success) = PR 19943; (2) this os-dev-report comment through post-stamped.mjs: POST /repos//issues/19930/comments. git push (the branch plus 5 commits) is not a REST write. No label writes: the dispatch named none, and a changeset exists, so skip-changeset does not apply.",
    "open_questions": [
    {
    "question": "The fix removes the only import edge through which shared/strict-object.ts reached data/field.zod.ts and its subtree, and any fix inside the two fences must remove it. gen:skill-refs builds skills/
    /references/_index.md from that textual closure, so: (a) it REFUSES on objectstack-i18n, whose TRANSITIVE_ALLOWLIST row (packages/spec/scripts/lib/skill-map-guards.ts) keeps shared/identifiers.zod.ts and data/field.zod.ts. Both were reachable only through the removed edge, and the row's own docblock says no import edge expresses them; they are kept because the i18n SKILL.md teaches them (snake_case "Critical:" rule at lines 186-191, option-value keys at line 162). (b) Once (a) is resolved, three published indexes shrink: objectstack-query from 7 to 1 transitive pointers, objectstack-api from 18 to 13, objectstack-automation from 11 to 7. They lose field, field-value, expression, identifiers, value-domain, metadata-protection and, for automation, filter; grep over those skills' authored markdown names none of them. All four _index.md files are under skills/** (Tier H), and the regeneration cannot be split into a separate PR, because this PR's required TypeScript Type Check job stays red without it. How should objectstack-i18n keep its two pointers?",
    "options": [
    "A - Declared pointers: an allowlisted pointer publishes when the file exists and ships (*.zod.ts), whether or not the closure reaches it. The guard keeps its typo/dead-file half, and its reachability half becomes an existence check for that list. i18n _index.md stays byte-identical; query/api/automation lose the helper-edge pointers. Cost: skill-map-guards.ts plus its test, build-skill-references.ts, and 3 regenerated _index.md files (Tier H).",
    "B - Promote both files to objectstack-i18n core entries, add a SHARED_CORE_SCHEMAS row for data/field.zod.ts (owned by objectstack-data), and delete the i18n allowlist row. Cost: the i18n index moves both pointers into "Core schemas", which claims them as i18n-owned surface; one more sharing row; 4 _index.md files change (Tier H).",
    "C - Drop the i18n allowlist row. The i18n index loses both pointers. Cost: the SKILL.md's snake_case rule and option-value keying lose the schema they point at; 4 _index.md files change (Tier H).",
    "D (rejected, listed for completeness) - Keep a type-only import of field.zod in suggestions.zod.ts so the textual closure is unchanged. This is a workaround: an edge kept only to steer a generator, and it preserves pointers the guard file itself calls helper implementation rather than authorable shape."
    ],
    "recommendation": "A. Real business need (measured): the i18n SKILL.md teaches exactly these two schemas, and the three shrinking skills teach none of the lost ones, so A keeps every taught pointer and drops only untaught ones. Long-term soundness: the allowlist docblock already says these pointers are not import edges, and A states that intent explicitly instead of riding the accidental cycle edge that crashed the rollback; B misstates ownership, C loses content. Preventing AI mistakes: agents on query/api/automation stop being sent to field/value-domain internals they cannot author through those surfaces, and i18n agents keep the snake_case and option-key sources, with nothing dropped silently. Startup scope: no new gate, the smallest published change (i18n byte-identical), one guard narrowed from reachability to existence. Whichever option is chosen, the landing PR is Tier H, and the pointer-set change in query/api/automation is the maintainer's to accept."
    }
    ],
    "out_of_scope_findings": [
    "noted, not filed - carrier: none (承接者:无) - comments still describing the removed suggestions.zod -> field.zod edge in the present tense: shared/strict-object.ts (the declarationStore / strictObjectError / PrimableErrorMap / closedObjectConstructor docblocks), shared/strict-object.test.ts (eager-construction block), data/field-value.zod.ts:35, data/field.zod.ts:184 (fenced), scripts/lib/skill-map-guards.ts:200. The defensive code under them stays valid. Not class a/b/c; recorded in the PR Acceptance notes.",
    "noted, not filed - carrier: none (承接者:无) - the #19101 dev report listed scripts/lib/refinement-projection and src/shared/refinement-projection as crashing first imports under the flag. They do not reproduce at fdeeea0: exit 0 in both probe shapes. Recorded in the PR Acceptance notes."
    ]
    }


    Generated by Claude Code

  4. objectstack-fleet commented on Sep 24, 2026

    @objectstack-fleet
    ContributorAuthor

    Decision box needs-user-decision · PR #19943 is built and green except one generated-index gate — how should the i18n skill keep its two schema pointers once the import cycle is cut?

    domain:spec seat 4 (session_019c3Hi6ZMU1p6m6aA6Bz45d), 2026-09-24T02:06Z. Source: the dev's needs_decision report (the os-dev-report comment on this card), re-checked by this seat on GitHub and on origin/main fdeeea0cc9.

    Verified by the seat:

    • PR fix(spec): break the eager import cycle that crashed /api and /data under OS_EAGER_SCHEMAS=1 #19943 at head fd9f55aadf: draft, Fixes #19930, 11 files, +332 / −102.
    • The one red check is Type Check · source gates. ci-failure.mjs --pr 19943 names the failed step: Check generated skill references are in sync with the spec (pnpm --filter @objectstack/spec check:skill-refs).
    • The allowlist row is real: packages/spec/scripts/lib/skill-map-guards.ts:247-256 (TRANSITIVE_ALLOWLIST['objectstack-i18n'] = shared/identifiers.zod.ts, data/field.zod.ts, each with its SKILL.md reason in the comment).

    维护者速读

    改了什么:文档里写的应急回退开关 OS_EAGER_SCHEMAS=1 一打开,@objectstack/spec 的 /api、/data 入口在导入时就崩。修法是把 suggestFieldType 搬到一个不再反向引用字段 schema 的小文件里,断开那条环。实测修后 1040 个模块、17 个入口、44 个发布产物在开关下全部能导入。公开导出不变(check:api-surface 读作不变)。

    卡在哪:技能说明书(skills/*/references/_index.md)的「相关 schema 指针」是按源码导入链自动生成的。断开这条环,导入链就变了。结果有两处:

    1. objectstack-i18n 技能的两个指针(标识符命名规则、下拉选项值)原本只靠这条环才够得着。生成器现在直接拒绝生成,门禁就红了。
    2. 另外三个技能(query / api / automation)会少掉几个本来就没教过的指针,比如 field、value-domain 这类内部文件。

    不管选哪条,这个 PR 都会改到 skills/**,按规矩是 Tier H,要您点一次合并。

    要您定的一件事:i18n 技能那两个指针怎么保住?

    选项 做什么 客户(用 AI 写应用的人)能感知的后果
    A 允许清单里的指针改成「文件存在且发布」即保留,不再要求导入链够得着 i18n 说明书一字不变;query / api / automation 少掉几个从没教过的内部指针
    B 把这两个文件升为 i18n 的「核心 schema」,并给 field.zod.ts 加一条共享归属 i18n 说明书把字段 schema 列成自己的核心,归属说错;四个说明书都要变
    C 删掉 i18n 的允许清单行 i18n 说明书丢掉两个指针,「必须 snake_case」「选项按值键」这两条规则失去出处
    D(不荐) 在 suggestions.zod.ts 留一条只为骗生成器的类型导入 什么都不变,但留了一条假边;这正是本卡要拆的那种隐患

    业务含义:A = 「清单上写明要的,就照写明的给」;B = 「为了保指针,把别人的东西记到自己名下」;C = 「指针跟着导入链走,链断了就不给」。

    四轴(业务立场)

    • ① 项目长远合理性:这两个指针本来就不是导入关系,是说明书教的内容。允许清单的注释也这么写。A 把这层意思直接写成规则,不再靠一条偶然的环。那条环正是让回退开关崩溃的根因。B 把归属写错,C 丢内容。
    • ② 实际业务拉动:实测 i18n 的 SKILL.md 正在教这两个 schema(snake_case 的「Critical:」规则、按选项值作键)。三个要缩小的技能,其正文一个都没提被删的那些指针。
    • ③ 防 AI 犯错:A 下,i18n 会话里的 agent 继续被指到命名规则与选项声明的出处。query / api / automation 的 agent 不再被指去它们根本写不到的内部文件。什么都不是静默丢的,变化都在生成的索引里、可 diff。
    • ④ 创业阶段不扩散:A 不新增门禁,只把一个已有守卫的「可达」判据收成「存在」判据,且只对清单内条目;已发布说明书的改动最小。

    os-decision-facets

    • ① 项目长远合理性:A 缩小特例——清单条目按声明保留,不再依赖一条会让回退开关崩溃的偶然导入边;B 扩大共享归属,C 丢已教内容。
    • ② 实际业务拉动:i18n SKILL.md 实测在教这两个 schema;三个缩小的技能正文零提及被删指针 ⇒ 缩小无人撞上。
    • ③ 防 AI 犯错:A 让 agent 被指到它真要用的规则出处,并停止把 agent 引向写不到的内部文件;所有变化落在生成物、可 diff,无静默。
    • ④ 创业阶段不扩散:A 零新增门禁,已发布说明书改动最小(i18n 逐字节不变)。

    Prior rulings read: skill-refs,transitive,allowlist,closure → 5 hits; ADR-0069 D5, ADR-0076 D2, ADR-0082 Decision §1, ADR-0090 D12; thread: none

    推荐:A。只看①选 A;②③④ 是否翻转:否(②③ 同向加强,④ 在 A 上最小)。回退项:C(内容损失可见、可补)。⛔ 不荐 D(为生成器保留假边,就是本卡要拆的隐患)。
    置信缺口:三个技能缩小后的指针集合取自 dev 的实测,本席未重跑生成器;「正文零提及」是 dev 对这三个技能 markdown 的 grep,本席未复核。

    Governing text: packages/spec/scripts/lib/skill-map-guards.ts:233-257 (TRANSITIVE_ALLOWLIST and its per-row reasons); AGENTS.md Prime Directive #14 (a skills/** diff lands only by a human merge); docs/NORTH-STAR.md 优先级 4 (a wrong sentence in a skill is a product defect).

    After the ruling

    • A / B / C ⇒ back to pm:queue. The next claim continues on branch claude/issue-19930-eager-import-cycle / PR fix(spec): break the eager import cycle that crashed /api and /data under OS_EAGER_SCHEMAS=1 #19943. It implements the chosen letter in packages/spec/scripts/lib/skill-map-guards.ts (+ test) and the skill-references builder, and regenerates skills/*/references/_index.md with the generator (⛔ never by hand). An at-tier contract review is still owed (the diff touches non-test packages/spec/src/**). The PR then waits Tier H: 维护者速读 on the card, reviews requested from os-zhuang / hotlong.
    • Claim surface amendment, recorded here: the move also touched the three importers (src/index.ts, shared/index.ts, shared/error-map.zod.ts), two test imports and the regenerated export-origins shards. These are consequences the move requires, inside packages/spec.

    Related

    PR #19943 · #19101 (seat 5, lazy-schema.ts, not touched by #19943) · PR #19909 (field.zod.ts, not touched by #19943).


    Generated by Claude Code

  5. objectstack-fleet commented on Sep 24, 2026

    @objectstack-fleet
    ContributorAuthor

    Release: session_019c3Hi6ZMU1p6m6aA6Bz45d (domain:spec#4) · cause: the dispatched round returned needs_decision — the fix is built (PR #19943), but landing it needs a maintainer letter on how the i18n skill keeps two schema pointers, and it lands Tier H (skills/**) either way · destination: decision box (needs-user-decision, analysis 5806180472); the seat is in wind-down on the maintainer's 「当前任务处理完,合并后就下班」 · 2026-09-24T02:07Z


    Generated by Claude Code

  6. 1 remaining item

  7. objectstack-fleet commented on Sep 24, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 1 (re-seated shift) — continuation after ruling A
    Session: session_019c3Hi6ZMU1p6m6aA6Bz45d
    Branch: claude/issue-19930-eager-import-cycle
    Worktree: objectstack-issue-19930
    Domain: domain:spec
    Seat: domain:spec#4
    File surface: PR #19943's existing 11 files, plus packages/spec/scripts/lib/skill-map-guards.ts and its test, the skill-references builder under packages/spec/scripts/, and the regenerated skills/*/references/_index.md (via gen:skill-refs only). Fences carried forward: ⛔ packages/spec/src/shared/lazy-schema.ts, ⛔ packages/spec/src/data/field.zod.ts (stop on breach; explain in the report)
    Container & model: M, mode:subagent, model: fable (MANDATORY: dispatch-gates --tier derives it from the skills/** surface)
    Clause-②: no
    Thread-read: 5814528240
    Serial constraints cleared: open-PR census 2026-09-24T18:04Z, 15 PRs: only PR #19943 itself touches suggestions.zod.ts. No open PR edits skill-map-guards.ts or any skills/*/references/_index.md.

    This continues the ruled card: batch #220 item 2, letter A (5814528240; the maintainer's 「批 #220 同意」). It is ⛔ not a restart. The PR lands Tier H (skills/**) on the maintainer's merge after an at-tier contract review.


    Generated by Claude Code

  8. objectstack-fleet commented on Sep 24, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report
    {
    "issue": 19930,
    "status": "done",
    "branch": "claude/issue-19930-eager-import-cycle",
    "pr": "#19943",
    "session": "session_019c3Hi6ZMU1p6m6aA6Bz45d",
    "head_sha": "247d0611fc164e95aedc91b213bd0642515b5760",
    "premise_still_valid": true,
    "summary": "Continuation after ruling A (5814528240) on PR #19943, no restart: two commits added on top of fd9f55a. (1) 1cda062 merges origin/main 61609ed via scripts/pm/os-regen-merge.sh (326 files; on this surface main brought packages/spec/src/shared/lazy-schema.ts +51 and skills/objectstack-ui/SKILL.md; step 2 kept the branch-only export-origins/root.json and shared.json; after pnpm install --frozen-lockfile and a spec rebuild check:generated read all 15 artifacts current, so the step-4 regeneration commit was empty). The cycle fix holds on the merged tree: the pin passes 20/20 and all 44 published JS files import first under OS_EAGER_SCHEMAS=1 and without it (88 runs, 0 failures). (2) 247d061 implements A: in packages/spec/scripts/lib/skill-map-guards.ts a TRANSITIVE_ALLOWLIST row is a declared pointer that publishes when the file exists under packages/spec/src and ships (.zod.ts), for packages with a list only; checkTransitiveAllowlist(map, allowlist, existsInSpecSrc) keeps the typo and dead-file halves, gains a does-not-ship half, loses its reachability half; publishedPointers(coreFiles, closure, allowed, existsInSpecSrc) carries the selection (no list = closure unchanged; a list = core + declared rows that ship, nothing the closure reached on its own). build-skill-references.ts hands the guard the disk and calls publishedPointers; the up-front closure pass is gone. Tests: the three the dispatch asked for plus does-not-ship refusal, closure-only file stays out, no-list unchanged, sorted output, and the wiring pin (36 pass). gen:skill-refs regenerated every _index.md: objectstack-i18n byte-identical to origin/main (git diff --exit-code exit 0); query 7 to 1, api 18 to 13, automation 11 to 7 transitive pointers, losing only helper-edge pointers their authored markdown never names (pointer_diff below). check:skill-refs and check:generated are green; 97 derived gate families reconciled (95 run exit 0, 2 NOT-MEASURED on their own prerequisite). Fences held: lazy-schema.ts and field.zod.ts untouched by the branch (the merge brought main's own lazy-schema.ts change). The newest Claim: (5819463080) names this branch; the assignee was not written. The PR body was not written (dispatch fence): the exact replacement text is in pr_body_replacement, with the 维护者速读 section. Landing: Tier H (skills/**) on the maintainer's merge after the seat's at-tier contract review.",
    "tests": "All at head 247d061 unless noted. GUARD TESTS: pnpm --filter @objectstack/spec exec vitest run --project local --maxWorkers=2 scripts/skill-map-guards.test.ts: Test Files 1 passed, Tests 36 passed (unlocked, 342 ms). ABLATION (commit-first, node scripts/ablation-replace.mjs wrap mode with its own restore; expected direction stated before the run: RED): anchor "if (shipsFromSpec(rel, exists)) kept.add(rel);" replaced by the same plus "&& closure.includes(rel)" in lib/skill-map-guards.ts, blob 99c90e3e → 1cca6cf1, anchor 1 → 0. Observed: the guard test went red on exactly "an allowlisted, existing, unreachable pointer publishes" and "returns the rows sorted" (Tests 2 failed | 34 passed) and check:skill-refs exited 1 with "skills/objectstack-i18n/references/_index.md (out of date)". Restore proven by the tool: blob after restore == HEAD 99c90e3e, git diff HEAD empty, git status clean. No dist is involved (the generator and the test run from source via tsx/vitest), so no dist preflight applies. REGEN: pnpm --filter @objectstack/spec gen:skill-refs exit 0 ("Generated 9 files"); git status names only api/automation/query _index.md; git diff --exit-code origin/main -- skills/objectstack-i18n/references/_index.md exit 0. CYCLE RE-MEASURE ON THE MERGED TREE: src/eager-entry-import.test.ts under the lock: Test Files 1 passed, Tests 20 passed (VERDICT command-exit 0, held 14 s). Dist probe (scratchpad script, 44 exports-map JS files x {flag, no flag}): SUMMARY 44 published JS files, 88 runs ok, 0 runs failed; the 8 /data and /api files read probe=object under the flag and probe=function without it; dist .build-input-hash 61e345f25156fe1e. BUILDS (locked): pnpm --filter @objectstack/spec build VERDICT command-exit 0 (held 143 s); turbo run build --filter lint/objectql/formula --concurrency=2: 14 successful, 14 total (1 cached). SUITES (locked, one hold of 20m38s): pnpm --filter @objectstack/spec run typecheck exit 0 (tsc --noEmit + check:scripts-typecheck + check:test-typecheck: 53 files / 255 errors / 142 pinned signatures held); vitest run --project local --maxWorkers=2: Test Files 533 passed (533), Tests 15675 passed | 2 todo; vitest run --project repo --maxWorkers=2: Test Files 31 passed, Tests 579 passed. The wrapper printed batch-last-exit 0 because the parts were separate statements; each part's exit code was landed in its own log (all 0). GATES: see gates. NOT MEASURED: the 1040-module whole-package eager sweep of round 1 was not re-run on the merged tree (the pin and the dist probe are the re-measure); check:dual-build-cjs-loads and check:type-check-debt (exit 3, PREREQUISITE NOT MET: every workspace package built; 66 have no dist/ here); CI conclusions (not waited for).",
    "gates": "node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack (no paths; change set from git off merge base 61609ed) derived 97 commands at 247d061. Every command was run sequentially (unlocked, check:
    class) with its exit code landed: 95 exit 0, 2 exit 3. Reconciliation node scripts/pm/dispatch-gates.mjs --ran ran.txt --repo objectstack-ai/objectstack exit 0: "97 derived famil(ies) accounted for - 95 run, 2 NOT-MEASURED (0 DERIVED from a recorded exit 3, 2 claimed)". Key verdict lines: check:skill-refs "9 generated files in sync with packages/spec"; check:generated "All 15 generated artifacts are up to date"; check:api-surface "public API surface + factory signatures unchanged"; check:export-origins "current: 5260 exports across 17 entry points"; check:doc-authoring "16302 customer-facing string(s) across 1046 spec sources clean"; check:skill-identifier-liveness OK (457 citations over 53 published files); check:nul-bytes OK (9424 text files); check:test-source-alias OK; check:cross-package-test-inputs OK; check:pm-governed-merges self-test 441 assertions; check:pm-dispatch-gates "1925 cases pass". NOT-MEASURED (claimed with reason): pnpm check:dual-build-cjs-loads (exit 3: "PREREQUISITE NOT MET - this gate reads built output, and some package has no dist/" listing 66 packages, "This is NOT a pass: nothing was measured"); pnpm check:type-check-debt (exit 3, needs every workspace package built). CI: in_progress at report time; not waited for per the role file.",
    "line_budget": "Whole branch vs origin/main: 17 files, +525 / -176. This round (247d061 vs the merge 1cda062): 6 files, +193 / -74 (build-skill-references.ts +9/-18 net 27 lines touched; lib/skill-map-guards.ts +118 lines touched; skill-map-guards.test.ts +107 lines touched; api _index.md -5; automation _index.md -4; query _index.md -6). skills/** readings: no SKILL.md touched; published catalog SKILL.md sum before=4402 after=4402 lines; changed files whole-file before/after: skills/objectstack-api/references/_index.md 51 → 46, skills/objectstack-automation/references/_index.md 46 → 42, skills/objectstack-query/references/_index.md 36 → 30; objectstack-i18n _index.md unchanged. The published skills/ catalog is outside check-skill-line-ratchet by its own header; token counts: no token gate defined.",
    "files_changed": "Whole branch (17): .changeset/19930-eager-first-import-cycle.md; packages/spec/export-origins/root.json; packages/spec/export-origins/shared.json; packages/spec/scripts/build-skill-references.ts; packages/spec/scripts/lib/skill-map-guards.ts; packages/spec/scripts/skill-map-guards.test.ts; packages/spec/src/data/type-compat.test.ts; packages/spec/src/eager-entry-import.test.ts; packages/spec/src/index.ts; packages/spec/src/shared/error-map.zod.ts; packages/spec/src/shared/field-type-suggestion.ts; packages/spec/src/shared/index.ts; packages/spec/src/shared/suggestions.test.ts; packages/spec/src/shared/suggestions.zod.ts; skills/objectstack-api/references/_index.md; skills/objectstack-automation/references/_index.md; skills/objectstack-query/references/_index.md. This round (6): the three packages/spec/scripts files and the three _index.md files. Commits added this round: 1cda062 (merge of origin/main 61609ed), 247d061 (ruling A + regeneration). Governed paths: skills/** (Tier H); no .claude/, docs/adr/, AGENTS.md or CLAUDE.md.",
    "pointer_diff": "objectstack-i18n: byte-identical to origin/main (2 core + 2 declared: shared/identifiers.zod.ts, data/field.zod.ts). objectstack-query 7 → 1: removed data/field-value.zod.ts, data/field.zod.ts, kernel/metadata-protection.zod.ts, shared/expression.zod.ts, shared/identifiers.zod.ts, shared/value-domain.zod.ts; kept shared/suggestions.zod.ts. objectstack-api 18 → 13: removed data/field-value.zod.ts, data/field.zod.ts, shared/expression.zod.ts, shared/identifiers.zod.ts, shared/value-domain.zod.ts. objectstack-automation 11 → 7: removed data/field-value.zod.ts, data/field.zod.ts, data/filter.zod.ts, shared/value-domain.zod.ts. objectstack-data, -ai, -ui, -platform, -formula: unchanged. Grep of each skill's authored markdown (every .md except references/index.md) by file stem: 0 hits for every removed pointer in its own skill (query's one filter.zod hit is its own core file, kept); by every exported identifier of each removed file, word-bounded: no schema-level name hits, only common-word exports colliding with prose (Field, Filter, FilterCondition, Expression, expression, cel, cron, P, Predicate), e.g. the api routing row "Filter, sort, paginate, or aggregate a request" and the automation heading "Filter tokens (config.filter)", a node-config slot whose shape lives in its core builtin-node-config / io-node-config pointers.",
    "deviations": [
    "Commit trailers use the model-free pair AGENTS.md prescribes (Claude-Session URL + Co-authored-by: Claude), not the model-named Co-Authored-By the harness reminder asked for; AGENTS.md takes precedence. Reported, not repaired.",
    "Lock discipline: builds, spec typecheck, both spec vitest projects and the eager pin ran under os-verify-lock (slot issue-19930); the single-file guard test (342 ms), gen:skill-refs, check:generated, the dist probe and the 97 derived gate commands ran unlocked as check:*-class work. One hold lasted 20m38s (typecheck + local + repo suites in one hold), a long holder by the wrapper's own threshold.",
    "The round-1 whole-package eager sweep (1040 modules) was not re-run on the merged tree; the merged-tree re-measure is the pin (17 entries under the flag, 2 unflagged controls) plus the dist probe (44 files x 2 modes).",
    "publishedPointers() lives in lib/skill-map-guards.ts (the claim's named file) rather than a new lib file; it is a selection helper, not a guard, placed beside the allowlist whose docblock it executes.",
    "The skill-map-guards.ts docblock (round-1 acceptance note "skill-map-guards.ts:200 present tense") was rewritten as part of implementing A, because the ruling changes what that docblock explains; the other present-tense sites listed in round 1 are untouched.",
    "os-regen-merge.sh step 4 produced no regeneration commit: check:generated read all 15 artifacts current after the merge and rebuild, so there was nothing to regenerate.",
    "No PR-body, label or MCP write, per the dispatch; the replacement PR body is in pr_body_replacement for the seat to write. The changeset from round 1 (patch, spec) is unchanged; scripts/ and skills/ publish nothing through npm."
    ],
    "mcp_calls": "0 - no MCP GitHub tool was called; all reads were plain REST GETs (issue, comments, PR).",
    "api_writes": "1 - this os-dev-report comment via node scripts/pm/post-stamped.mjs --comment=19930 (fleet-write relay, landing as objectstack-fleet[bot]): POST /repos//issues/19930/comments. git push x2 (1cda062, 247d061) is not a REST write. No label, PR-body or MCP writes.",
    "open_questions": [],
    "out_of_scope_findings": [
    "noted, not filed - carrier: none (承接者:无) - comments still describing the removed suggestions.zod → field.zod edge in the present tense: shared/strict-object.ts (declarationStore / strictObjectError / PrimableErrorMap / closedObjectConstructor docblocks), shared/strict-object.test.ts (eager-construction block), data/field-value.zod.ts:35, data/field.zod.ts:184 (fenced). The defensive code under them stays valid. Not class a/b/c; in the PR Acceptance notes. The scripts/lib/skill-map-guards.ts item from round 1 is resolved by this round.",
    "noted, not filed - carrier: none (承接者:无) - the two refinement-projection modules reported as crashing first imports do not reproduce at fdeeea0 (exit 0 in both probe shapes). In the PR Acceptance notes."
    ],
    "pr_body_replacement": "Fixes #19930\nClause-②: no\n\n## What was wrong\n\nOS_EAGER_SCHEMAS=1 is the documented emergency rollback of the lazy-schema memory optimization (content/docs/deployment/environment-variables.mdx:30). With it set, a process whose first spec import was @objectstack/spec/api or @objectstack/spec/data threw at import. The default lazy path was not affected.\n\n## Measured before changing anything (origin/main fdeeea0cc9)\n\n- Load chain, taken with a Module._load hook on the source entry: src/data/index.ts → data/query.zod.ts → data/filter.zod.ts → shared/strict-object.ts → shared/suggestions.zod.ts → data/field.zod.ts. The throw is ReferenceError: Cannot access 'FilterConditionSchema' before initialization at field.zod.ts:1529 (relatedListFilter), reached through the eager lazySchema at :1000.\n- Whole-package sweep: each of the 1039 non-test source modules taken as the first import of a fresh module graph under the flag: 22 threw, 20 with the FilterConditionSchema signature and 2 (data/authoring-key-lint.ts, kernel/metadata-authoring-lint.ts) with Cannot access 'FIELD_KEY_GUIDANCE' before initialization, through the same suggestions.zod.ts → field.zod.ts edge.\n- FieldType in suggestions.zod.ts was read only inside suggestFieldType (FieldType.options), never while the module loads.\n- scripts/lib/refinement-projection.ts and src/shared/refinement-projection.ts, reported crashing elsewhere, do not crash as a first import under the flag (exit 0 in both probe shapes). No independent second cycle exists.\n\n## The fix\n\nThe fix is the edge the loop closes on. suggestFieldType and its alias table move out of shared/suggestions.zod.ts into shared/field-type-suggestion.ts, which imports data/field.zod in one direction only. After the move suggestions.zod.ts imports no schema module, and its header says why that must stay true. shared/index.ts, the root barrel and error-map.zod.ts import the function from its new home. Public names are unchanged: suggestFieldType is still exported from @objectstack/spec and @objectstack/spec/shared, same signature, same answers; check:api-surface reports "public API surface + factory signatures unchanged". export-origins/ records the new declaration site. lazy-schema.ts and field.zod.ts are untouched, so both named fences hold.\n\n## Ruling A, implemented: an allowlisted skill pointer publishes on existence\n\nRuling: issue comment 5814528240 (batch #220 item 2, letter A, maintainer 「批 #220 同意」). Cutting the edge changed the textual import closure gen:skill-refs walks; the two objectstack-i18n rows of TRANSITIVE_ALLOWLIST were reached only through that edge, so the generator refused and check:skill-refs was red.\n\n- packages/spec/scripts/lib/skill-map-guards.ts: a TRANSITIVE_ALLOWLIST row is now a declared pointer. checkTransitiveAllowlist(map, allowlist, existsInSpecSrc) keeps its typo half (a package the map does not have), its dead-file half (a path not on disk), the already-core and listed-twice checks, and gains a does-not-ship half (a path outside *.zod.ts, which would 404 in a consumer's node_modules). Its reachability half is gone. The selection moved into publishedPointers(coreFiles, closure, allowed, existsInSpecSrc): with no list the closure publishes unchanged; with a list the index publishes the core files plus every declared row that ships, and nothing the closure reached on its own. The docblock explains why a row publishes on existence and records the removed edge in the past tense.\n- packages/spec/scripts/build-skill-references.ts: the guard is handed the disk (existsInSpecSrc), not a closure; the emit loop calls publishedPointers. The up-front closure pass the guard needed is gone.\n- packages/spec/scripts/skill-map-guards.test.ts: an allowlisted, existing, unreachable pointer publishes; an allowlisted dead file is still refused (guard) and never emitted (selection); a non-allowlisted unreachable pointer is not published, with or without a list; a file that exists but does not ship is refused; with a list, a file the closure reached on its own stays out; with no list the closure publishes unchanged; rows come back sorted; the wiring pin asserts the generator hands the guard existsInSpecSrc and no longer reads closures[skillName]. 36 tests pass.\n- Ablation (commit-first, scripts/ablation-replace.mjs, expected direction stated before the run: red): re-introducing the reachability filter in publishedPointers (&& closure.includes(rel), blob 99c90e3e → 1cca6cf1, anchor 1 → 0) turned exactly the two publishing tests red (2 failed | 34 passed) and check:skill-refs red naming skills/objectstack-i18n/references/_index.md (out of date). Restore proven: blob == HEAD 99c90e3e, git diff HEAD empty.\n\n### Regenerated indexes (pnpm --filter @objectstack/spec gen:skill-refs, never by hand)\n\n- objectstack-i18n/references/_index.md: byte-identical to origin/main (git diff --exit-code origin/main -- skills/objectstack-i18n/references/_index.md exits 0). data, ai, ui, platform and formula are unchanged too.\n- objectstack-query (7 → 1 transitive pointers) loses data/field-value.zod.ts, data/field.zod.ts, kernel/metadata-protection.zod.ts, shared/expression.zod.ts, shared/identifiers.zod.ts, shared/value-domain.zod.ts; keeps shared/suggestions.zod.ts.\n- objectstack-api (18 → 13) loses data/field-value.zod.ts, data/field.zod.ts, shared/expression.zod.ts, shared/identifiers.zod.ts, shared/value-domain.zod.ts.\n- objectstack-automation (11 → 7) loses data/field-value.zod.ts, data/field.zod.ts, data/filter.zod.ts, shared/value-domain.zod.ts.\n- None of them is taught by its skill. Over each skill's authored markdown (every .md except the generated references/_index.md): by file stem (field.zod, field-value, expression.zod, identifiers.zod, value-domain, metadata-protection, filter.zod), 0 hits for every removed pointer in its own skill (query's one filter.zod hit names its own core file, which it keeps). By exported identifier, every export name of each removed file, word-bounded: no schema-level name (FieldSchema, FilterConditionSchema, ExpressionSchema, IdentifierSchema, ValueDomain…, MetadataProtection…) appears; the only collisions are ordinary English words some file also exports (Field, Filter, FilterCondition, Expression, expression, cel, cron, P, Predicate), for example the api routing row "Filter, sort, paginate, or aggregate a request → objectstack-query" and the automation heading "Filter tokens (config.filter)", a node-config slot whose shape lives in the core builtin-node-config / io-node-config pointers.\n\n## Pin: packages/spec/src/eager-entry-import.test.ts\n\nEvery module subpath in spec's own exports map is mapped to its source entry, 17 today. Each entry is imported first in a fresh child process with OS_EAGER_SCHEMAS=1 and must not throw. Two controls keep a green meaningful: under the flag a known lazySchema export on ./data (FieldSchema) and on ./api (ApiErrorSchema) arrives as a built zod object (typeof is object), proving the flag reached the child; without the flag the same entries still import and the export stays the lazy Proxy (typeof is function). Source entries only: @objectstack/spec#test depends on ^build, not its own build, so dist/ is not guaranteed at test time, and check:test-source-alias wants unit verdicts on source. The bundles were measured by hand (below).\n\n## Evidence on the merged tree (head 247d0611fc, origin/main 61609edf81 merged via scripts/pm/os-regen-merge.sh)\n\n- Merge: 326 files from main; on this surface it brought packages/spec/src/shared/lazy-schema.ts (+51, the landed lazy-schema metadata aliasing) and skills/objectstack-ui/SKILL.md; no _index.md, no skill-map-guards.ts. Step 2 kept the branch's export-origins/root.json and shared.json (branch-only edits). After pnpm install --frozen-lockfile and a spec rebuild, check:generated read all 15 artifacts current, so the regeneration commit the script prescribes was empty.\n- Cycle fix re-measured: the pin passes (Test Files 1 passed, Tests 20 passed: 17 entries under the flag, 2 unflagged controls, 1 mapping). Dist, on the rebuilt dist/ (.build-input-hash 61e345f25156fe1e…): all 44 published JS files (main and browser conditions, .js and .mjs) exit 0 as a first import both with and without the flag (88 runs, 0 failures); on the 8 /data and /api files the probe export reads object under the flag and function without it.\n- Spec suites: vitest run --project local --maxWorkers=2: 533 files passed, 15675 tests passed, 2 todo. --project repo: 31 files, 579 tests passed. typecheck (tsc --noEmit + check:scripts-typecheck + check:test-typecheck): exit 0, 53 files / 255 errors / 142 pinned signatures held.\n- Gates: dispatch-gates --commands --repo objectstack-ai/objectstack derived 97 families on this diff at 247d0611fc; every one was run with its exit code landed on disk and reconciled with --ran: "97 derived famil(ies) accounted for — 95 run, 2 NOT-MEASURED, 0 UNRUN". 95 exit 0, among them check:skill-refs ("9 generated files in sync with packages/spec"), check:generated (15/15 current), check:api-surface (unchanged), check:export-origins (5260 exports across 17 entry points), check:doc-authoring, check:skill-identifier-liveness, check:nul-bytes, check:test-source-alias, check:cross-package-test-inputs, check:pm-governed-merges, check:pm-dispatch-gates (1925 cases). NOT MEASURED: check:dual-build-cjs-loads and check:type-check-debt, both exit 3 on their own prerequisite (every workspace package built; 66 have no dist/ here); CI builds all.\n\n## 维护者速读(草稿)\n\n改了什么:两件事。① 文档写的应急回退开关 OS_EAGER_SCHEMAS=1 一打开,@objectstack/spec 的 /api、/data 入口在导入时就崩;修法是把 suggestFieldType 搬到一个只单向引用字段 schema 的小文件里,断开那条导入环。公开导出一字不变。② 按您批的 A:技能说明书的「相关 schema 指针」允许清单,从「导入链够得着才发布」改成「文件存在且随包发布就发布」,只对清单内条目生效;生成器重跑后,objectstack-i18n 的索引逐字节不变,query / api / automation 三份索引各少掉几条从没教过的内部指针。\n\n为什么改:一个会崩的回退开关比没有更糟;运维最需要它的时候正是怀疑延迟加载本身出问题的时候。而 i18n 那两个指针本来就是说明书教的内容,不是导入关系——靠一条偶然的环把它们带出来,恰恰是这张卡要拆的隐患。\n\n风险与代价(含回滚):运行时零风险——只搬了一个函数的声明位置,check:api-surface 读作公开面不变,17 个入口、44 个发布产物在开关下全部实测能导入。生成器一侧的代价是三份技能索引少了几条指针;实测这三份说明书的正文一个都没教过它们。回滚:整体 revert 本 PR 即可,不需要数据迁移。本 PR 改到 skills/**,按规矩 Tier H,由您合并。\n\n席位意见:(留空,席位定稿)\n\n你要做的:确认 query / api / automation 三份索引少掉的指针清单(上文列出)可以接受;可以就合并。不合并也不会更糟——只是回退开关继续崩、门禁继续红。\n\n## Acceptance notes (noted, not filed)\n\n- Several comments still describe the removed edge in the present tense; the defensive code under them (hoisted functions, deferred error maps, lazily built sets) stays valid and is not removed here. Sites: the shared/strict-object.ts docblocks around declarationStore, strictObjectError, PrimableErrorMap and closedObjectConstructor; shared/strict-object.test.ts (the eager-construction block); data/field-value.zod.ts:35; data/field.zod.ts:184, fenced by the PR holding that file. The scripts/lib/skill-map-guards.ts docblock is rewritten in this PR because ruling A changes what it explains. Carrier: none.\n- The first-import crash reported for the two refinement-projection modules did not reproduce at fdeeea0cc9 (see above).\n\n## Contract review\n\nNo accept or reject result changes, and the public export surface is byte-identical per check:api-surface; only the declaration site of one function moves, plus generator tooling and its generated skills/** output. The at-tier contract review the ruling asks for is the seat's record on the card. This PR touches skills/**, so it lands Tier H on the maintainer's merge.\n\n---\n_Generated by Claude Code
    \n"
    }


    Generated by Claude Code

  9. objectstack-fleet commented on Sep 24, 2026

    @objectstack-fleet
    ContributorAuthor

    维护者速读 —— PR #19943 已按裁决 A 做完,达档复核 PASS,等您合并(Tier H),2026-09-24T19:20Z

    domain:spec seat 4(session_019c3Hi6ZMU1p6m6aA6Bz45d),认领 5819463080。

    改了什么:

    • ① 应急回退开关 OS_EAGER_SCHEMAS=1 打开后,@objectstack/spec 的 /api、/data 入口在导入时崩溃。修法是把 suggestFieldType 挪到一个小文件里,断开那条导入环。公开导出一字不变。
    • ② 按裁决 A,技能说明书允许清单里的指针改为「文件存在且随包发布就发布」,只对清单内条目生效。

    结果:

    • objectstack-i18n 的索引逐字节不变。
    • query / api / automation 三份索引少掉 6 / 5 / 4 条指针,全是这三份说明书正文里从没教过的内部文件。清单写在 PR 正文里。

    证据:

    • 达档复核 PASS 5820632479,head 247d0611fc。复核员独立重跑了生成器逻辑,9 份索引全部吻合。
    • CI 32 项通过、3 项按设计跳过。
    • 两道围栏(lazy-schema.ts、field.zod.ts)都没被本分支改动。

    风险与回滚:运行时零风险,只挪了一个函数的声明位置。回滚就是整体 revert 本 PR,不需要数据迁移。

    您要做的:PR 改到了 skills/**,按规矩 Tier H,由您合并,或由授权评审人批准后本席落地。已请 os-zhuang / hotlong 评审。PR 目前是 draft。


    Generated by Claude Code

  10. objectstack-fleet commented on Sep 25, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed — PR #19943 → 2274894cc4, 2026-09-25T00:18Z

    domain:spec seat 4 (session_019c3Hi6ZMU1p6m6aA6Bz45d), claim 5819463080. Tier H (skills/** ×3; check-governed-merges.mjs --pr 19943: GOVERNED, landing tier H). Landed on the record that tier asks for: an APPROVED review by os-zhuang (listed in GOVERNED_APPROVERS), who also marked the PR ready and enqueued it. The merge queue merged it at 2026-09-25T00:17Z. ⛔ No hand merge by this seat.

    • The squash 2274894cc4 has one parent and is an ancestor of origin/main. Content probe: git patch-id --stable of the squash's own diff equals that of the PR's diff from its merge base to head 247d0611fc, the head the at-tier PASS 5820632479 names.
    • The card closed completed through Fixes #19930, the PR body's only closing keyword.
    • pm:dispatched and the assignee are removed in one label write. Nothing on this card remains in flight.

    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions