…get (#1355)
OpenCode's SQLite container, its WAL, and its storage archive share one recursive watch unit over the configured root, so the live container draws on the same shared recursive budget as the archive. A root reached after that budget is spent registers no native watch at all, and exhaustion inside the root's own walk marks the container's coverage degraded along with the archive's.
This separates each OpenCode-family root into a shallow container unit covering the database and its WAL and a recursive unit at `<root>/storage`. A shallow watch never draws on the recursive budget, so the container's coverage no longer depends on the archive's size. The split applies only where it is representable: a root with no storage directory keeps the single recursive unit, because naming an absent watch root would plan a polling obligation probed on a path that may never appear, and a probe that cannot be satisfied defers every other obligation on the same configured dir. A symlinked root also keeps it, since the daemon refuses to watch a recursive root through a symlink and gates the configured dir's reconciliation on the link target instead.
A native watch shared by overlapping roots is now charged to the shared budget once. `RecursiveWatchResult.Watched` still counts the directories a root covers; `Allocated` counts the watches a registration installed, and registration subtracts that. Reuse is settled before the budget check, so a root whose directories are already watched registers cleanly against an exhausted budget and records its ownership, and reclaiming a shared watch returns one budget slot rather than one per root that walked it.
Two units share one configured root and the engine dispatches every changed path once per emitted watch root, so `unitScopeAllows` scopes classification to the units of the root that owns the path; without it a WAL write would run the shared container's whole session listing twice. Configured roots can nest, so the deepest one containing the path owns it, and a virtual member path resolves to its physical container before the check. The container unit still claims storage paths: whether the storage unit exists is a filesystem fact, while the engine resolves each provider's watch roots once and reuses that set, so deferring would leave a storage tree created afterwards claimed by nothing. That costs one repeated source lookup per storage event, which the engine's per-pass source set discards; the database fan-out cannot double, because a storage watch root never contains the database or its WAL.
This PR stands alone and supersedes PR #1318, whose conditional storage-unit split covered the same provider topology. Portable lifecycle ownership of a missing watch root is not attempted here. The bounded parser feed that motivated PR #1331 is abandoned rather than shipped: OpenCode exposes no producer-owned cross-drain journal cursor, so that work was closed and this slice is the salvage.
Refs #1208
Co-authored-by: Rod Boev <rodboev@users.noreply.github.com>
Recurring OpenCode work is scheduled per root, so its cost follows the container instead of what actually moved. Any write to the shared database breaks the freshness trust that lets a pass skip unchanged sessions; the next pass rebuilds the per-session digest over every message and part row, and each watcher batch that advances a session's update time re-parses that session's entire history. OpenCode publishes a durable event journal recording which sessions changed and which finished changing. This makes that journal the unit of scheduling: with 5,000 stored sessions and one changed, the recurring pass previously evaluated all 5,000 sessions to skip 4,999, while the journal drain reads one row and returns one session identity.
What this changes:
Session lineage and termination classification are unchanged, and providers without the capability construct no adapter. Journal checkpoints are in-memory, so a restart rebaselines under startup reconciliation. Pure-SQLite OpenCode roots remain in the startup and daily audit scope when storage is absent, while poll-path deletion detection for admitted containers still defers to the daily audit.
Based on #1318; the diff collapses to this change alone when it lands.
Closes #1208