OneDrive walk concurrency — implementation plan
Status: In progress · Author: Claude (NOS-211 orchestrator) · Date: 2026-07-23 · Repo: north-os
Related: 2026-07-23-onedrive-walk-concurrency-design.html (the spec — read it first; §1–§4 define the invariants verbatim) · Linear NOS-211
Work item 1 — agent-runtime: level-batched BFS + parallel fan-out/widen + cancellation
Files: packages/agent-runtime/src/tools/matter-drive-documents.ts (+ test file), optional sibling helper walk-limiter.ts (+ test). No public tool API change.
WALK_CONCURRENCY = 6; a small promise-based FIFO counting semaphorecreateLimiter(max)whose queue is cancellation-aware (a queued task checks the pool's stop state when its permit arrives and returns without running).searchMatterTree: mapped roots serial in DB order; within a root, deterministic BFS level batches — take frontier FIFO, submit up to the batch to the limiter, and per node inside its permit, synchronously: stop/deadline/signal check → per-rootseencheck+insert → budget check-and-decrement →visitedCount++→ startlistChildren. Settle the batch (allSettled), assemble children in parent order (hits recorded in assembly order — insertion order identical to the serial walk), refill the frontier, repeat.- Pool-local
AbortControllerper walk chained to upstreamsignal+deadlineAt. Target found / substantive error / deadline → stop admission, abort in-flight, await settle, then return/throw. Substantive (non-abort) error wins over cancellation noise. Timebox settles{ complete: false, interruption: "timebox" }; budget exhaustion"walk_budget". - Return
visitedCount(= budget actually consumed by this walk). Accept an optional shared limiter so one tool call spans all its walks with ≤6 total in-flight listings. - Thread
config.signalinto the single-matter search (:1487) andfindDocumentUnderMatter(containment walk) — today they pass none. - Multi-matter branch (
:1255): resolve all chosen matters serially, in input order, before any walk (deadline checks as today → preserves the pinned unauthorized-user barrier and the delayed-RLSinterrupted_matter_idbehavior; resolution-only outcomes still count completed). Then walk the authorized matters concurrently under the ONE budget + limiter. Assemble sections in input order after settling. Envelope per spec §2: completed / pending /interrupted_matter_id= first started-incomplete in input order /interruptedMatterWalkItems= that walk'svisitedCount/ terminal detection unchanged /meta.resultIds+ coverage key restricted to completed scopes. collectConnexWiden: siblings resolved serially through the disclosure gate (unchanged), then walked concurrently under the same shared budget + limiter;blocks/matchesassembled in candidate order.
Tests (extend matter-drive-documents.test.ts): the gate list in spec §Gates, notably: serial-prefix visit subset at budget exhaustion; slow same-depth sibling (fast branch must NOT descend early — level barrier); limiter-queued node never starts after deadline; deadline during RLS resolution; error-while-sibling-hangs → abort + substantive error; target short-circuit with queued+in-flight siblings (no orphan); upstream cancellation via single-matter + containment paths; multiple incomplete matters envelope + terminal + section order + resultIds restriction; unauthorized in batch → zero OneDrive calls; equal-key hits across overlapping roots; global ≤6 concurrent listings (instrument the fetch mock with an in-flight counter). Concurrency-affected tests must use URL/query-aware fetch + SQL mocks, not mockResolvedValueOnce sequences; convert the fan-out/widen tests that break under reordering (update only order-encoding assertions — semantics stay pinned, except the deliberately-updated "solo retry after prior matters consumed the budget" distribution test per spec §2).
Work item 2 — connectors-api: /children bundle cache + Graph cancellation
Files: new apps/connectors-api/src/lib/bundle-cache.ts (+ test), apps/connectors-api/src/routes/drives.ts, apps/connectors-api/src/lib/drives.ts (signal threading).
createBundleCache({ fence, ttlMs: 30_000, maxEntries })— fence function injected for testability (defaults togetRefreshedBundle). SeparateinFlightandvaluemaps; TTL stamped on successful settle; in-flight joined even past TTL; rejection propagates to all joined waiters and evicts with identity comparison (never a newer replacement); errors never cached; expired entries pruned opportunistically on access + hard entry cap. A waiter's abort must not cancel the shared fence run.ConnectorNotFoundErrorpasses through untouched.- Wire into the
/childrenroute only (drives.ts:121 call site). The other four call sites keep per-request fences. - Cancellation:
/childrenpassesc.req.raw.signal→listDriveItems(bundle, driveId, itemId, { signal })→graph.api(path).options({ signal }).get()(supported, SDK v3.0.7). Optional param — other callers unchanged. - Tests (
bundle-cache.test.ts+ a drives route test): concurrent gets → one fence run; TTL-from-success expiry → re-fence; slow fence past TTL still joined; rejection → all waiters reject, next get retries, identity-safe eviction; distinct keys isolated; entry-cap pruning; route test: aborting the request propagates the signal into the (mocked) Graph fetch.
Worker split
- omp worker A: work item 1 (agent-runtime only).
- omp worker B: work item 2 (connectors-api only).
- No shared files; run in parallel. Orchestrator owns git, reviews, gates.
Gates & benchmark
bun --filter=@workspace/agent-runtime test, connectors-api tests, rootlint+typecheck.- Live before/after (probe: dispatcher's
walk-bench.ts): before = main code + shared connectors-api — SUSS-117 34.7 s / ELHM-150 27.6 s / fan-out×4 42.4 s / TESX-102 1.2 s. After = worktree agent-runtime + worktree connectors-api on a side port (CONNECTORS_API_PORT=8181,CONNECTORS_API_URLoverride). Table in the PR body; small-matter non-regression required.