Skip to content

Bug sweep — packages/ts

State: open/active · Genre: analysis

A deep, adversarially-validated bug hunt over packages/ts, run one package at a time, bottom of the dependency stack first.

The earlier attempt ran the whole tier at once in two batches. It did not finish: 12 of 36 territories scanned, every validation agent lost to a session limit, and 314 candidates left unconfirmed. Package-at-a-time replaces it because one package fits inside a single session’s budget, which is what makes the deep rounds reachable at all.

packages/ts is three layers deep with no cycles. Sweeping a leaf first means its fixes are merged before anything downstream is scanned, so a dependent’s scan reads the corrected code and does not re-report an upstream defect as its own. It also front-loads blast radius: a bug in command-seam reaches five packages, one in markdown-ui reaches none.

Order within a layer is by transitive dependents, then by size.

Each package is one pass through the bug-finder skill, scoped to that package:

  1. Scan — three altitudes (in-function, in-module, in-library), bomb rounds, with known seeded from what previous runs already found so early rounds do not re-report fixed bugs.
  2. Validate — adversarial refuters per candidate, majority to survive. Findings that skip this step are candidates, not bugs.
  3. Rate — severity and reachableToday (yes / only-for-consumers / no).
  4. Fix — one worktree per package, regression test per fix.
  5. Gate — the package’s own scripts.test AND scripts.typecheck. A green suite is half the gate.
  6. Land — one PR per package, merged before its dependents are swept.

Legend: ☐ not started · ◐ scanned, not validated · ☑ landed

files counts non-test .ts/.vue/.js sources. dep is transitive dependents inside packages/ts.

L0 — leaves (no packages/ts dependencies)

Section titled “L0 — leaves (no packages/ts dependencies)”
packagefilesdepPRnotes
☐command-seam35highest blast radius in the tier
☐markdown-util14never had a territory in the old plan
◐obsidian-md50337 candidates, unvalidated
◐ui53127 candidates, unvalidated
◐json-canvas-core37134 candidates, unvalidated
☐agent-call131
☐intersect131shared corpus is include_str!’d by the Rust port — a TS-only fix can break packages/rust/intersect
☐panes111
☐system-model61
☑node-tree51#18381 fixed: an invalidated in-flight fetch clobbered the retry’s fresh page; per-branch write generation
☐flowline90
☑fleet50#18340 bugs — nothing survived refutation; 3 doc corrections and one env-injecting test fix
☐graph-canvas50
☑yaml-splice40#18372 fixed: non-finite numbers silently written as null; assertSingleSpan asserted only tautologies
☐dashboard-client20never had a territory in the old plan
—prettier-config00config only, nothing to scan
—tsconfig00config only, nothing to scan
packagefilesdepPRnotes
☐easy-git112
☐obsidian-vault32
◐obsidian-editor51149 candidates across two shards, unvalidated
◐detect-runners44147 candidates across two shards, unvalidated
◐app-frame90041 candidates across two shards, unvalidated; largest package in the tier
◐json-canvas-vue23027 candidates, unvalidated
☐agent-plugin160
☐manifest-graph80parallel port of solutions/ontological/crates/manifest-graph; examples/dump.rs diffs the two, and no shared corpus makes a TS-only drift go red
packagefilesdepPRnotes
◐markdown-ui61021 candidates, unvalidated
◐obsidian-bases20031 candidates, unvalidated
☐waypost170
☐agent-dev100never had a territory in the old plan
☐easy-gh60
☐vault-graph-core60

Calibration — what the two batches actually cost and yielded

Section titled “Calibration — what the two batches actually cost and yielded”

Six packages swept package-at-a-time, 47 agents, no session limit hit.

batchpackagescandidatesconfirmedfixedagents
1markdown-util, dashboard-client, command-seam215524
2yaml-splice, fleet, node-tree3111323

Three numbers worth carrying forward.

  • Refutation kills roughly three quarters of candidates. 52 raw to 16 confirmed. The earlier tier-wide run’s 306-to-137 was a gentler filter; three refuters defaulting to refuted: true is the stricter and better gate.
  • The land agent is a second filter, and it earns its place. 16 confirmed became 8 fixed. Five of the eight declines were bugs already fixed on main that the scan re-reported from stale line numbers, and two did not reproduce when probed — one of those would have added a list() per open branch on every scope change, the other would have hidden legitimate failures. Telling the land agent it may refuse a confirmed finding is what caught them.
  • A package that was just swept yields little. fleet confirmed nothing. That is the correct result, not a failed run, and the brief has to say so or the agent manufactures work.

Dedupe by file:line:title does not catch one bug reported three ways: three yaml-splice altitudes each found the non-finite-number defect at the same line under different titles, and all three survived to the land agent as separate findings. Dedupe on the failure, not the label.

The abandoned tier-wide run left 314 raw candidates over 12 packages, saved at deep-a.json in the session scratchpad. They are unfiltered: on the first sweep 306 raw candidates became 137 after refutation, and a later calibration pass downgraded 18 severities against 2 upgrades. Scanner self-ratings run optimistic.

When a ◐ package comes up, validate its carried candidates first and seed the scan’s exclusion list with them, rather than re-scanning cold.

Each of these came from a defect this sweep shipped and had to repair.

  • A green suite is half the gate. scripts.typecheck is a separate CI job from scripts.test. A test file that passes at runtime can still fail tsc.
  • A test belongs in the package that owns the code it pins. Mocking a bare specifier the test’s own package does not declare intercepts the module only if both resolve to the same copy — it mocks correctly on a dev machine and silently mocks nothing in CI.
  • Wait for the condition, not for a tick count. A fixed number of nextTick/setTimeout rounds races a lazy import; a loaded CI runner wins.
  • Check the territory boundary before fixing. A TS package with a Rust port or a shared fixture corpus can be broken by an edit that keeps its own suite green.
  • Never add vite as a direct devDependency of a packages/ts/* package. It makes solutions/ontological/desktop/bun.lock never reach a fixed point.

← Back to Notes