Skip to content

refactor(act): share non-widening builder members via BuilderCore#1111

Open
Rotorsoft wants to merge 2 commits into
masterfrom
act-1110-builder-base
Open

refactor(act): share non-widening builder members via BuilderCore#1111
Rotorsoft wants to merge 2 commits into
masterfrom
act-1110-builder-base

Conversation

@Rotorsoft

Copy link
Copy Markdown
Owner

Closes #1110 (partial — the clean F-bounded portion; see Follow-up).

Follows the builder-utils extraction in #1109. act-builder and slice-builder still duplicated the type signatures of the members that return the builder unchangedwithProjection, on, events. This extracts them into a BuilderCore<Self, …> interface both builders extend via F-bounded polymorphism.

What changed

  • New libs/act/src/builders/builder-core.tsBuilderCore<Self, TEvents, TActions, TActor, TLanes> holding withProjection, on, events. Self is the concrete builder passed by each extender, so hover/error output resolves to SliceBuilder<…> / ActBuilder<…>, not an abstract base.
  • SliceBuilder and ActBuilder become interfaces that extends BuilderCore<Self, …>. Interface is the canonical form for F-bounded self-reference; a type alias = Base<Self> & {…} circularly references itself (TS2456).
  • Net ~56 fewer lines in the builders.

Why withState/withLane stay per-builder

They widen generics (return a different instantiation), which a shared signature could only express with HKT emulation. A spike confirmed HKT works functionally but degrades the public builder's hover and error messages — a poor trade for deduping two stable declarative signatures. This is documented in builder-core.ts, and the full shared base is left as a follow-up under the same rationale (it's the highest type-risk piece).

Test plan

  • pnpm typecheck clean
  • pnpm test — 968 act tests pass (runtime unchanged; the @ts-expect-error type-assertion tests still hold, confirming builder type behavior is identical)
  • Coverage: 100% statements / 100% branches / 100% functions / 100% lines (builder-core.ts is type-only).
  • pnpm lint clean, pnpm build clean
  • CI green
  • Review

Stability charter impact

Additive / none. SliceBuilder/ActBuilder change from type alias to interface — a source-text-only snapshot diff with identical structural surface (same members, same signatures). No export removed or narrowed.

🤖 Generated with Claude Code

rotorsoft and others added 2 commits July 3, 2026 10:50
)

act-builder and slice-builder duplicated the type signatures of withProjection,
on, and events — the members that return the builder unchanged (Self). Extract
them into a BuilderCore<Self, ...> interface both builders extend via F-bounded
polymorphism, so Self resolves to the concrete SliceBuilder/ActBuilder and error
messages are unaffected. SliceBuilder/ActBuilder become interfaces (the
canonical form for F-bounded self-reference; a type alias circularly references
itself). ~56 net lines removed.

withState and withLane stay per-builder: they *widen* the generics (return a
different instantiation), which a shared signature could only express with HKT
emulation — and that degrades the public builder's hover/error output, a poor
trade for two declarative signatures. Documented in builder-core.ts. Runtime
unchanged (968 act tests, 100% coverage); public surface unchanged (interface
vs alias is a source-text-only snapshot diff). The full shared base is left to
follow-up per the same rationale.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…1110)

Rename builder-core.ts to builder-base.ts and the type BuilderCore to
BuilderBase (both builders extend it, so 'base' reads truer than 'core'). Move
the ReactionOn step type from internal/builder-utils.ts into builder-base.ts so
the two shared builder *types* live together in the builders layer, and drop
ReactionOn from the internal barrel — the runtime helpers (reaction_on,
register_lane) stay in internal. ReactionOn inlines its schedule param as
DeferWhen | (event => DeferWhen) (DeferWhen is public) so it needs nothing from
internal, keeping the layering clean.

Bonus: ReactionOn left the (re-exported, snapshotted) internal barrel, so the
stability snapshot net-shrinks — the source-text growth that tripped the RFC
gate on the prior commit is gone. No behavior change (968 act tests, 100%
coverage), no public surface change.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.

refactor(act): shared builder base type — act composes slice (T2 follow-up)

1 participant