Skip to content

Roadmap follow-up: preserve E5 async-function declined-runner residue while widening route parity #3491

Description

@faktorial-ai

User report of this issue


Context

Created by Roadmapper build issue autrun-dj1c8v8ke914-a39f3d3eec on 2026-06-08. ADR 0373 superseded the attempted async-function declined-runner bridge retirement because rejecting route-ineligible async functions broke current async semantics. docs/plans/bytecode-proof-manifest.json now keeps E5b/E5d async-function runner anchors as classified open residue.

Scope

Pick one bounded route-parity slice that reduces async-function declined-runner reliance without replacing fallback execution with rejection. Keep terminal dynamic residue and ordinary sync/class/sync-generator runner owners separate.

Proof Shape

Update docs/plans/bytecode-proof-manifest.json, docs/plans/bytecode-burndown-checklist.md, and focused async-function route/fallback tests together. The proof must show declined async bodies still complete through the classified legacy runner unless the exact shape is semantically owned by the resumable unified route.

Investigation

Blast Radius

  • src/Asynkron.JsEngine/Ast/TypedAstEvaluator.AsyncFunctionInvoker.cs - owns async-function resumable route attempts, classified fallback runner construction, and should thread strictness into eligibility.
  • src/Asynkron.JsEngine/Execution/UnifiedBytecode/UnifiedBytecodeProductionEligibility.cs - owns the descriptor-backed block-scoped function declaration decline gate and activation descriptor.
  • src/Asynkron.JsEngine/Execution/UnifiedBytecode/UnifiedBytecodeVirtualMachine.cs - already has DeclareFunction handling; inspect only if the strict route exposes missing declaration-environment behavior.
  • tests/Asynkron.JsEngine.Tests/AsyncAwaitTests.cs - contains the current strict block-function fallback pins and the nearby sloppy fallback completion pin.
  • tests/Asynkron.JsEngine.Tests/BytecodeProofManifestTests.cs - validates E5b/E5d manifest shape and may need the new proof row accounted for.
  • docs/plans/bytecode-proof-manifest.json - source of the E5b/E5d async declined-runner proof rows; add the new route proof without retiring the source-presence residue.
  • docs/plans/bytecode-burndown-checklist.md - human checklist must match the manifest’s narrowed async-function route/fallback boundary.

Dependencies

  • ADR 0373 is the controlling contract: route-ineligible async functions must complete through the classified legacy runner, not reject.
  • The existing E5-async-function-declined-body-runner-still-present proof should remain open/source-present after this slice.
  • No human decision is needed.

User Stories

Story 1: As a runtime maintainer, I want one exact async-function body shape to route through resumable unified bytecode, so that E5 async declined-runner residue shrinks without changing fallback semantics.

Story 2: As a JavaScript semantics reviewer, I want unsupported async-function bodies to keep completing through the classified legacy runner, so that currently valid async programs do not reject or hang.

Approach

  • Use the narrow candidate slice: strict async-function block-scoped function declarations that do not need sloppy Annex B var-style hoisting or unowned activation-capture behavior.
  • Compute/pass IsStrict into the async-function UnifiedBytecodeProductionActivationDescriptor before EvaluateResumable.
  • Narrow the descriptor-backed FunctionDeclarationInstruction resumable decline so the strict async-owned shape can route, while sloppy/Annex B, captured-activation, dynamic, and terminal residue shapes stay declined.
  • Convert or add focused async tests: one strict route-hit proof should assert unified-bytecode-resumable-async-fast-path func=run and no classified-async-function-declined-body-runner-residue; one nearby unsupported case should still resolve through the classified fallback.
  • Add the corresponding runtime proof row to the manifest and update the checklist wording. Keep E5b/E5d runner source-presence/allowlist rows open.

Risks

  • Broadly admitting all descriptor-backed block declarations would accidentally include sloppy Annex B or capture cases.
  • Treating this as runner retirement would violate ADR 0373; the classified async fallback helper must remain until every declined shape is owned.
  • Async settlement tests can pass incorrectly if promises are not deterministically drained; keep the focused async timeout pattern.

Additional quality gates

  • Async fallback preservation - declined async bodies must still complete through classified-async-function-declined-body-runner-residue unless the exact source shape routes.
  • No rejection-based retirement - do not replace fallback completion with explicit rejection.
  • Owner separation - do not fold terminal dynamic residue or ordinary sync/class/sync-generator runner owners into this async-function slice.

Recommended Labels

  • task
  • bytecode
  • area/runtime
  • unified-bytecode
  • priority: medium
  • size:M

Build Reasoning Effort

High. The code change is narrow, but the correctness boundary crosses async promise settlement, strict block declaration semantics, and manifest source-presence ratchets. Build should avoid broad admission and prove both the new route and the preserved fallback neighbor.

Acceptance Criteria (Refined)

AC-1: A strict async-function block-scoped function declaration resolves correctly through the resumable async route and logs unified-bytecode-resumable-async-fast-path func=run.

AC-2: A nearby unsupported async-function block declaration shape still resolves through classified-async-function-declined-body-runner-residue and does not log the resumable async fast path.

AC-3: CreateClassifiedAsyncDeclinedBodyRunner remains source-present and classified in the E5b/E5d manifest rows.

AC-4: docs/plans/bytecode-proof-manifest.json, docs/plans/bytecode-burndown-checklist.md, and manifest tests are synchronized with the new route proof.

AC-5: Focused async and manifest proof commands pass with the repository async timeout settings.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions