You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit 81f4666
Browse filesBrowse the repository at this point in the historyBrowse files
refactor(eager-closure): tolerate a pure split in the no-growth rule (#2469) (#3298)
* refactor(eager-closure): tolerate a pure split in the no-growth rule (#2469)
The NO-GROWTH rule counts modules, and a split of a hub module is count
growth with no new eager work, which made extracting any module inside a
gated closure impossible (#2423 extraction deferred, ADR 0027 measured
the descriptor-root split at +11 per surface). Move the rule to
closure-growth-rule.ts and tolerate exactly the split shape: the head
closure still evaluates every merge-base module (renames canonicalized)
and the closure's total top-level-statement weight (wiring excluded) did
not grow. Each half alone is refutable; together they admit healthy
extraction and refuse a smuggled heavy edge or a dropped module. The
hard ADR-0019 checks stay untouched.
Planted in both directions: a pure split passes end to end, a split
smuggling a module-scope call into a new module fails naming the
smuggled weight, and a flat-weight drop fails as not-a-split.
First consumer: extract runner-phase-budget.ts and runner-toolchain-
probe.ts out of runner-cache-metadata.ts (the #2423 review's ownership
ask). All seven affected entries keep containment at flat weight, so
the gate passes with zero approval rows.
* fix(apple): keep runner-cache.ts's exported surface exactly as before the split
createRunnerPhaseBudget reached consumers through runner-xctestrun.ts;
the split briefly widened runner-cache.ts to export it too, which
fallow's dead-code audit reports as a new unconsumed re-export.
* fix(eager-closure): count a source-less re-export as wiring; state the guarantee precisely
Review on #3298:
- topLevelStatementCount treated `export { a }` (no source, no declaration) as module-scope
work, so the re-export half of a split's wiring added false weight and could fail a pure
split that re-exports through a local binding. Excluded at the owning predicate; planted
in closure-growth-rule.test.ts (mutation: return false for declaration-free named exports
re-fails exactly that case).
- ADR 0027, its rule-module header, and ADR 0028 said the pair 'refuses new eager work'.
It does not: nothing count-based sees a split that replaces a statement with a more
expensive top-level call. The texts now state the actual guarantee -- flat statement
weight over a preserved closure -- and name the residue and what bounds it.
* fix(eager-closure): a tolerated split may only add new files; shrinkage cannot fund an eager edge
Maintainer review on #3298: the two-fact tolerance (containment + flat weight) never
checked WHERE the added modules came from, so a PR could statically import a heavy
merge-base module that sat outside the closure and pay for it by deleting top-level
statements elsewhere in the same closure. Both facts still held; real growth passed.
The tolerance is now three facts, all read from the graphs plus the merge-base tree:
containment, added-module NOVELTY (every head-closure module the base closure did not
evaluate is absent from the merge-base tree at its rename-canonicalized path -- checked
at the BASE path for a rename's head, so re-naming a closure-external module into the
closure is still pre-existing code newly made eager), and flat weight. classifyGrowth
requires all three and prints one reason per failed fact.
Planted, in the maintainer's exact shape: the split fixture plus a committed
pre-existing-heavy.ts pulled in by part-c, weight offset by merging the hub's two
remaining declarations -- containment holds, weight flat 3->3, novelty false, verdict
names shrinkage-funding. Unit-level pair for the fact itself, and rename-canonicalized
novelty pinned with and without the rename map against a colliding tree. Mutation:
dropping the novelty conjunct fails exactly the two planted novelty tests and nothing
else.
ADR 0027 (status, rules, decision, alternatives: the first draft is now a named refuted
alternative) and ADR 0028 step 3 state the three facts; the residue paragraph drops the
'true of any closure' claim.
* fix(eager-closure): novelty reads deleted-source content, not just git's rename pairs
Cubic review on b1cc79e:
- closure-growth-rule.ts:94 accepted a REWRITTEN move of a pre-existing external
module. -M rename detection is a similarity heuristic; a move that changes the path
and churns formatting reports as delete + add, so an absent destination proved
nothing. closureGrowthEvidence now also receives the statement texts of every
production source deleted since the merge-base (deletedSourcesSince runs --diff-filter=D
WITHOUT -M; texts come from the base tree through statementTextsOf, normalized so
comments/layout/export-keyword churn is invisible). An added module carrying two or
more identical statements of a deleted source is that source's code -- not novel.
Two, because a single shared line is coincidence at constant-declaration scale.
- ADR 0027 said rename canonicalization covered 'a rename'; the gate checks the
CANONICALIZED BASE path, which preserves modules already in the base closure and
reads a moved-in external module as newly eager. Status, rules, decision fact 2,
the rule-module header, and the verdict text now say exactly that; the second draft
(path-novelty alone) joins the refuted alternatives, pinned by the planted
rewritten-move test -- the evasion itself (-M reports D, not R) is asserted, so the
test cannot silently degrade into rename-handling coverage.
Mutation-verified: dropping the content comparison fails exactly the rewritten-move
test; checking the head path instead of the canonicalized one fails exactly the
moved-in test and the original rename test.
* docs(eager-closure): price the fifth git process and state the gate's actual check
Two review notes on PR #3298:
- The gate's cost comment still promised four merge-base git processes; the
novelty fact's fifth is the -M-less delete diff (deleted-source CONTENT comes
from the base tree's existing cat-file batch). Comment now enumerates all five.
- ADR 0027 stated the deleted-source guarantee absolutely. The gate checks
movedFromDeletedSource: two identical normalized statements from one deleted
source is the move signal, one is coincidence, and a rewritten transplant
evades a textual probe. The ADR, the rule header, and the refuted-directions
list now state exactly that boundary.
* docs(eager-closure): state deletedSourcesSince's real rename semantics
Approval-round note on PR #3298: plain `git diff` pairs renames by DEFAULT, and
--diff-filter=D drops the paired ones, so the docstring's "WITHOUT -M, so a
rename the similarity detector would pair stays visible" had it backwards --
the list covers only deletions pairing did NOT absorb, and paired renames are
answered by renamedSince. Captured the maintainer's companion constraint too:
dropping the default pairing (--no-renames) would put a re-homed hub in the
deleted set and refuse the very splits the tolerance exists for. Fixed at the
three siblings carrying the same claim: the function docstring, the rule's
deletedSourceTexts field doc, and the gate's cost comment.
* docs(eager-closure): state the deleted-source probe's threshold and evasion in deletedSourcesSince
PR #3298 review: the sentence "such a move stays non-novel" was unqualified
where the sibling docs are precise. The probe brands a move only on two or more
identical normalized statements with ONE deleted source; one shared statement is
tolerated as coincidence, and a statement-by-statement rewrite evades a textual
match. Containment stated as the structural refusal this probe reinforces.
* test(eager-closure): request rename pairing explicitly in the deleted-source probe
PR #3298 re-review: the probe leaned on git's DEFAULT pairing, so a
diff.renames=false gitconfig would have listed every re-homed split hub as
deleted source and refused honest splits. --find-renames now states the
requirement in the argv; verified in a scratch repo that the pairing survives
the hostile config while the old argv leaks the hub under it. Docstrings follow
the argv.
Copy file name to clipboardExpand all lines: docs/adr/README.md
+1-1Lines changed: 1 addition & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -28,7 +28,7 @@
28
28
|[0024 Screen Recording — One Coordinator, Two Facts (Proposed)](0024-screen-recording-provable-signal.md)|`record start`/`stop` lifecycle, the export-exists / recorder-stopped split, backend stop-and-collect contract, recovery through the coordinator, the ADR 0019 §5 amendment and the failed-finish cleanup fix |
29
29
|[0025 Foldable Apple Panels — Capture the Lit Panel](0025-foldable-apple-panels.md)| iPhone Duo outer/inner panels, CoreDevice display info as the only screen authority, why `simctl`'s implicit display default captures a black panel, derived (never asserted) pose, and why no pose-control command exists |
30
30
|[0026 Scroll Clip Authority — Ownership Is the Parent Edge](0026-scroll-clip-authority.md)| iOS scroll indicators and the visible band, why ownership is read from the parent edge instead of an ancestor walk, what may remove a node from an interactive snapshot, and ejection dispositions |
31
-
|[0027 Descriptor Root Size vs Eager-Closure Budget (Proposed)](0027-descriptor-root-vs-eager-closure-budget.md)|splitting `packages/command-registry/src/registry.ts`, the ADR-0019 eager-closure module-count budget, and why a byte-neutral hub split is currently unshippable|
31
+
|[0027 Descriptor Root Size vs Eager-Closure Budget](0027-descriptor-root-vs-eager-closure-budget.md)|accepted: the eager-closure budget tolerates a statement-neutral hub split only (containment + added-module novelty + flat top-level-statement weight) and nothing else; splitting `packages/command-registry/src/registry.ts` is unblocked|
32
32
|[0028 Capability-Family Cell Vocabulary — One Runtime Source (Proposed)](0028-capability-family-cell-vocabulary.md)| adding a capability operation family, `UnavailablePlatformRuntimeFacts` / `UNAVAILABLE_CELLS`, `INTERACTOR_OPERATIONS`, and why an eight-package fan-out recurs |
33
33
|[0029 Daemon Policy](0029-daemon-policy.md)|`AGENT_DEVICE_DAEMON_POLICY`, confining a daemon's commands, devices, or device shutdown, and where operator rules are enforced for batch/replay steps |
34
34
|[0030 Process Lock Exclusion](0030-process-lock-exclusion.md)| process-lock publication/reclaim/release, retained mutation guards, and the single-protocol upgrade boundary |
0 commit comments