Skip to content

fix(dev): #75 — completed MultiStats cohorts are the authoritative compile identity, no FIFO hook pairing - #83

Merged
ScriptedAlchemy merged 2 commits into
mainfrom
cursor/issue-75-completed-cohort-identity-87e7
Aug 31, 2026
Merged

fix(dev): #75 — completed MultiStats cohorts are the authoritative compile identity, no FIFO hook pairing#83
ScriptedAlchemy merged 2 commits into
mainfrom
cursor/issue-75-completed-cohort-identity-87e7

Conversation

@ScriptedAlchemy

@ScriptedAlchemy ScriptedAlchemy commented Aug 31, 2026

Copy link
Copy Markdown
Owner

Fixes #75. Follow-up to the Rsbuild 2.2.1 conformance audit (#72); this is the identity rearchitecture that #76 deferred.

Problem

The runtime compile observer allocated an attempt ID in the global onBeforeDevCompile hook and consumed IDs FIFO in onAfterDevCompile. Rsbuild documents lifecycle order but not a stable cycle ID, callback cardinality under coalesced invalidations, or one-to-one pairing when MultiCompiler children invalidate at different times. On top of that, the session's activation guard waited on every attempt barrier newer than the activating snapshot — so a onBeforeDevCompile whose completion never arrived (a coalesced or superseded invalidation) held an activation hostage until the bounded budget failed it loudly.

What changed

  • examples/rsc-agent-runtime/rsbuild.config.ts — the FIFO pendingAttemptIds queue, the duplicate-identity throw, and the "completed without a matching attempt" throw are gone. onBeforeDevCompile now only calls the advisory observeCompileStart(); it owns no identity, queue, or barrier. Every onAfterDevCompile callback allocates the next monotonic identity via beginCompletedCohort() at its start, in global completion order. Nothing pairs the two hooks, so there is no queue to mispair.
  • examples/rsc-agent-runtime/src/dev/rsbuild-runtime-session.tsAttemptBarrier, the #attempts map, #latestAttemptSequence, and #sequenceFor are removed. #beginCompletedCohort() allocates cohort-N ordinals at completion time. The activation guard's wait() no longer owns an attempt barrier: the completed-cohort ordinal (#latestRscCohortRevision) bumps synchronously with each completed changed cohort's callback, and supersession is fully decided by check() against completed ordinals and prepared-runtime authority. Pre-compile observation remains a bounded collapse hint: an in-flight activation waits for observed compiles only up to half the activation phase budget (then proceeds, never fails), so one settled edit keeps activating exactly one visible generation, while a completion that never arrives can only delay one activation once and self-heals at the next settled completion. In-flight compiles have no identity at all; failed and no-op completed cohorts still never bump the ordinal, so the flake: RSC runtime generation activation can wedge or go permanently stale after a repair rebuild (Node 24 CI, 2x on 2026-08-29) #38 commit-window protections (a live/failed/no-op later compile must not discard the newest successful one) are intact and covered by the existing commit-window tests.
  • The failed-attempt dedup, candidate cleanup (#candidatesByAttempt), compiler-asset checkpoint lifecycle, bounded activation phases from fix(dev): harden runtime relay against Rsbuild internals #76, and the writeToDisk copy path are all unchanged.

How this differs from #76

#76 kept FIFO identity and made its violations loud: a non-singleton pending queue or duplicate identity failed all live attempts and threw rather than silently misassociating, and a wedged activation failed within a bounded budget. This PR removes the FIFO identity entirely, so those failure modes cannot arise: identity is allocated per completed MultiStats cohort in completion order, pre-compile observation is advisory, and no activation ever waits on a pre-compile observation. The bounded activation budgets from #76 remain as a backstop for genuinely wedged store/registry phases.

New deterministic coverage

Plugin-level (driven directly against the observer hooks):

  • coalesced / missing / duplicated onBeforeDevCompile callbacks — identity derives only from completions, no failures;
  • duplicated and reordered completion callbacks — ascending fresh identity, unchanged-cohort no-op instead of a mispair;
  • skewed child invalidations — each distinct completed rsc/widget hash pair is its own cohort in completion order.

Real-session (via the intercepted compile observer on a live RsbuildRuntimeSession):

  • a completion with no paired pre-compile callback activates a generation;
  • a dangling pre-compile observation (completion never arrives) does not block the next completed cohort from activating;
  • an observed compile completing during an older activation's guard wait collapses that activation into the newer cohort (exactly one activated generation per settled edit);
  • a dangling observation during an in-flight activation delays it by at most the bounded grace, then commits with zero failed events;
  • redelivered identical MultiStats is a no-op with zero runtime.generation.failed events.

The two #76-era commit-window regression tests are retained (retitled/re-commented for the new model), as are all failed-attempt, no-op-attempt, and source-build-diagnostic tests.

No changeset — why

Every change is inside examples/rsc-agent-runtime (the private, unpublished @agent-bundle/rsc-agent-runtime-demo example runtime). No published package (agent-bundle, @agent-bundle/rsc-runtime, create-agent-bundle) changes behavior, so per the changeset convention there is nothing to version. (#76 needed a changeset because it also changed the published dev-server relay in agent-bundle.)

Out of scope (stayed in lane)

No changes to #73's reload channel, #74's per-environment output staging or onAfterEnvironmentCompile checkpoints, how live writeToDisk roots are copied, packages/agent-bundle/src/build/rslib.ts, build.test.ts, hooks.test.ts, or RuntimeClientSurfaceProxy frame parsing.

Test plan

  • pnpm build
  • pnpm typecheck
  • pnpm --filter @agent-bundle/rsc-agent-runtime-demo typecheck
  • rslint examples/rsc-agent-runtime (0 errors)
  • tests/dev-provider.integration.test.ts — 39/39 passed (includes the 4 new tests)
  • tests/generation-materializer.test.ts — all passed
  • full @agent-bundle/rsc-agent-runtime-demo suite — 181 tests: 175 passed, 6 skipped (env-gated native/eval tests, pre-existing), 0 failed
Open in Web Open in Cursor 

@changeset-bot

changeset-bot Bot commented Aug 31, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 0e0c095

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@pkg-pr-new

pkg-pr-new Bot commented Aug 31, 2026

Copy link
Copy Markdown
npm i https://pkg.pr.new/ScriptedAlchemy/agent-bundle@83
npm i https://pkg.pr.new/ScriptedAlchemy/agent-bundle/create-agent-bundle@83
npm i https://pkg.pr.new/ScriptedAlchemy/agent-bundle/@agent-bundle/rsc-runtime@83

commit: fa8cf22

@ScriptedAlchemy
ScriptedAlchemy marked this pull request as ready for review August 31, 2026 17:10
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Aug 31, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-08-31T17:14:21.077632Z c59db70 Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@ScriptedAlchemy

Copy link
Copy Markdown
Owner Author

Review against #75 / #39 (with stress evidence)

Design verdict: the completed-cohort identity redesign is sound — identity is allocated per completed MultiStats cohort in completion order, pre-compile observation owns no identity or barrier, supersession keys only on the monotonic captured-cohort ordinal plus prepared-runtime authority (equivalent prepared revisions still never supersede), and failed/no-op cohorts never bump the ordinal, so the #38/#39 commit-window protections held in review and under test.

One real regression found and fixed (605b2bc). Removing the guard's wait-on-live-attempts also removed a behavior PR #39 documented and the pinned suites rely on: while an older cohort's activation sits between its prepared phases and the commit check, a compile that starts in that window could no longer supersede it before commit. One settled edit could then activate two generations. Instrumented traces showed a spontaneous watcher recompile ~500 ms after a repair activation on both branches (beforeDevCompile with no file edit, distinct deterministic rsc hash); main collapses it via the old wait (11/11 HMR e2e runs green), while this branch double-committed — the HMR e2e's pinned "exactly one replay per recovered activation" failed with a 6th /api/runtime/runs POST, and the branch held only 7/15 HMR e2e passes and 1/5 capture passes locally.

The fix keeps #75's acceptance criteria intact: pre-compile observation is now a bounded collapse hint — every settled completion (captured, no-op, or failed) resets the observed-start count after its ordinal bump, and guard.wait() waits for observed in-flight compiles only up to half the activation phase budget before proceeding (it can delay, never fail). A dangling observation therefore delays at most one activation once and self-heals at the next settled completion — no wedge, no loud budget failure. Two deterministic real-session tests pin the seam (collapse during the guard wait; dangling observation commits after the grace with zero failed events).

Post-fix verification (all local, quiet 96-core host unless noted):

  • HMR e2e: 10/10 (8 plain + 2 under taskset -c 0,1 at CI time scale) — pre-fix 7/15, main baseline 11/11
  • runtime-playground capture: 3/3 — pre-fix 1/5
  • runtime-playground e2e: 2/2
  • dev-provider.integration.test.ts: 41/41 at CI time scale (includes the 2 new tests)
  • demo suite: 176 passed / 1 failed — the failure is state-and-definition.test.ts lease-timing, reproduced 3/5 failing on unmodified main under taskset -c 0,1 (also the current CI "Examples check" failure); pre-existing contention flake, unrelated to this PR
  • root typecheck, lint, test:unit, demo validate/build/typecheck: green

cursoragent and others added 2 commits August 31, 2026 22:32
Replace the FIFO attempt queue between the global onBeforeDevCompile and
onAfterDevCompile hooks with completion-allocated identity: every completed
MultiStats cohort allocates the next monotonic ordinal at the start of its
completion callback, and onBeforeDevCompile becomes a purely advisory
observation that owns no identity, queue, or activation barrier.

The activation guard no longer waits on live pre-compile attempts;
supersession keys only on completed cohort ordinals (bumped synchronously
with each completed callback) and prepared-runtime authority, so coalesced,
missing, duplicated, or reordered global callbacks can neither wedge an
activation nor associate Stats with a stale observation. Failed and no-op
completed cohorts still never bump the ordinal, preserving the #38
commit-window protections.

Refs #75

Co-authored-by: Zack Jackson <ScriptedAlchemy@users.noreply.github.com>
…h a bounded grace

Completed-cohort identity dropped the one job the pre-compile hook still
did for the activation guard: while an older cohort's activation sat
between its prepared phases and the commit check, a newly started compile
could no longer supersede it before commit, so one settled edit could
activate two generations (the doomed older cohort, then the newer one).
The runtime playground pins one automatic replay per recovered
activation, and its HMR e2e failed roughly half of local runs on this
branch while main held 11/11 - a spontaneous watcher recompile ~500ms
after a repair activation double-committed.

Pre-compile observation now counts as a bounded collapse hint: every
settled completion (captured, no-op, or failed) resets the count after
its ordinal bump, and guard.wait() waits for observed in-flight compiles
only until half the activation phase budget before proceeding. A
completion that never arrives can therefore delay one activation once and
self-heals at the next settled completion - it can never wedge or fail an
activation, preserving the #75 acceptance criteria, while the newest
completed cohort again supersedes a doomed older activation before it
becomes visible (#39's single visible activation per settled edit).

Two deterministic real-session tests cover the seam: an observed compile
completing during the guard wait collapses the older activation (one
activated generation, one transient failed event), and a dangling
observation with no completion commits after the grace with zero failed
events. Post-fix stress: HMR e2e 10/10 (2 under taskset -c 0,1), capture
3/3, dev-provider 41/41.
@cursor
cursor Bot force-pushed the cursor/issue-75-completed-cohort-identity-87e7 branch from fa8cf22 to 0e0c095 Compare August 31, 2026 22:35
@ScriptedAlchemy
ScriptedAlchemy merged commit 97e1257 into main Aug 31, 2026
8 of 9 checks passed
@ScriptedAlchemy
ScriptedAlchemy deleted the cursor/issue-75-completed-cohort-identity-87e7 branch August 31, 2026 22:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Make completed MultiCompiler cohorts authoritative without FIFO hook identity

2 participants