Skip to content

💥 Rename DurableCtx to DurableContext - #409

Merged
taras merged 1 commit into
mainfrom
rename/durable-context
Aug 9, 2026
Merged

💥 Rename DurableCtx to DurableContext#409
taras merged 1 commit into
mainfrom
rename/durable-context

Conversation

@taras

@taras taras commented Aug 9, 2026

Copy link
Copy Markdown
Owner

Closes #268.

Why

This repository spells Context out in identifiers — DurableContext, ReviewContext, or the unsuffixed house style (Staging, ActiveLoop). DurableCtx was the last exported holdout, and it sat next to the DurableContext interface it carries, so every read named the same subject twice under two spellings.

What changes

The exported Effection context in @executablemd/durable-streams is named DurableContext, sharing the name with the interface it holds — a value and a type occupy separate declaration spaces, the same shape Symbol has.

Before:

import { type DurableContext, DurableCtx } from "@executablemd/durable-streams";

const state = scope.expect<DurableContext>(DurableCtx);

After:

import { DurableContext } from "@executablemd/durable-streams";

const state = scope.expect<DurableContext>(DurableContext);

How it works

The context key is untouched: createContext<DurableContext>("@effection/durable") still names the same context, and Effection identifies a context by that string rather than by the binding that holds it. A running workflow observes exactly the state it observed before.

mod.ts re-exports the name once, as a value re-export rather than a export { … } / export type { … } pair, because a value re-export carries every meaning of the name. Splitting it back into two lines would collide on the identifier; keeping only the export type line would leave the type exported and the context itself unreachable.

Review guide

Start with: packages/durable-streams/context.ts

Then review:

  1. packages/durable-streams/mod.ts — the public surface, now one line instead of two
  2. packages/durable-streams/{effect,combinators,run}.ts — the internal readers and the one writer
  3. packages/core/src/loop.ts — the only consumer outside the package that reads the context
  4. packages/durable-streams/tests/context.test.ts — the coverage the export previously had none of

What must stay true

  • The context key stays "@effection/durable" — nothing in this diff touches the createContext argument, so durable state installed by one loaded copy stays visible to another.
  • DurableContext remains reachable as a value from @executablemd/durable-streams — checked by packages/durable-streams/tests/context.test.ts, which fails to typecheck if the export narrows back to export type.

How to verify it

  • context.test.ts runs a workflow under durableRun that reads its own state through the exported DurableContext, asserts the stream and child counter it was given, and returns the coroutine id durableRun assigned it (root.7). It fails if the value export is dropped, and it fails if durableRun stops installing the context on the workflow scope.
  • Mutation check: replacing export { DurableContext } with export type { DurableContext } in mod.ts makes that test fail type-checking ('DurableContext' was exported here at mod.ts:62), confirming the test is not vacuous.

Local verification on this commit:

  • deno task lint — 0 errors, formatting clean
  • deno task check — exit 0
  • deno task check:jsrSuccess Dry run complete
  • deno task test --changed=origin/main — 301 passed (2235 steps), 0 failed
  • npx tsc --project tsconfig.node.json — exit 0

Scope

Included

  • The export, its internal readers, and its one consumer in packages/core.
  • The specification and decision-record occurrences the rename makes untrue: specs/executable-mdx-spec.md, specs/decisions.md, and the two documents under packages/durable-streams/specs/.

Intentionally unchanged

  • The context key "@effection/durable" — renaming it would be a behavioral break, not a naming one.
  • Local variables named ctx, parentCtx, and childCtx. They are not exported, and the issue scopes this PR to the exported name and its call sites.
  • The module-private TestAgentCtx, which the TestAgent migration slice renames.

Generated or mechanical changes

  • The Markdown changes under specs/ and packages/durable-streams/specs/ are identifier substitutions with no other edits.

Risks and limitations

  • Breaking: DurableCtx is gone from the published surface of @executablemd/durable-streams. No consumer inside this repository referenced it outside the files changed here. Recovery for an external consumer is a one-word rename at the import site.

Scope confirmation

  • Every changed file supports the purpose described above.
  • Unrelated cleanup and formatting changes are excluded.
  • Generated or mechanical changes are clearly identified.
  • The description matches the final diff and test results.

The repository spells `Context` out in identifiers, and `DurableCtx` was the
last exported holdout. It now shares its name with the `DurableContext`
interface it carries — a value and a type occupy separate declaration spaces —
so a scope read states its subject once: `scope.get(DurableContext)` and
`scope.expect<DurableContext>(DurableContext)`.

The context key is untouched. `createContext("@effection/durable")` still
names the same context, and Effection identifies a context by that string, so
the state a running workflow observes is the state it observed before. What
changes is the export: `@executablemd/durable-streams` no longer exports
`DurableCtx`, which is a breaking change for anyone reading the context
directly.

`mod.ts` re-exports the name once, without `export type`, because a value
re-export carries every meaning of the name; splitting it would leave the type
exported and the context unreachable. The new `context.test.ts` runs a
workflow that reads its own state through the exported value and returns the
coroutine id `durableRun` assigned it, so dropping the value export fails the
typecheck rather than passing silently.

Co-authored-by: Taras Mankovski <74687+taras@users.noreply.github.com>
@github-actions

github-actions Bot commented Aug 9, 2026

Copy link
Copy Markdown

PR #409: 💥 Rename DurableCtx to DurableContext

12 files, +60 / -34

Scope

✅ PR scope looks good.

Structural

Oxlint structural signals:

  • no-unnecessary-type-assertion ×4: packages/durable-streams/effect.ts, packages/core/src/execute.ts
  • no-empty-function ×3: packages/durable-streams/effect.ts, packages/core/src/execute.ts
  • no-unused-vars ×1: packages/durable-streams/effect.ts

Slop

✅ Slop indicators look low.

Static Analysis

Oxlint: 23 diagnostics across 4 files (8 rules)
Density: 0.383 violations/added-line

no-unsafe-type-assertion (9): packages/durable-streams/combinators.ts, packages/core/src/execute.ts, packages/durable-streams/run.ts
no-unnecessary-type-assertion (4): packages/durable-streams/effect.ts, packages/core/src/execute.ts
no-empty-function (3): packages/durable-streams/effect.ts, packages/core/src/execute.ts
no-floating-promises (3): packages/durable-streams/effect.ts, packages/core/src/execute.ts
no-unused-vars (1): packages/durable-streams/effect.ts
consistent-function-scoping (1): packages/durable-streams/effect.ts
consistent-return (1): packages/core/src/execute.ts
unbound-method (1): packages/core/src/execute.ts

Correctness

No extraneous code patterns detected.

@taras
taras marked this pull request as ready for review August 9, 2026 11:26
@taras
taras merged commit 6d79667 into main Aug 9, 2026
11 checks passed
@taras
taras deleted the rename/durable-context branch August 9, 2026 11:38
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.

💥 Rename DurableCtx to DurableContext

1 participant