Skip to content

fix!: align task lifecycle and runtime contracts - #196

Merged
jerome-benoit merged 10 commits into
masterfrom
fix/worker-contracts
Oct 19, 2026
Merged

jerome-benoit merged 10 commits into
masterfrom
fix/worker-contracts

Conversation

@jerome-benoit

@jerome-benoit jerome-benoit commented Oct 17, 2026 •

Copy link
Copy Markdown
Contributor

PR Checklist

  • Conventional Commit title; linked bug reports.
  • Consumer regressions and operational documentation updated.

Does this PR introduce a breaking change?

  • Yes
  • No

Deferred worker overrides must echo taskFunctionOperationId, or retain the original taskFunctionProperties reference through the standard transport. Replies without recoverable identity reject after the original cohort settles; identities are never guessed. Migration and runtime constraints.

Upstream alignment

Ports poolifier/poolifier#3360 at a9c3ce0832093df258e946f244dbb6c718456e8f and #3362 at 778fb7adf12ff9ab6445d229f08bc0ad059a6837 onto the existing port fixes.

  • Preserve cancellation reasons, pre-abort checks and one-shot signal/transfer-list snapshots.
  • Match promise/thenable normalization, cancellation gates, cleanup ACKs and fatal transport-error handling.
  • Snapshot registration messages; serialize mutations against their original target cohort. Preserve buffered outcomes and restore registrations, selected defaults and scheduling before replacement readiness. Failures remain nontransactional.
  • Preserve transferred-input error identity without returning ownership; retain worker-side mutations for nontransferred inputs.
  • Align weighted affinity selection, PromiseLike types, declaration consumers and affinity documentation.
  • Check JSR publication on Ubuntu before bundling, using the port's release CLI. The existing explicit structural context annotation already covers #3362's type fix; no source rewrite or documentation addition is needed.

Independent pool and worker reviews repeated after both ports, with four AST comparisons. Shared algorithms follow upstream identifiers, predicates and helper structure. Remaining differences are classified Web/runtime boundaries or preserved port contracts; no unexplained semantic drift found.

Port-specific boundaries

  • Keep EventTarget/MessageEvent errors, UUID worker IDs, protected numeric listener/execution hooks, awaitable cleanup hooks, secure launch options and permission-lazy CPU weights.
  • Pre-register the original cohort exit callbacks before protected Web dispatch hooks; refresh captured-owner indices after listener registration in both cohort and single-worker replay dispatch to handle synchronous retirement without targeting replacements. Shared ACK and send-error semantics remain unchanged.
  • Deno/Bun use native AsyncLocalStorage, promise branding and synchronous queued-port reads. Web exit consumers drain before channel closure, including reentrant retirement and cleanup ACKs.
  • Browsers retain explicit/original-properties UUID correlation, synchronous context and safe promise normalization. Optional prototype/own-property probes do not reject otherwise valid thenables. No synchronous queued-message recovery or asynchronous-context guarantee is fabricated; modified-constructor promise limits are documented.
  • Keep native TypeScript declarations and DOM/WebWorker consumers. No Node cluster/build cutover, new public exports/options or redundant aliases. README changes only the affinity link.

Verification

Check Result
Full Deno coverage (2310e523) 27 suites, 489 passing steps, zero failures, 9 ignored steps
Format, lint, browser/Bun builds, DOM/WebWorker declaration consumers Passed
JSR publication validator Dry run passed. Isolated removal of the context annotation fails with missing-explicit-type; restoring it passes. No upload or slow-type bypass
Independent native Deno 2.9.7 / Bun 1.4.2 69/69 each; genuine literal local/foreign Worker entrypoints
Reentrant native capture callback boundary 2/2 each runtime; real queued task/ACK messages, directly entered error callback
Actual Chromium 155, fresh browser bundle 32/32, including counterfeit/proxied promise task and cleanup cases
Published Bun ESM artifact 27/27 public-API scenarios
npm pack --dry-run Complete 40-file package and declaration tree
GitHub Actions CI (2310e523) 8/8 jobs passed, including JSR publication validation
Overall checks 14/15; Sonar S7739 blocks the intentional upstream thenable adapter. Evidence and required Sonar triage
Single-worker replay follow-up (2310e523) 73 focused regression steps pass. Deno/Bun/Chromium each pass three replay controls; pinned upstream #3360/#3362 threads/cluster pass all 12 equivalent controls. Source, attribution and proof
Fresh review Two shared-upstream P2 cases confirmed on Deno/Bun/Node: captured affinity and mutation ordering after a send exception. Both remain open, outside the CI-only #3362 follow-up

Closes #189, closes #190, closes #191, closes #192, closes #193, closes #194, closes #195, closes #197.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 17, 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-10-18T23:54:40.063059Z — New commits
ℹ️ 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.

Comment thread src/pools/utils.ts Fixed

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c84bb84d69

ℹ️ 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".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/worker/abstract-worker.ts Resolved

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e5cef1cb90

ℹ️ 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".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/pools/abstract-pool.ts Resolved
Port poolifier/poolifier#3360 at a9c3ce083 while preserving Web events, worker UUIDs, protected hooks and runtime-specific contracts.

BREAKING CHANGE: deferred task function operation handlers must retain taskFunctionOperationId or the original taskFunctionProperties reference. Replies without recoverable identity reject after the original cohort settles.
@jerome-benoit jerome-benoit changed the title fix: correct task, worker and runtime contracts fix!: align task lifecycle and runtime contracts Oct 18, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: cadd7ec827

ℹ️ 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".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +1808 to 1809
private chooseWorkerNode(name?: string): number | undefined {
const workerNodeKeys = this.getTaskFunctionWorkerNodeKeys(name)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Use captured affinity when live metadata is missing

In a dynamic pool with min = 0, executing a runtime function restricted to a later index (for example [2]) initially creates worker 0 before any live metadata exposes that affinity. If replay fails on that ineligible worker but leaves it alive, waitForWorkerTaskFunctionBootstraps() correctly suppresses the failure, yet this lookup still returns no affinity because it only consults worker metadata. Since the pool is then neither empty nor busy, no additional dynamic worker is created and selection rejects instead of reaching worker 2. Fall back to the captured taskFunctions registration here so an ineligible replay failure cannot prevent creation of the eligible worker.

AGENTS.md reference: AGENTS.md:L105-L112

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confirmed P2 on real Deno 2.9.7, Bun 1.4.2 and Node 26.11.1 workers. This is shared upstream behavior, not a port regression: the same lookup/growth path exists at a9c3ce083:1800-1849.

Public registration with min=0 and affinity [2] succeeds. After worker 0's real replay validation failure, execution exhausts six selection retries; only key 0 exists and a subsequent native round trip proves it remains alive. All three no-fault controls create keys [0,1,2] and execute on actual worker 2. The replay failure is deliberately introduced through standard worker validation, not a fabricated ACK. Commands and six logs are retained under /tmp/pr196-captured-affinity/.

Minimum correction: recover missing runtime affinity from the captured registration before growth/eligibility selection, reusing existing name/default resolution. This remains open; the faithful #3362 follow-up changes only CI and does not silently introduce an unrelated pool algorithm change.

Comment on lines +1025 to +1026
} catch (error) {
reject(error)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Wait for accepted targets after a send failure

When sendToWorker throws for a later cohort member after an earlier worker has already accepted the mutation, this immediately rejects the operation and removes every acknowledgement listener. The mutation queue can then dispatch the next public mutation while the accepted request is still pending; a worker override that defers operations can apply that earlier request after the later one, breaking the documented call-order guarantee and leaving its result unobserved. Preserve the transport error but wait for already-contacted targets to acknowledge or exit before settling the operation.

AGENTS.md reference: AGENTS.md:L105-L112

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confirmed P2 on real Deno 2.9.7, Bun 1.4.2 and Node 26.11.1 workers. The source is shared with upstream: a9c3ce083:3100-3112 immediately rejects and removes accepted-target listeners.

The controlled reproduction holds an earlier REMOVE on worker 0 and injects a one-shot protected sendToWorker exception for worker 1. The next ADD completes on both workers before the old REMOVE ACK. Releasing that genuine request then removes the new function from worker 0: subsequent calls alternate between not-found and the new result. Both before/after states and actual ACKs are observed; no ACK is mocked. This proves the exception-boundary behavior, not spontaneous failure of a stock native transport. Commands/fixtures remain under /tmp/pr196-send-cohort-failure/.

This is ordering, not transactional rollback. Retain the transport error but wait for already-contacted targets to acknowledge or exit before releasing the mutation queue. Finding remains open; no unrelated runtime fix is folded into the CI-only #3362 follow-up.

@jerome-benoit

Copy link
Copy Markdown
Contributor Author

SonarCloud gate: intentional thenable adapter

CI passes 8/8 jobs at cadd7ec8; overall checks are 14/15. The quality gate fails only reliability: one S7739 finding, “Do not add then to an object”. Coverage, duplication and security conditions pass.

The reported adapter is deliberately identical to upstream. It captures then once, then delegates assimilation to Promise.resolve through a controlled thenable. Reflect.apply preserves the receiver without reading callable .call/.apply properties. This assimilation is standard behavior.

Replacing it with Promise.resolve(result) rereads the original getter, reintroducing the single-use-accessor bug. Removing then prevents assimilation. Native Deno/Bun, Chromium and committed regressions exercise this behavior; verification is in the PR description.

No workaround or analyzer suppression was added. Recommended action: accept this intentional rule exception in Sonar, with this evidence. Automated API/browser sessions have no authenticated Sonar user or issue permissions; no Sonar token is available. The external gate remains blocked pending that triage.

Port poolifier/poolifier#3362 using the native release CLI on Ubuntu. The existing explicit structural context annotation already satisfies the public API requirement.
Keep optional browser promise inspection from rejecting valid thenables.
Preserve native branding, single-read assimilation and original receivers.
Align the neutral registration lookup with upstream.

Closes #197
@jerome-benoit

Copy link
Copy Markdown
Contributor Author

Fixed #197 in b4e34cb. Optional browser prototype/own-property probing now falls back to captured-then assimilation; native branding and genuine getter/constructor errors are unchanged. The neutral registration lookup now matches upstream. Verification: 110 focused Deno regression steps pass; real Chromium 155 passes 11/11 scenarios using the committed task/cleanup fixtures (the pre-fix bundle fails nine); browser/Bun builds, DOM/WebWorker declarations, JSR dry run, lint and formatting pass. No fixes for upstream #3365/#3366 are included; those algorithms remain unchanged.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b4e34cb019

ℹ️ 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".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/pools/abstract-pool.ts Resolved
Register original cohort exits before protected Web Worker hooks can
terminate a later target synchronously. Refresh captured-owner indices
after listener registration to avoid dispatching to replacements.

Keep upstream acknowledgement and send-error semantics unchanged.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: bd7183c281

ℹ️ 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".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/pools/abstract-pool.ts Resolved
Comment on lines +3218 to +3222
} catch (error) {
// Retired bootstraps no longer block waiting submissions.
if (this.workerNodes.includes(workerNode)) throw error
} finally {
this.workerTaskFunctionBootstraps.delete(workerNode)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Restore readiness after correcting a failed replay

When a live worker fails to replay a stored runtime function, this removes its bootstrap while leaving the worker permanently marked not ready. A later addTaskFunction() that replaces the bad registration can succeed on that same worker, but nothing retries the bootstrap or restores readiness, so a one-worker fixed pool still rejects every execution with no eligible worker despite now holding the valid function. Keep a recoverable failed-bootstrap state, retry replay after a corrective mutation, or retire the failed worker instead of discarding its bootstrap unconditionally.

Useful? React with 👍 / 👎.

Refresh the captured owner index after protected listener registration.
Skip dispatch when a synchronous Web exit already retired that owner.
Preserve acknowledgement, rejection and cleanup semantics.
@sonarqubecloud

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
C Reliability Rating on New Code (required ≥ A)

See analysis details on SonarQube Cloud

Catch issues before they fail your Quality Gate with our IDE extension SonarQube for IDE

@jerome-benoit
jerome-benoit deleted the fix/worker-contracts branch October 19, 2026 00:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment