Repository navigation
Conversation
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configuration
You can disable this status message by setting the Use the checkbox below for a quick retry:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
aaf2tbz
force-pushed
the
feat/2040-concurrent-scope-passes
branch
3 times, most recently
from
October 8, 2026 17:04
a6345f0 to
b6f06ee
Compare
aaf2tbz
force-pushed
the
fix/2094-dreaming-review-cursor
branch
from
October 8, 2026 18:29
5f95f10 to
7768322
Compare
aaf2tbz
force-pushed
the
feat/2040-concurrent-scope-passes
branch
from
October 8, 2026 19:15
a0f55d0 to
3c9b588
Compare
Dreaming ran at most one pass per agent scope, so a scope with a large backlog drained one pass at a time no matter how high maxConcurrentPasses was set. Two passes in one scope would have been handed the same sources by the delivery queue, which only learns what was consumed when a pass finalizes. memory.dreaming.maxPassesPerScope (default 1, so behavior is unchanged) lets an incremental pass share its scope. Such passes lease the sources they take from the delivery queue in dreaming_evidence_leases; a lease is taken in one owner write that refuses a source another running, unexpired pass holds, and the queue skips sources leased elsewhere. A lease lapses when its pass leaves running (finalize, failure, cancellation, startup recovery) or after twice the pass timeout, so a failed pass's sources are delivered again. Finalization does not settle a source another running pass holds, even if this pass looked it up. The worker gives a scope whose backlog reaches tokenThreshold extra passes from slots left after every scope with work has one. Only the first pass on a scope works its pending attention. Content and hygiene passes, which commit the memory head, still run alone. Claim ordering already follows evidence time; when only one of two contradicting claims carries an event time, the later-captured source now decides instead of the write order. dreaming_state.last_pass_at no longer moves backward when passes finish out of order. Refs #2040 Assisted-by: Claude-Code:claude-opus-5-5
Assisted-by: Claude-Code:claude-opus-5-5
A shared pass took a scope's pending attention only when no other pass held the scope. Under a continuous drain (the bench loop triggers whenever a slot frees), the scope always had a running pass after the first one finished, so contested claims, review_due and surprisal records waited until the whole backlog drained. Track which running pass owns each scope's attention. A joining pass takes attention for scopes no running pass owns, so exactly one pass works it at a time and the next pass to start after the owner finishes takes it over. Assisted-by: Claude-Code:claude-opus-5-5
The backlog probe stops after 50 sources and reports an indeterminate count below tokenThreshold, so a scope with many small pending sources never qualified for maxPassesPerScope extra passes. An indeterminate probe that found backlog has more pending than it read, so it now qualifies. The default of one pass per scope is unchanged. Assisted-by: Claude-Code:claude-opus-5-5
A pass that lost a lease race got a short or empty queue page with hasMore true, which a model can read as the end of the queue. The search_evidence tool now re-drains, up to four times, skipping sources other passes leased, so the pass gets the next unclaimed sources. When other passes hold everything left, the page is empty with hasMore false and heldByOtherPasses true plus a note to finish. Assisted-by: Claude-Code:claude-opus-5-5
aaf2tbz
force-pushed
the
feat/2040-concurrent-scope-passes
branch
from
October 8, 2026 19:57
3c9b588 to
7b428ae
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Dreaming ran at most one pass per agent scope.
partitionDreamingScopes(platform/daemon/src/pipeline/dreaming-worker.ts) only spread passes across scopes, so one large scope drained one pass at a time no matter how highmaxConcurrentPasseswas. The blocker the issue names is real: the delivery queue (drainDreamingEvidenceQueueInDb) learns what was consumed only when a pass finalizes, so two passes in one scope would be handed the same sources.This PR implements the issue's proposal behind a new opt-in setting:
dreaming_evidence_leases, migration 170). The lease is taken in one owner write per source whose upsert refuses a row held by another pass that is stillrunningand unexpired, so two passes racing for one source cannot both win. The queue skips sources leased by another live pass. A pass that loses a race re-reads the queue (up to four times) and gets the next unclaimed sources instead. When other passes hold everything left, its page is empty withhasMore: falseandheldByOtherPasses: true, and a note tells the model to finish. A lease ends when its pass leavesrunning(finalize, failure, cancellation, or startup recovery marking it interrupted), or after twice the pass timeout as a crash backstop. A failed or cancelled pass's sources are delivered again. Finalization does not settle a source another running pass holds, even if this pass looked it up bysourceRef, so the holder keeps it and a failed holder's sources stay queued.577f87200,6c0d931f4): an incoming claim whose slot has an active claim with newer evidence lands as superseded. One gap remained: when only one of the two claims carried an event time, the later write won. With newest-first delivery or concurrent passes, "lives in Austin" (no date, older session) written after "moved to Denver in May 2024" (dated, newer session) became current. Now event times decide only when both claims have one; otherwise the later-captured source wins.memory.dreaming.maxPassesPerScope> 1, a scope whose measured backlog reachestokenThreshold, or that has more pending sources than the 50 the backlog probe reads, gets up to that many incremental passes. They come only from slots left after every scope with work has a pass, and they stay withinmaxConcurrentPassesand the shared LLM limit. A scope can also be joined while its earlier passes still run.Fixes #2040
Changes
platform/daemon/src/pipeline/dreaming-evidence-leases.ts(new): lease acquire, live-lease lookup, and release at finalize. Release also clears rows left by passes that are no longer running.platform/daemon/src/pipeline/dreaming-capabilities.ts: the queue drain excludes sources leased by other live passes, both fresh sources and continuations.search_evidenceleases a drained page when the pass hasevidenceLeaseMs, and re-drains when it loses part of the page to another pass. An empty queue page reportsheldByOtherPasses: truewhen other running passes hold leases in the scope. The tool description says whathasMoreandheldByOtherPassesmean when passes share a scope.platform/daemon/src/pipeline/dreaming.ts:DreamingPassLiveOptions.sharedScope.attentionScopes. The worker gives a scope's attention to one running pass at a time: a joining pass gets it only when no running pass owns it, so two passes never work the same attention record, and the next pass to start after the owner finishes takes it over.resetDreamingTokensno longer moveslast_pass_atbackward when passes finish out of order.platform/daemon/src/pipeline/dreaming-worker.ts:tokenThresholdor isindeterminatewith backlog (the probe hit its 50-source cap, so more is pending). The scheduler trigger and the other probe consumers are unchanged.partitionDreamingScopestakes an optional per-scopepassescount and adds extra same-scope groups round-robin into spare slots, after attention-only scopes.closedScopes()replacesleasedScopes()for admission. Exclusive, directed, and user-request passes still close their scopes.platform/daemon/src/ontology-proposals.ts: the mixed event-time ordering fix described above.platform/core/src/types.ts,platform/daemon/src/memory-config.ts,platform/daemon/src/routes/pipeline-routes.ts:maxPassesPerScope(optional, default 1, clamped 1..16), reported inGET /api/dream/status.platform/core/src/migrations/170-dreaming-evidence-leases.tsand the registry.web/docs/src/content/docs/api/knowledge-ontology.md: the setting, when a scope qualifies, leases and lost lease races, attention ownership, and the ordering rule.scripts/event-loop-contract-baseline.jsonregenerated.Type
feat— new user-facing feature (bumps minor)fix— bug fixrefactor— restructure without behavior changechore— build, deps, config, docsperf— performance improvementtest— test coveragePackages affected
@signet/core@signet/daemon@signet/cli/ dashboard@signet/sdk@signet/connector-*@signet/webpredictorMigration Notes (if applicable)
Migration 170 only creates
dreaming_evidence_leasesand its index (IF NOT EXISTS). An older daemon ignores the table. WithmaxPassesPerScopeat 1, nothing writes to it.Testing
bun testpasses. I ran the touched suites:src/pipeline/,src/ontology-proposals.test.ts,src/memory-config.test.ts,src/episodic-sources.test.ts,src/startup-recovery.test.ts,platform/core/src/migrations,scripts/audit-event-loop-contract.test.ts. The only failures werecreateAcpxProvidercases insrc/pipeline/provider.test.ts. They spawn ACPX processes and do not touch this code. Oneimported-source-lifecyclecase also failed in the combined run because the custom SQLite was already loaded; it passes alone.bun run typecheckpasses (pre-commit typecheck of all affected workspaces)bun run lintpasses (biome checkon changed files: no new warnings;comments:checkis current)New tests. Each fails without the change, except the two default-behavior guards:
dreaming-evidence-leases.test.ts:hasMore: falseandheldByOtherPasses: true, and so is a later pass's page.runDreamingAgentPassruns in one scope each settle only their own sources, release their leases, and only the first sees pending attention.dreaming-worker.test.ts:maxPassesPerScope: 3, one large scope runs three concurrent passes whose delivered sources never overlap, and every delivered source is settled once.tokenThreshold) gets three passes withmaxPassesPerScope: 3.tokenThresholdkeeps one pass even when sharing is allowed.ontology-proposals.test.ts: a dated newer claim stays current when an undated claim from an older source is written after it.After the rebase onto #2122's rebased head (
776832220) and the two fixes below, I re-ran withSIGNET_SQLITE_PATHset to Homebrew SQLite:dreaming-evidence-leases,dreaming-worker,dreaming,ontology-proposals,episodic-sources(249 pass), core migrations (84 pass),scripts/doc-drift-packagesandscripts/audit-event-loop-contract(25 pass), plus the other suites that callsearch_evidence(49 pass). Daemontscis clean,comments:checkis current, and the event-loop baseline had no drift.Not run: the BEAM 1M and LongMemEval acceptance runs from the issue. They need the benchmark harness and a live provider. The bench scripts set
maxConcurrentPassesbut not yetmaxPassesPerScope.Live verification. I ran daemons built from this branch before the rebase (head
a0f55d027) and frommain(ea8e8b569, the merge base), each with an isolatedSIGNET_PATH/HOME. Dreaming inference went to a local OpenAI-compatible stub that drives each Pi-executor pass with a fixed script:search_evidencequeue pages of 5,review_evidencefor each page, one extra page left unreviewed, onecreate_entity, then stop. The stub held each reply for 1.5 s so passes overlapped. The corpus was 120 imported transcripts (about 1.6k chars each) in thedefaultscope. No real providers were called.maxPassesPerScopeunset,maxConcurrentPasses: 3): five concurrentPOST /api/dream/triggerrequests returned one 202 and four 409s, the same as onmain. A 500 s trigger loop never had more than one pass running.GET /api/dream/statusreportsmaxPassesPerScope: 1.maxPassesPerScope: 3,maxConcurrentPasses: 3,maxLlmConcurrency: 4: one trigger started three passes in the one scope, and further triggers got 409 until a slot freed up. In a 120 s loop, 27 passes ran, three at a time. I checked the stub log at millisecond precision: no source was delivered to a pass while another pass that already had it was still running (0 of 540 deliveries). A once-per-second sample ofdreaming_evidence_leasesshowed up to three passes holding leases at once and never one source leased by two passes. Every pass finalized and no lease rows were left over. The cursor moved only for reviewed text: the unreviewed extra page of each pass stayed queued (stalled_passes1) and went to a later pass.5f95f104b(now0afbb2384, which this rebased branch includes) keeps re-delivering the tails of reviewed imported transcripts, so the queue never empties. To time a full drain, I ran a scratch copy of this head with only that fix applied. One pass at a time took 114 s to drain all 120 transcripts (8 passes). Three per scope took 41 s (9 passes). Both runs ended with every source consumed and none stalled.failed, its lease rows stopped counting as live, and all ten sources it had been given went to the two passes still running within 2 s.contested_claimattention row while triggers kept three passes running. Only one running pass had it in its<pending_attention>at any time. Passes that joined while the owner was running sawnone pending. The first pass to start after the owner finished took it over. Across four rounds the owners' run times never overlapped.main: I seeded a workspace withmain(schema 168), ran passes there, then started this branch on the same workspace. Migrations 169 and 170 applied at boot with no errors. Three passes per scope then ran correctly on the upgraded data. With the fix(dreaming): advance evidence cursor only by reviewed text #2122 fix in place, they drained the 60 remaining transcripts and did not re-deliver the 60 thatmainhad consumed.Workspace migration is drainingretry at shutdown thatmainlogs too.tokenThreshold, but the probe counts at most 50 sources. A scope whose backlog is many small sources never got a second pass:/api/memory/rememberstores chunks of about 600 chars, so 900 pending memories got one pass withmaxPassesPerScope: 3(50 sources come to about 8k tokens, below the 10k minimum threshold). Fixed in426eec844: a probe that hit the source cap with backlog now qualifies. WithmaxPassesPerScopeat 1 nothing changes.items: []withhasMore: true, and a model that reads an empty page as the end of the queue stopped early (my first stub script did). Fixed in3c9b588c3:search_evidencere-drains and returns the next unclaimed sources. It returns an empty page only when other passes hold everything left, and then says so withhasMore: falseandheldByOtherPasses: true.AI disclosure
Assisted-bytags in commits)Notes
Stacked on #2122. This branch is rebased onto #2122's current head (
776832220, which is itself rebased onto main) and is based onfix/2094-dreaming-review-cursorbecause #2122 takes migration 169 and the migration registry requires consecutive versions. This PR uses 170 and must merge after #2122. If #2122 is squash-merged, this branch needs a rebase onto main.How it interacts with #2122. #2122 moves the evidence cursor only by acknowledged (
review_evidence) or cited text, and counts stalls for queue-delivered revisions with no progress.sourceRefor query lookup, which fix(dreaming): advance evidence cursor only by reviewed text #2122 does not mark as queued, so it never counts as a stall here.deferredEvidence. Neither this pass'sreview_evidenceacknowledgements nor its citation floors can advance a cursor the holder owns. The holder's own review and citations advance it as usual.Decisions made for the maintainer:
maxConcurrentPasses. The issue says a scope "gets up tomaxConcurrentPassespasses", but the product default for that setting is 2. Reusing it would change behavior for every single-scope install. I addedmaxPassesPerScope(default 1), still capped bymaxConcurrentPasses. Happy to fold it intomaxConcurrentPassesif you'd rather turn this on by default.tokenThreshold, so I used "measured backlog reachestokenThreshold". Because the probe stops after 50 sources, a probe that hits that cap with backlog also qualifies, so many small sources get extra passes too.Known limits:
attention_list.incremental-contentorincremental-hygiene, which are exclusive, so extra passes per scope come fromPOST /api/dream/triggerwithmode: "incremental"(what the bench drain loop sends), not from the scheduler./api/dream/tools/:capability(ACPX MCP targets) do not lease evidence. That route also does not persist tool calls, so queue progress for ACPX targets is already limited.Expected conflicts:
platform/daemon/src/memory-config.tsin different hunks andscripts/event-loop-contract-baseline.json. The baseline needs a regenerate on rebase.