cf266fcd cleanup(core): hash the plans affected already built instead of planning twice
Task-grained selection plans every candidate task, then the hasher planned the
surviving ones all over again. Planning costs about 3ms per task on this repo,
so the second pass was roughly a second on a 335 task graph, and it is what put
the task path 18% behind the project-grained one.
subsetHashPlans narrows an existing HashPlans to a task list. A plan is a vector
of interned instruction ids, so this filters a map and clones an Arc; the work
that made the plans is shared, not repeated.
It returns null when any requested task has no plan, which is the caller's
signal that the plans describe some other task graph. The hasher then plans for
real. That happens with an I/O snapshot bundle, whose plans are fetched after
selection and so describe different hashing, and after makeAcyclic, which drops
edges and changes which upstream outputs a task reads.
Verified hash-identical across all 276 tasks of a real affected run, reused
against freshly planned. Warm cache totals, median of three, this repo:
changed task before task after project
utils/path.ts 7449ms 6315ms 6291ms
ab-testing.spec.ts 6510ms 5102ms 6374ms
The wide case reaches parity, and the narrow case is 20% ahead because it also
hashes 233 tasks rather than 278.