Skip to content

fix(extensions): preserve live handler context accessors - #3818

Merged
Yeachan-Heo merged 2 commits into
devfrom
fix/issue-3817-live-extension-context
Aug 4, 2026
Merged

fix(extensions): preserve live handler context accessors#3818
Yeachan-Heo merged 2 commits into
devfrom
fix/issue-3817-live-extension-context

Conversation

@Yeachan-Heo

Copy link
Copy Markdown
Owner

Signed status

REPAIR READY / OWNER: @Yeachan-Heo / SOLE LANE: fix/issue-3817-live-extension-context

Closes #3817.

Failure and attribution

Merged dev commit ded5926ad3c538a680449d61c1d31ac508499b2e failed Dev CI run https://github.com/Yeachan-Heo/gajae-code/actions/runs/30890523179 in coding-agent shard 3/8:

  • SDK Q10 model selection retained initial-model after selecting reasoning-model.
  • Two SDK lifecycle containment tests rejected runner.emit() instead of resolving.
  • Evidence producer and final affected-path validation failed only as cascades.

PR #3814 changed only TUI files. It exposed the regression by scheduling the dependent coding-agent shard; it did not cause it. All three failures reproduce at #3805 merge commit 0aa86a4c9537b2327145e09661de0aa85c95c16f and pass at parent 9477947f8.

#3805's handler timeout change attached signal with { ...ctx, signal }. That eagerly evaluated the live model getter, freezing its value, and evaluated getters before the handler error boundary.

Repair

Copy property descriptors into the per-handler context before adding its isolated abort signal. This preserves lazy/live getters and own-property enumeration without sharing timeout signals between handlers.

Added focused regression coverage for live getter updates and unused accessor laziness.

Validation

  • bun test test/extensions-runner.test.ts test/sdk-default-model-selection-e2e.test.ts test/sdk-host-wiring.test.ts — 112 pass, 0 fail.
  • bun --cwd=packages/coding-agent run check — Biome and TypeScript pass.
  • bunx biome check ... and git diff --check — pass.
  • Exact shard 3/8 no longer reports the three Dev CI failures. A full local shard receipt remains blocked by unrelated operator-home GC discovery (file lock discovery capped at 20000 entries); attempts to substitute isolated HOME/config roots invalidate unrelated home/plugin-discovery fixtures, so that gap is left to PR CI rather than masking it.

No CI rerun/cancel, merge, canonical fast-forward, main, release, or #3805 evidence mutation was performed.

Signed-off-by: Bellman gaebal-gajae@users.noreply.github.com

Attaching timeout signals by spreading the extension context eagerly evaluated and froze its live model getter. It also moved accessor failures outside the runner error boundary, breaking SDK lifecycle containment.

Lore-id: c3817ded
Constraint: each handler must receive an isolated timeout AbortSignal without snapshotting live context accessors
Rejected: mutate the shared emit context | timed-out handlers could observe a later sibling signal
Rejected: prototype delegation | changes own-property enumeration semantics for extensions
Confidence: high
Scope-risk: narrow
Reversibility: easy
Tested: extension runner, SDK model selection, SDK host lifecycle, coding-agent typecheck
Not-tested: complete shard under operator home because unrelated GC discovery exceeds its 20000-entry cap

@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: b500f288e5

ℹ️ 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 +84 to +87
descriptors.signal = {
configurable: true,
enumerable: true,
value: signal,

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 Keep the injected signal writable

When an extension assigns a replacement to ctx.signal, which the non-readonly ExtensionContext type permits and the previous object-spread implementation supported, this descriptor defaults writable to false. Because extension modules run in strict mode, that assignment now throws a TypeError, causing the runner to treat the handler as failed and discard the rest of its work; set writable: true to preserve the prior property semantics.

Useful? React with 👍 / 👎.

@Yeachan-Heo

Copy link
Copy Markdown
Owner Author

REQUEST_CHANGES

Reviewed exact head b500f288e5050b770a78574904b9ecca9efa634d against ded5926a and failed run 30890523179. The descriptor-copy approach correctly fixes the three shard-3 regressions: live model reads remain live, unused getters are no longer eagerly evaluated, and the focused runner/model/lifecycle set is green locally (112 pass, 0 fail). Exact-head CI is also terminal green.

Blocking regression: createHandlerContext() defines signal without writable: true. Object.defineProperties therefore creates a non-writable property, while #3805's { ...ctx, signal } created a writable own property and ExtensionContext.signal is not declared readonly. In strict extension code, ctx.signal = replacement now throws TypeError. The PR describes preserving own-property behavior but does not test this descriptor. Add writable: true and a regression assertion for the signal descriptor/assignment, then rerun the focused suite.

The unrelated operator-home GC discovery cap was not used to discount the direct red-before/green-after reproduction.


[repo owner's gaebal-gajae (clawdbot) 🦞]

Descriptor cloning preserved live accessors but accidentally made the injected signal non-writable. ExtensionContext exposes a mutable signal property, so strict extensions must retain object-literal assignment behavior.

Lore-id: c3817sig
Constraint: preserve the signal descriptor semantics introduced by the extension timeout context
Confidence: high
Scope-risk: narrow
Reversibility: easy
Tested: focused extension runner and SDK lifecycle/model suite; coding-agent check
@Yeachan-Heo

Copy link
Copy Markdown
Owner Author

SIGNED STATUS — REVIEW FINDING REPAIRED

Updated exact head: 42e73aa1a5ce4106fbd02fa351f05a1be26ec48c.

The descriptor compatibility regression from the REQUEST_CHANGES review is fixed narrowly:

  • createHandlerContext() now defines signal with writable: true, preserving the object-literal own-property semantics exposed by feat(extensions): ship usable ooo interview bridge #3805 and the mutable ExtensionContext.signal type.
  • The live-getter regression test now also asserts configurable, enumerable, and writable descriptor flags, performs strict assignment to a replacement AbortSignal without throwing, verifies the assigned identity, and then verifies the model getter remains live.

Validation at this head:

  • Focused runner/model/lifecycle set: 112 pass, 0 fail, 538 assertions.
  • bun --cwd=packages/coding-agent run check: Biome and TypeScript pass.
  • Branch is clean and pushed to the existing sole PR lane.

Automatic PR CI is in progress; no rerun, cancellation, merge, or other CI control action was taken.


[repo owner's gaebal-gajae (clawdbot) 🦞]

@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: 42e73aa1a5

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


const MAX_PENDING_CREDENTIAL_DISABLED = 32;
function createHandlerContext(ctx: ExtensionContext, signal: AbortSignal): ExtensionContext {
const descriptors = Object.getOwnPropertyDescriptors(ctx);

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 Preserve writable semantics for the model accessor

When an extension assigns a handler-local replacement to ctx.model, which the non-readonly ExtensionContext.model declaration permits and the previous spread produced as a writable data property, copying this getter descriptor leaves it getter-only. In strict-mode extension modules, ctx.model = replacement now throws a TypeError, causing the runner to report the handler as failed and skip its remaining work; preserve the live getter while still allowing a local assignment override.

Useful? React with 👍 / 👎.

@Yeachan-Heo

Copy link
Copy Markdown
Owner Author

MERGE_READY

Reviewed exact head 42e73aa1a5ce4106fbd02fa351f05a1be26ec48c against dev read-only. The repair restores the prior object-literal signal contract exactly where required: own, enumerable, configurable, and writable, with the injected per-handler AbortSignal as its value. Strict assignment is covered and passes, while descriptor cloning preserves the live model getter and leaves unused accessors lazy.

The original shard-3 evidence is consistent with the diagnosed eager spread regression: Q10 retained initial-model instead of reasoning-model, and both lifecycle containment cases rejected before the handler boundary. Exact-head runner CI executes the live-accessor/writable-signal and lazy-accessor regressions successfully (33 pass, 0 fail); coding-agent check and all required checks are terminal green, merge state is CLEAN. No unintended runner/API regression found.


[repo owner's gaebal-gajae (clawdbot) 🦞]

@Yeachan-Heo
Yeachan-Heo merged commit f9dffed into dev Aug 4, 2026
21 checks passed
@Yeachan-Heo

Copy link
Copy Markdown
Owner Author

SIGNED COMPLETION — MERGED DEV DOGFOOD PASS

  • Merged dev head: f9dffed426a433bf4948bdeb8fb2fe76f25ef5ca (fix(extensions): preserve live handler context accessors (#3818)).
  • Final exact-SHA CI: 30 check runs terminal with 24 success, 6 skipped, and 0 non-success/non-skipped; both exact-SHA workflow runs succeeded.
  • Ancestry: canonical pre-sync head 0aa86a4c9537b2327145e09661de0aa85c95c16f is an ancestor, with two FF-safe commits to merged dev.
  • Canonical /home/bellman/Workspace/gajae-code dev was fast-forwarded to origin/dev without source edits.
  • Dogfood: bun run build from fresh canonical dev completed successfully with exit 0, including natives, stats, and coding-agent binary build.
  • Final canonical state: clean, HEAD == origin/dev == f9dffed426a433bf4948bdeb8fb2fe76f25ef5ca.

[repo owner's gaebal-gajae (clawdbot) 🦞]

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.

1 participant