gates: let the eager-closure budget tolerate a module split without admitting new eager work

Open
#2,469 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Assessment

Difficulty
5/5
Estimated time
Over a week
Newbie friendliness
35/100
Issue type
Feature
Clarity
Mostly clear
Activity status
Active
Tech stack
typescript
Domain
tooling

Research direction

Start with scripts/tests/eager-closure-budgets.ts and the ADR-0019 loading-shape requirement. Compare the proposed rules, add coverage for a pure module split and a split with a new heavy eager edge, then re-run the extraction described in PR #2423; done means the split passes while added eager work fails and other no-growth protections remain unchanged.

Written by the indexing model from the issue text.

Description

refactor

Purpose

Decide how the eager-closure gate (scripts/__tests__/eager-closure-budgets.ts) should treat a module split, so that extracting a large module inside a gated closure stops being impossible without weakening the property the gate protects.

Current behavior

Every entry that exists at the merge-base is held to NO GROWTH of its eager import closure, counted in modules. There is no approval path for growth of an existing entry (APPROVED_OVER_CEILING covers first-introduced entries only). A module that is in a gated closure therefore cannot be split: one module becoming N modules is +N−1 for every entry that evaluates it.

Measured on PR #2423 at b21efb0: extracting packages/platform-apple/src/runner/runner-cache-metadata.ts (621 lines) into runner-phase-budget.ts + runner-toolchain-probe.ts grows seven entries by two each: app-lifecycle-facade 120→122, app-resolution-facade 61→63, doctor-facade 101→103, perf-facade 60→62, physical-device-facade 47→49, runner-operations-facade 100→102, runner/index.ts 13→15. The review on #2423 asked for exactly that extraction for ownership clarity, and it had to be deferred.

What the gate protects

The ADR-0019 loading shape: a façade must not evaluate implementation it does not need at import time. The module count is a proxy for eager work. A split does not add eager work when the new modules together evaluate what the old one did, but it does add modules, so the proxy reads it as growth.

Design question

Choose a rule that admits healthy extraction without admitting new eager work. Candidates, none proven:

  1. Count something closer to eager work than module count: total source bytes, or statements at module top level, of the closure. A pure split keeps it flat; a new static edge to real implementation raises it. Costs: a heavier walker; renames and formatting churn move the number.
  2. Recognize a split structurally: the new modules are all new at the head, and each is imported only by modules that were in the old module's importer set at the merge-base. As the reviewer of #2423 noted, this does not by itself prove no new eager work (a "new" module can carry a new heavy edge).
  3. An explicit, reviewed approval row for growth of an existing entry (APPROVED_GROWTH), naming the issue, the reason, and the expected delta, retired automatically once the merge-base carries the new shape. Same mechanism as the existing ceiling rows; costs a hand-edited row per split.
  4. Combine 2 and 1: accept a structural split only when the closure's byte or statement total does not grow.

Deliverable

A short design note choosing one, with the walker change and the test that plants (a) a pure split and (b) a split that smuggles a new heavy edge, showing the first passes and the second fails. Then re-run the #2423 extraction as the first consumer.

Non-goals

Loosening no-growth for anything other than splits; changing category ceilings.

Dominant language
TypeScript
Stars
4.7k
Forks
304
Avg merge
12h 42m
Merged PRs (30d)
463

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from callstack/agent-device

All issues in callstack/agent-device

Similar issues

More TypeScript issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.