Skip to content

fix(deps): a fresh scaffold's tree carries no ts-morph — the CLI takes the compiler from typescript directly - #29711

Merged
objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/sec-hardening-b
Nov 3, 2026
Merged

objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/sec-hardening-b

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Clause-②: no
Metadata-protocol: none

维护者速读

  • 改了什么:@objectstack/cli 不再依赖 ts-morph,改为直接依赖 typescript ^6.0.3(与 @objectstack/lint 已声明的同一范围);CLI 只用到 ts-morph 转出的编译器命名空间,从未用它的项目 API。
  • 为什么改:新建项目的依赖树里,ts-morph → @ts-morph/common → minimatch 10 这一支是我们自己的包可以去掉的;改后新脚手架的树不再含 ts-morph、@ts-morph/common 及其带入的 minimatch 10。
  • 风险与回滚:低——同一条 TypeScript 6.0 线,命令、导出、行为不变;npm 安装的项目会多嵌套一份 typescript@6.0.3(净增约 9.9 MB 解包体积,pnpm 无增量反而净减);回滚即还原本 PR。
  • 先看:packages/cli/package.json(依赖互换)、packages/cli/src/utils/emitted-source-parses.ts(懒加载改为取 default)、packages/cli/test/published-subpath-hook-body.pin.test.ts(tarball pin 改借 typescript)。
  • 两分钟验证:pnpm --filter @objectstack/cli exec vitest run --project unit --maxWorkers=2 test/published-subpath-hook-body.pin.test.ts,预期 15 个用例全过(打包后的 tarball 用 typescript 执行抽取器)。
  • 需要你做的:无,席位落地。

What changes

The rule: a fresh scaffold's tree carries no ts-morph and no @ts-morph/common, so none of the minimatch 10 line that @ts-morph/common 0.29.0 (its newest release, minimatch ^10.0.1) pulls in. The CLI takes the TypeScript compiler from typescript directly.

  • packages/cli/package.json: ts-morph ^28.0.0 is dropped from dependencies; typescript ^6.0.3 moves from devDependencies to dependencies. That is the range @objectstack/lint (a CLI dependency) already declares at runtime, on the 6.0 line ts-morph 28 bundled (6.0.2).
  • Six CLI modules took only the compiler namespace (ts) from ts-morph. They now import it from typescript: a static import ts from 'typescript' in detect-free-identifiers.ts and extract-hook-body.ts, and a lazy const { default: ts } = await import('typescript') in emitted-source-parses.ts, importable-binding.ts, scaffold-wiring.ts and authored-source-codemod.ts, which stay lazy for the reason their headers give. Type-only imports become import type TS from 'typescript'.
  • Tests: four test files switch the same import. The ./hook-body tarball pin now borrows typescript into its consumer and asserts that the manifest declares it under dependencies. create-in-repo-scaffold-typechecks.test.ts reads the CLI's own TypeScript range from dependencies, which is where it is declared now.
  • pnpm-lock.yaml: ts-morph@28.0.0, @ts-morph/common@0.29.0 and code-block-writer@13.0.3 leave the lockfile. osv-scanner.toml and a pnpm-workspace.yaml comment stop listing @ts-morph/common/ts-morph as a minimatch parent, because the lockfile no longer has it.
  • .changeset/cli-compiler-from-typescript.md: @objectstack/cli patch.

The other path a fresh scaffold had to the affected glob line, @objectstack/metadata, already carries the patched glob ^14.0.3 range on main (pending changeset 28292-glob-14-advisory). This PR does not touch it.

Tests

All run at deaa7e45 unless a line says otherwise. Build and test seconds were measured under the shared verify lock on a shared box.

  • turbo run build --filter=@objectstack/cli...: 62/62 tasks, lock VERDICT command-exit 0 (5b804323; the later commit only changes a test file).
  • pnpm --filter @objectstack/cli typecheck: exit 0 at 5b804323. The run includes the test-layer check, check:test-typecheck: OK, with debt held at its pinned entries. The later commit's test file was re-run, as described below.
  • pnpm --filter @objectstack/cli exec vitest run --project unit --maxWorkers=2: 374/375 files and 5286/5287 tests passed at 5b804323. The one red was create-in-repo-scaffold-typechecks.test.ts, which still read the CLI's TypeScript range from devDependencies. It was fixed in deaa7e45 and re-run: 1/1 file, 5/5 tests. The integration tier is left to CI, because no integration-tier file or spawn entry changed.
  • create-objectstack, the scaffold package: typecheck exit 0, and vitest run passed 23/23 files and 356/356 tests.
  • Built dist executed on plain node, with no tsx and no vitest: findEmissionParseFailures flags export const x = ; and passes export const y = 1;. findBarrelAliasRefusal('my-alias') refuses and findBarrelAliasRefusal('myAlias') returns null. barrelExportsBinding answers true for an exported a and false for b, which appears only in a comment. The compiler resolved from the CLI is 6.0.3.
  • One-time reverse verification of the retargeted tarball guard. The typescript line was deleted from packages/cli/package.json (anchor count 1 to 0, blob 9c7c8f4a630c to 9cd3a889c435). The pin then failed on @objectstack/cli must declare typescript in "dependencies". Restored: blob equals HEAD and git diff HEAD is empty. The first attempt was a no-op that the helper refused before any test ran, because its replacement text was a substring of the anchor.

Acceptance notes

  • Not closed here (no producer fix exists today): two other paths in a fresh scaffold's tree still resolve minimatch below 11.
    • @oclif/core 5.1.11, its newest release, declares ^10.2.5.
    • exceljs 4.4.0 (@objectstack/rest's xlsx import/export, also its newest release) reaches minimatch through archiver 5 and unzipper 0.10.
    • Even archiver 8.0.0's readdir-glob 3.0.0 declares minimatch ^10.2.2.
    • The only patched minimatch is 11, which removes makeRe and AST and changes the parsed-pattern form that glob 7 reads. No published parent accepts it yet, the same reading the osv-scanner.toml entry records for this workspace. So a cross-major scaffold overrides entry was not added.
  • Declined: an npm audit check in the scaffold smoke. Not added. New gates are "no" by default, and the only exception is a maintainer naming one (.claude/skills/pm-dispatch/SKILL.md, and references/instrument-discipline.md). No such naming exists here.
  • Install footprint: a project whose root typescript is the scaffold's ^5.3.0 gets one more nested typescript@6.0.3 under @objectstack/cli from npm. @objectstack/lint's copy is nested beside it, and npm cannot share sibling-nested copies. That copy is 24.3 MB unpacked. It replaces ts-morph (1.5 MB), @ts-morph/common (12.8 MB, which bundled its own compiler), code-block-writer and path-browserify, for a net of about +9.9 MB on npm. pnpm stores one copy, so the net there is about -14.4 MB.
  • osv-scanner.toml: the existing minimatch entry keeps its id, scope and ignoreUntil. Its reason only drops @ts-morph/common, a parent that is no longer in the lockfile. That is not an addition or a renewal, so it may ride with the fix under the file's convention 3.
  • main moved 14 commits since the base 06881565. None of them touch a path in this diff, so the branch was not merged.
  • No API, command, export or behaviour change. Type-only imports change their spelling, not their shape.

Verification

Simulated next-release scaffold: the blank template's manifest, with every reachable @objectstack/* package (60) replaced by its current workspace manifest. npm 10.9.9, npm install --package-lock-only --ignore-scripts --install-links, then npm ls.

Installed-node diff, base 0688156 vs this branch (537 to 534 nodes)
removed node_modules/@ts-morph/common   @ts-morph/common@0.29.0
removed node_modules/code-block-writer  code-block-writer@13.0.3
removed node_modules/path-browserify    path-browserify@1.0.1
removed node_modules/ts-morph           ts-morph@28.0.0
added   node_modules/@objectstack/cli/node_modules/typescript  typescript@6.0.3
npm ls ts-morph @ts-morph/common typescript, this branch
my-app@0.1.0
+-- @objectstack/cli@18.2.0
| +-- @objectstack/lint@18.2.0
| | `-- typescript@6.0.3
| `-- typescript@6.0.3
`-- typescript@5.9.3
Gates
derived  node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack @ deaa7e45 -> 77 commands
pass 1   75 exit 0; 2 exit 3 PREREQUISITE NOT MET (dist/ of packages outside the CLI closure):
           pnpm check:dual-build-cjs-loads, pnpm check:i18n-coverage
re-run   once those dist/ trees existed, both exit 0:
           check:dual-build-cjs-loads: 115 published require entry point(s) across 70 package(s) load
           check-i18n-coverage: OK (14 config(s), 426 baselined untranslated string(s), none new)
--ran    77 derived, 77 run, 0 NOT-MEASURED, 0 UNRUN
selected verdicts (exit 0):
  check:undeclared-dep-imports  129 workspace packages, 5757 non-test src files; 3 ledger rows, evidence intact
  check:override-consistency    63 published-manifest declarations resolve to their override targets
  check-osv-exemptions          2 exemptions, unexpired ignoreUntil within 90 days, reason with advisory link
  check-adr-0087-registration   no declared-breaking changeset (1 non-breaking changeset seen)
  check:published-files         74 publishable packages declare a covering files whitelist
  check:nul-bytes               24403 text files, no raw ASCII control bytes

Generated by Claude Code

…ctly

@objectstack/cli used ts-morph only for the compiler namespace it
re-exports. It now declares typescript ^6.0.3 (the range
@objectstack/lint already declares) and drops ts-morph, so a fresh
scaffold's tree no longer carries @ts-morph/common or its minimatch 10
line.

Claude-Session: https://claude.ai/code/session_01QSeKYn5CmmdShCJifm6WYj
Co-authored-by: Claude <noreply@anthropic.com>
The in-repo scaffold pin compared IN_REPO_TYPESCRIPT_RANGE with the
CLI's devDependencies.typescript; that declaration now lives under
dependencies, where the tsc the file spawns resolves it from.

Claude-Session: https://claude.ai/code/session_01QSeKYn5CmmdShCJifm6WYj
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added size/m dependencies Pull requests that update a dependency file documentation Improvements or additions to documentation tests tooling Fix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface labels Nov 3, 2026
@github-actions

github-actions Bot commented Nov 3, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

4 anchor(s) derived from 1 changed package(s); no hand-written page names any of them. ⚠️ 3 changed file(s) yielded no anchor (packages/cli/package.json, packages/cli/src/utils/detect-free-identifiers.ts, packages/cli/src/utils/extract-hook-body.ts), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files.

What this run could not see
  • 3 changed file(s) yielded no anchor (packages/cli/package.json, packages/cli/src/utils/detect-free-identifiers.ts, packages/cli/src/utils/extract-hook-body.ts) — pages documenting those are invisible to this run
  • the SDK route bridge reached 60 of 216 client-bound route-ledger rows — the other 156 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 156: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 55 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 101 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.
  • a key NAME is not a key, so the hand re-read the line above prescribes can land on the wrong schema. The same spelling is authorable on one governed type and a [REMOVED] tombstone on another for each of active, aria, joins, objects, template, tools and version (censused on [finding] tools is a key on BOTH AgentSchema (tombstoned, dead) and SkillSchema (live, cloud-attested), so a name-based search attributes skill examples to the agent key — it produced a false stop-the-line alarm on PR #19059 #19093 over the liveness ledger's governed types, top-level keys); nothing in a search result distinguishes the two, so a grep hit on a LIVE example reads as evidence about the DEAD key. Measured on fix(spec): the agent.tools liveness row says dead — it claimed live on a key the schema tombstoned #19059: content/docs/ai/agents.mdx was reported as contradicting the agent.tools tombstone over its tools: example at :161, which is inside the defineSkill({ block opened at :155 — the page was already correct. Settle ownership by PARSING the value against both schemas, never by the name: that literal PASSES SkillSchema, and as an AgentSchema it FAILS at tools with the tombstone prescription. ⛔ These names are not the whole class — a key retired through a .strict() guidance map leaves no tombstone in the walked shape and none of them here (tool.category, live as AIToolDefinition.category).

Coarse fallback — 35 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json c9636f1e50e48a2fee5205f0a9978c2a63f2dc07 → packageMentionDocs.

Which tree this was computed on

This run read content/docs from 748121db508ca9dabceddf04464bfc627b4f1632 — the merge of head deaa7e450e040e8ac8f1f05738c01dacae616dcd into base c9636f1e50e48a2fee5205f0a9978c2a63f2dc07, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 748121db508ca9dabceddf04464bfc627b4f1632 && git checkout 748121db508ca9dabceddf04464bfc627b4f1632
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin c9636f1e50e48a2fee5205f0a9978c2a63f2dc07 deaa7e450e040e8ac8f1f05738c01dacae616dcd && git checkout -B drift-repro c9636f1e50e48a2fee5205f0a9978c2a63f2dc07 && git merge --no-ff deaa7e450e040e8ac8f1f05738c01dacae616dcd

node scripts/docs-audit/affected-docs.mjs --json c9636f1e50e48a2fee5205f0a9978c2a63f2dc07

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:skills#2 · os-justin · session_01QSeKYn5CmmdShCJifm6WYj

席位核对

ACCEPT · head deaa7e450e040e8ac8f1f05738c01dacae616dcd · 2026-11-03T02:47Z

  • Shape: 18 files, +63/−73, not governed (check-governed-merges.mjs --pr 29711), under 3000. Clause-②: no; changeset is an @objectstack/cli patch.
  • Diff read by the seat: every ts-morph import (value and type) becomes typescript (import ts from 'typescript', await import('typescript') → default). typescript ^6.0.3 moves from devDependencies to dependencies on the same range. The lockfile drops ts-morph, @ts-morph/common and code-block-writer. The tarball pin now borrows typescript and its guard names it. The osv exemption's reason drops @ts-morph/common (no id, scope or date change). The public text names only upstream packages and public GHSA ids.
  • Check-runs at this head: 57 success, 7 skipped, 0 failed. The skips are opt-in harnesses, the dogfood and temporal conformance jobs, and the schedule gate, judged by name; check-expected-skips.mjs NOT MEASURED (no node_modules in this container). mergeable_state is clean.
  • Accepted deviations: no origin/main merge (none of the 14 newer commits touches these paths); the two out-of-package text corrections (osv-scanner.toml reason, pnpm-workspace.yaml comment) that this change made false.
  • Out of scope: the remaining transitive entries via @oclif/core and exceljs have no producer fix. The seat keeps the existing exemption (renewal 2026-11-30) and waits for upstream.
  • Landing: ready, then auto-merge.

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review November 3, 2026 02:49
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge November 3, 2026 02:49
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Nov 3, 2026
Merged via the queue into main with commit c76c0e0 Nov 3, 2026
65 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/sec-hardening-b branch November 3, 2026 03:07
marchtian pushed a commit that referenced this pull request Nov 3, 2026
…Dependency

#29711 dropped ts-morph from @objectstack/cli, and no workspace package
declares it any more. The codemod resolved ts-morph through the CLI's
closure, so on a fresh `pnpm install --frozen-lockfile` its --verify and
--self-test stopped at exit 2 and the ruled proofs could not be reproduced
from a clean checkout.

- package.json: ts-morph ^28.0.0 joins the root devDependencies, the way
  the other root scripts take typescript, yaml and semver.
- pnpm-lock.yaml: written by `pnpm install --lockfile-only`. It adds
  ts-morph@28.0.0, @ts-morph/common@0.29.0 and code-block-writer@13.0.3.
  @ts-morph/common's minimatch resolves to the 10.2.6 already in the tree.
- scripts/codemod-split-sql-driver.mjs: the loader resolves from the root
  manifest. The header says so, and also says which exit a tree with no
  install at all gets.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017ihL2gLghEqeDKTbDTC7Zd
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies Pull requests that update a dependency file documentation Improvements or additions to documentation size/m tests tooling Fix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant