Skip to content

build: stop embedding source text in published source maps (sourcesContent: false across the tsup builds) and repair the cli package's dead sources references (build half of #15905 question 1) #16469

Closed

Description

@os-zhuang

Filed by the director seat under the #15905 ruling (decision batch #64, 2026-09-07): question 1 of the E3 standard ("is .js.map sourcesContent published?") is answered by turning it off, so that the answer is "no" by construction rather than by interpretation.

Measured today (from #15905, verified on the npm tarballs being distributed)

  • 55 of 57 publishable packages ship .js.map files with sourcesContent embedding the complete original source including every comment (@internal · Test-only blocks are readable from the npm package).
  • 72% of non-test source files, 23,242 TSDoc blocks and 87,281 line comments are distributed this way; maps are 49.3% of published bytes (@objectstack/spec: about half of 49 MB).
  • Nobody decided this: sourcemap: true is configured; embedding is esbuild's default. The tsc-built @objectstack/cli does not embed — and its 238 sources entries point at src/** paths that are not in the tarball (dead references).

Scope

  • Set sourcesContent: false once, at the shared tsup / esbuild configuration level (not 55 per-package edits), so every published map keeps mappings for stack-trace positioning but carries no source text.
  • Fix the cli package's maps: either include the referenced sources in files[] (probably not) or emit maps whose sources resolve — pick the one consistent with the rest of the fleet and say why.
  • Pin: a check: gate over dist/**/*.map that fails when any map carries a non-empty sourcesContent array, with a positive control fixture.
  • Changeset: patch across affected packages; the release note is written centrally (the changeset is this PR's input).

Not in scope

Acceptance

  • fresh build: zero maps with sourcesContent; gate red on a planted fixture
  • cli maps have no dead sources
  • published-size delta recorded on this card (before/after for spec and one small package)

Refs #15905, #14147 / #15395 (where the sourcesContent debate first surfaced).

Activity

  1. claude commented on Sep 7, 2026

    @claude
    Contributor

    Claim: PM seat domain:devx @ objectstack (#6023), session session_01Vbw3RPgdtqesx4azk9SbW8, 2026-09-07T03:30Z — dispatching to an os-dev subagent on branch claude/issue-16469-sourcemaps-no-sources-content.

    Clause-②: no

    Reason for no: the clause covers a card that changes a published contract's accept/reject behaviour or widens the public surface. This one removes source text from published .js.map files and adds a gate over dist/**/*.map — the published surface narrows, no contract's accept/reject behaviour moves, and no API is widened. The tier is ordinary; the direction is already ruled (director seat, #15905 decision batch #64), so the dev implements, it does not re-decide.

    Pre-dispatch premise checks at this claim (read on origin/main, not on the card):

    • File surface free: no open PR of the 33 currently open touches tsup.config.ts, a sourcemap setting, packages/cli's package/tsconfig, or a map-checking gate script.
    • The per-package tsup.config.ts files the card's "not 55 per-package edits" instruction points away from are present on origin/main (packages/core, packages/lint, packages/metadata-*, packages/apps/*, …); whether a shared config layer exists that all of them extend is the dev's first premise to verify — if there is no single place to set sourcesContent: false, the dev reports the shape it found and its options before editing 55 files.
    • The cli's dead sources references and the measured byte shares in the body are the card's claims, not verified by this seat; the dev re-measures on a fresh build before acting on them.

    The assignee and this comment are the PM's write. The dev inherits both, posts no second claim, and never touches the assignee field.


    Generated by Claude Code

  2. claude commented on Sep 7, 2026

    @claude
    Contributor

    Dev stall + reset, 2026-09-07T03:47Z — PM seat domain:devx @ objectstack (#6023), session session_01Vbw3RPgdtqesx4azk9SbW8, recorded on the card because a stall that is not written down reads as silence.

    The dev on this card (claim 5564618043, branch claude/issue-16469-sourcemaps-no-sources-content) ended its turn with "Both jobs remain in progress. I'll pick this back up automatically via the armed Monitor notifications" while a closure build and the dispatch-gates reconciliation batch were running. That wake does not arrive in this harness, so the work stopped rather than continued. Reset sent at 03:47Z: resume in the same worktree under the same claim, poll long jobs in the foreground instead of ending the turn, and finish the card as briefed. No work was lost and nothing was re-dispatched.

    This is the seventh instance of the same shape on this seat today; the pattern and its cost are tracked on #16332. Recorded here only so this card's timeline shows why the gap exists.


    Generated by Claude Code

  3. baozhoutao commented on Sep 7, 2026

    @baozhoutao
    Contributor

    os-dev-report
    {
    "issue": 16469,
    "status": "done",
    "branch": "claude/issue-16469-sourcemaps-no-sources-content",
    "pr": "#16492",
    "premise_still_valid": true,
    "summary": "sourcesContent is now false by construction for every tsup/esbuild build: one shared helper (scripts/tsup-drop-sources-content.mjs, wired through tsup's esbuildOptions hook) is the single place the setting is decided; the repo-root tsup.config.ts (which 47 packages build through directly, with zero config changes of their own) and the 19 bespoke tsup.config.ts files that cannot use the shared file each wire it in with one line. mappings are untouched -- verified a rebuilt map still resolves a real position to the correct original file/line/column. packages/cli (tsc-built) needed no code change: measured that its dead sources references are not a cli-specific defect but the same shape every tsup-built package's maps now share once embedded content stops masking it, and the PR states why shipping src/ was rejected instead. Added pnpm check:sourcemap-no-sources-content, a closure-wide gate over dist/**/*.map with a red-first positive-control fixture, wired into ci.yml's Build Core job.",
    "tests": "node scripts/check-sourcemap-no-sources-content.mjs --self-test: fixture-driven batteries (8 cases + 3 dispatch-gates-hint cases) pass, including the positive control (a planted map carrying sourcesContent asserted to go red, named by package and file) and a same-run assertion that run() answers it with exit 1. Red-first proof against the real tree: ran the gate against a fresh, unmodified build of origin/main before any config edit -- 55 built packages flagged (matches the card's 55-of-57 figure). After the fix: rebuilt @objectstack/spec and @objectstack/types (measured before/after byte counts below), rebuilt @objectstack/cli's full ~50-package dependency closure, then a full pnpm build (72/72 tasks) -- pnpm check:sourcemap-no-sources-content reports 68 built packages swept, 484 maps, none embed source text; pnpm check:dts-closure and pnpm check:dual-build-cjs-loads both green on the same closure. Stack-trace positioning verified with node:module's SourceMap.findEntry against a rebuilt @objectstack/spec map: a real generated position resolves to the correct original file/line/column. Measured size deltas (fresh builds, same commit): @objectstack/spec dist 163.08MB->80.88MB, .map bytes 97.49MB->15.29MB (84.3% map reduction); @objectstack/types dist 928.3KB->406.5KB, .map bytes 577.0KB->55.3KB (90.4% map reduction) -- both materially larger than the card's recollection ('spec: about half of 49MB'), stated as a real disagreement rather than reconciled. eslint --no-inline-config clean on every touched file; check-nul-bytes clean; check-declaration-mirrors and check-watch-hint-literal both pass on the new scripts/tsup-drop-sources-content.d.mts pairing and the new ROOT_DIR_WATCH_HINTS/ROOT_FILE_WATCH_HINTS declarations. pnpm check:type-check-debt re-measured clean both before and after a second origin/main merge (root DEBT ledger held at 26, no drift) -- the first pass caught a real +1 (TS7016 on the untyped .mjs import into the root tsc program), fixed with the .d.mts pairing before opening the PR. pnpm check:pm-dispatch-gates (1534 self-test cases) green. node scripts/pm/dispatch-gates.mjs --commands then --ran against the full 100-command derived list: 100 derived, 100 run, 0 NOT-MEASURED, 0 UNRUN, re-confirmed unchanged after two origin/main merges. One transient, unrelated finding: a full pnpm build once failed at @objectstack/client-react's DTS pass with a Cannot-find-module error against @objectstack/client even though client's dist/index.d.ts was already on disk -- neither package nor its config is touched by this diff, and an isolated rebuild of client-react immediately succeeded; read as a pre-existing closure-build race (the class check:dts-closure's own header documents, #15042) and stated in the PR body rather than silently re-run away.",
    "mcp_calls": 2,
    "open_questions": [],
    "out_of_scope_findings": []
    }


    Generated by Claude Code

  4. claude commented on Sep 7, 2026

    @claude
    Contributor

    ACCEPT — PR #16492 (head 205163545, read 2026-09-07T04:33Z) — PM seat domain:devx @ objectstack (#6023), session session_01Vbw3RPgdtqesx4azk9SbW8. Reviewed against the three-dot diff origin/main...claude/issue-16469-sourcemaps-no-sources-content (26 files, +675/−0), every changed line read; the dev's report is the increment I checked, not the evidence.

    • PR shape: draft, base main, first line Fixes #16469; full-body keyword scan reads that one binding and nothing else. Four commits, size/l, zero deletions anywhere. No model identifiers in title, body or diff (the commit's co-author trailer is the one permitted place).
    • The premise this seat sent out as unverified came back answered, and answered the right way. The brief said: if there is no shared tsup layer, ⛔ do not fan out 55 per-package edits — report the shape. The dev measured the real shape instead of either assumption: there is no monorepo-wide config packages extend, but ~47 packages build through the repo-root tsup.config.ts directly (tsup --config ../../tsup.config.ts), and 19 carry a bespoke one. So the decision lives in one new file — scripts/tsup-drop-sources-content.mjs, a single dropSourcesContent(options) that sets options.sourcesContent = false through tsup's esbuildOptions hook — wired into the root config (covering the 47 with no change of their own) and one line into each of the 19. That is the smallest correct change, not a fan-out, and the helper's own doc-block records why for the next reader.
    • sourcemap: true untouched everywhere, so mappings survive; the dev verified positioning with node:module's SourceMap.findEntry against a rebuilt @objectstack/spec map (a real generated position resolving to the right original file/line/column). packages/spec's config gets the hook on both its main and browser configs, correctly, since each is its own Options.
    • The cli half, which the ruling left to the dev's judgement: it chose to leave cli's tsc-emitted maps as they are, and its reasoning holds — the non-resolving sources labels are not a cli-specific defect but the shape every tsup-built package's maps share once embedded text stops masking it, and shipping src/** in files[] to make them resolve would put most of the removed bytes straight back. The PR body states this. Accepted as the fleet-consistent answer the card asked for.
    • Gate: pnpm check:sourcemap-no-sources-content (scripts/check-sourcemap-no-sources-content.mjs, +506) sweeps every built non-private package's dist/**/*.map. It has the shape this repo requires: a positive control that plants real sourcesContent on real disk and asserts the run goes red, a PREREQUISITE NOT MET exit 3 distinct from a finding's exit 1 when no dist/ exists (so it can never pass silently on an unbuilt tree), and a selfTestReachedVerdict guard so a self-test that returns early cannot read as green. Wired into ci.yml's Build Core job beside check:dual-build-cjs-loads, which is the right neighbour for the same reason — it reads a real dist/.
    • Red-first against the real tree, not only the fixture: the gate run against a fresh unmodified build of origin/main flagged 55 built packages — independently reproducing the card's "55 of 57" figure — and after the fix a full pnpm build (72/72) swept 68 packages / 484 maps with none embedding source text.
    • Changeset scope — checked, not assumed: the changeset lists 20 packages, which at first reading looks narrower than the ~55 whose output changes. It is not: .changeset/config.json puts 69 packages in one fixed group, and all 20 are inside it (verified by set difference — empty), so any one bump versions the whole group. Every published package therefore republishes with the new maps; no package is left behind carrying embedded source text until some unrelated future bump. Check Changeset green.
    • Governed: check-governed-merges.mjs --test on the final 26-file list ⇒ NOT governed. Governed Surface Queue Guard green.
    • Two disagreements the dev reported rather than smoothed over, both correct to record: (1) the measured size deltas are materially larger than the card's recollection — @objectstack/spec map bytes 97.49MB → 15.29MB (−84.3%), @objectstack/types 577.0KB → 55.3KB (−90.4%), against the card's "spec: about half of 49MB"; the card's number was a different session's measurement and this one is the fresh build. (2) a one-off @objectstack/client-react DTS failure during a full build that an isolated rebuild did not reproduce, in a package this diff never touches — read as the pre-existing closure-build race check:dts-closure's own header documents ([finding] @objectstack/service-cluster's dist/*.d.ts vanished AFTER its own build reported 3/3 declared declaration file(s) present — a downstream typecheck then failed TS7016 against a package that had built green #15042), stated in the PR body rather than re-run away. Neither blocks this PR; the second is worth its own card if it is seen again.
    • Evidence through the lock: check:dts-closure and check:dual-build-cjs-loads both green on the rebuilt closure; check:type-check-debt clean with the root ledger held at 26 after a real +1 was caught and fixed with a .d.mts pairing for the untyped .mjs import; check:pm-dispatch-gates 1534 self-test cases green; dispatch-gates 100 derived / 100 run / 0 NOT-MEASURED / 0 UNRUN, re-confirmed after two origin/main merges.

    Landing plan: flip point armed 05:10Z; flip ready when every row at 205163545 reads completed/success on its own line (each Test Core (k/6), Lint & Repo Gates, each Type Check · row, each Dogfood Regression Gate (k/3), Dogfood Verify CLI, Build Core, Build Docs, Console Pin Gate, Temporal Conformance, Validate Package Dependencies, Spec property liveness; Check Changeset already green) → auto-merge → probe added_to_merge_queue. On MERGED → probe on origin/main: options.sourcesContent = false ≥1 in scripts/tsup-drop-sources-content.mjs, esbuildOptions: dropSourcesContent ≥1 in the root tsup.config.ts, and check:sourcemap-no-sources-content ≥1 in package.json; negative control: check:sourcemap-no-sources-content = 0 on the pre-merge origin/main ref recorded at flip time; card closes by Fixes, strip pm:dispatched, clear assignee.


    Generated by Claude Code

  5. claude commented on Sep 7, 2026

    @claude
    Contributor

    LANDED — PR #16492 merged 2026-09-07T06:14:49Z (merge commit c5d680366a196e90b913973214a27097b6217ca2, head 205163545, Fixes #16469 closed the card). PM seat domain:devx @ objectstack (#6023), session session_01Vbw3RPgdtqesx4azk9SbW8, probe read 2026-09-07T06:19Z.

    • Direction implemented: the The E3 standard needs three written published boundaries — .js.map sourcesContent, @objectstack/spec’s shipped src/**/*.zod.ts, and whether it reaches a defect the round did not cause #15905 ruling (director seat, decision batch Fix TypeScript Module Resolution for Subpath Exports #64) — question 1 of the E3 standard answered "no" by construction. Per the ACCEPT 5565049794.
    • The premise this card shipped with turned out to be wrong in the useful direction. The card said "set sourcesContent: false once, at the shared tsup/esbuild configuration level (not 55 per-package edits)". There is no monorepo-wide config packages extend — but ~47 packages build through the repo-root tsup.config.ts directly, and 19 carry a bespoke one. So the decision still lives in exactly one place (scripts/tsup-drop-sources-content.mjs), wired into the root config and one line into each of the 19. One decision point, 20 wire-ups, not a 55-file fan-out — which is what the dispatch forbade and what the measurement avoided.
    • Queue: added_to_merge_queue 05:00:32Z, one regroup (recorded 5565670792), no removal, the seat's re-queue unspent. Group run 34086185555 went all six shards green with its aggregate at 05:45:34Z but was superseded by the 05:42:25Z regroup; landing group run 34087826215 carried it in. A superseded green run is the queue re-forming, not a verdict on the PR.
    • Content probe on origin/main = a5eccf925 (git fetch + git merge-base --is-ancestor yes):
      • options.sourcesContent = false in scripts/tsup-drop-sources-content.mjs = 1
      • esbuildOptions: dropSourcesContent in the root tsup.config.ts = 1
      • check:sourcemap-no-sources-content in package.json = 1
      • negative control at the pre-merge ref 4500bc5d7: the same string = 0, while defineConfig in that ref's tsup.config.ts = 2 (the ref and the file are real, so the zero is real)
    • Measured, and larger than this card estimated: @objectstack/spec map bytes 97.49MB → 15.29MB (−84.3%), @objectstack/types 577.0KB → 55.3KB (−90.4%). The card's own figure was "spec: about half of 49MB" — that was an earlier session's reading, and the dev reported the disagreement rather than reconciling it silently.
    • Pin: pnpm check:sourcemap-no-sources-content sweeps every built non-private package's dist/**/*.map, with a planted positive control on real disk, PREREQUISITE NOT MET (exit 3) distinct from a finding (exit 1) so it can never pass silently on an unbuilt tree, and a verdict-reached guard. Wired into Build Core. Red-first against a fresh build of origin/main flagged 55 packages, independently reproducing this card's figure.
    • Republish coverage, checked not assumed: the changeset names 20 packages, but .changeset/config.json holds 69 in one fixed group and all 20 are inside it — so every published package versions together and republishes with the new maps. No package is left carrying embedded source text until some unrelated future bump.
    • Card: pm:dispatched stripped (four-step, read-back tooling, priority:p2, domain:devx); assignee cleared; closed by GitHub via Fixes.

    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

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions