Skip to content

Fix create - #134

Open
andrew-fleming wants to merge 11 commits into
OpenZeppelin:mainfrom
andrew-fleming:fix-create-improve-type-check
Open

Fix create#134
andrew-fleming wants to merge 11 commits into
OpenZeppelin:mainfrom
andrew-fleming:fix-create-improve-type-check

Conversation

@andrew-fleming

@andrew-fleming andrew-fleming commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

The base create forced a fixed param shape and a generic return, so subclass overrides fail ts static-side checks. This PR makes create more permissive. Without doing a heavy refactor of typechecking, the evidence that this fix works is in the simulators in integration/. Without the fix, there's an ide error. With the fix, no error

Summary by CodeRabbit

  • Bug Fixes
    • Improved simulator creation compatibility across different invocation patterns.

@andrew-fleming
andrew-fleming requested review from a team as code owners July 27, 2026 03:01
@coderabbitai

coderabbitai Bot commented Jul 27, 2026

Copy link
Copy Markdown

Review Change Stack

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 756b3535-a7b4-4d77-8aee-ed7365c65f4d

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Walkthrough

The simulator now uses separate TypeScript configurations for building and typechecking, updates its build script, generalizes the static factory signature, and adjusts witness override generic parameters.

Changes

Simulator TypeScript and factory updates

Layer / File(s) Summary
Separate build and typecheck configurations
packages/simulator/tsconfig.build.json, packages/simulator/tsconfig.json, packages/simulator/package.json
The package adds an emit-only build configuration, expands typechecking to source and test files, and points the build script to tsconfig.build.json.
Generalize factory and witness typing
packages/simulator/src/factory/createSimulator.ts, packages/simulator/test/integration/Witness.test.ts
The static factory uses variadic arguments and returns Promise<Simulator>, while witness overrides use IWitnessWitnesses<unknown, WitnessPrivateState>.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Possibly related PRs

Suggested reviewers: 0xisk

Poem

A bunny builds with types so bright,
Separating checks from output night.
The factory takes a flexible leap,
While witnesses their shapes now keep.
Hop, compile, and declarations bloom!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Title check ❓ Inconclusive The title is too vague to convey the main change; it only says "Fix create" without indicating the simulator or TypeScript updates. Rename the PR to describe the core change, such as updating the simulator create API and typecheck/build configs.
✅ Passed checks (4 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/simulator/src/factory/createSimulator.ts`:
- Around line 202-213: Update the static create method’s public
overload/signature to return the concrete constructor-derived subclass type
instead of Promise<Simulator>, while retaining a permissive implementation
signature for subclass overrides. Ensure await DerivedSimulator.create(...)
preserves DerivedSimulator-specific members, using the existing this constructor
type and create implementation.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 6b4ed645-9947-4a26-acb0-bef8e4951660

📥 Commits

Reviewing files that changed from the base of the PR and between bcce9b5 and 9d4281e.

📒 Files selected for processing (5)
  • packages/simulator/package.json
  • packages/simulator/src/factory/createSimulator.ts
  • packages/simulator/test/integration/Witness.test.ts
  • packages/simulator/tsconfig.build.json
  • packages/simulator/tsconfig.json

Comment thread packages/simulator/src/factory/createSimulator.ts Outdated
@andrew-fleming
andrew-fleming marked this pull request as draft July 27, 2026 03:11
@andrew-fleming andrew-fleming changed the title Fix create, improve type check Fix create Jul 28, 2026
@andrew-fleming
andrew-fleming marked this pull request as ready for review July 28, 2026 02:23
* @returns The constructed simulator (subclass-aware via `this`).
*/
static async create<T extends Simulator>(
static async create(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

🔵 followupsuper.create(tuple, options) is no longer checked against TArgs.

Every subclass has exactly one call site that assembles the tuple, and it is now unchecked:

type Args = readonly [owner: Uint8Array, salt: Uint8Array];

static async create(owner: Uint8Array, salt: Uint8Array, options = {}) {
  return super.create([owner], options) as Promise<Sub>;  // forgot `salt`
}
  • on main: TS2345: Source has 1 element(s) but target requires 2
  • with this PR: compiles, then fails at runtime inside config.contractArgs(...contractArgs)

Direct calls on the base class lose the same protection. Base.create({ backend: 'dry' }) and five-argument calls both type-check now. main rejected both.

A typed sibling keeps override compatibility and the arg checking. I compiled this against all three sample simulators (clean), and the bad call above errors again:

static async create(
  this: new (deps: BackendDeps<P, L>) => Simulator,
  ...args: unknown[]
): Promise<Simulator> {
  const [contractArgs, options] = args as [TArgs?, SimulatorOptions<P, W>?];
  return (this as unknown as typeof Simulator)._create.call(this, contractArgs, options);
}

/** Typed construction path; subclass `create` overrides should call this. */
static async _create(
  this: new (deps: BackendDeps<P, L>) => Simulator,
  contractArgs: TArgs = [] as unknown as TArgs,
  options: SimulatorOptions<P, W> = {},
): Promise<Simulator> {
  const deps = await prepareBackend(contractArgs, options);
  return new this(deps);
}

Subclasses then call super._create([owner, salt], options). Underscore-public matches the existing _backend / _signers convention for declaration emit. Purely additive, so it can also land after this PR.

added by claude (dev3-code-review)

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.

Yes, this is better 👍

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.

// Permissive so subclasses can override `create` with contract-specific
// params/returns without tripping TS's static-side `extends` check.
...args: unknown[]
): Promise<Simulator> {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

🔵 followup — a subclass that does not override create loses its own instance type.

class NoOverride extends Base {
  bump() { return this.circuits.impure.setVal(1n); }
}
await (await NoOverride.create([1n])).bump();
// this PR: TS2339: Property 'bump' does not exist on type '...Simulator'
// main:    fine, `T` was inferred from `this`

Overriding create becomes mandatory for every subclass, including ones that only add circuit methods. Blast radius is small today (composedTokens.ts and sharedInitCollision.ts are the only non-overriders in compact-contracts), but it narrows the API contract. Worth a README line so people meet it as documentation rather than as a type error.

added by claude (dev3-code-review)

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.

Indirectly addressed above. Improved readme doc d9fea4f

@@ -199,13 +199,18 @@ export function createSimulator<
* @param options - Backend selection, witnesses, private state, live world.
* @returns The constructed simulator (subclass-aware via `this`).

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

nitpick — this @returns is no longer accurate. The return type is now the base Simulator, so it is not subclass-aware. That is precisely what the widening gave up. Suggest something like "the base simulator type; subclass create overrides narrow it".

added by claude (dev3-code-review)

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.

const UINT_OVERRIDE = 333n;

const overrideWitnesses = (): IWitnessWitnesses<WitnessPrivateState> => ({
const overrideWitnesses = (): IWitnessWitnesses<

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

nitpickunknown is correct here, but it reads as arbitrary. WitnessWitnesses is <L>() => IWitnessWitnesses<L, WitnessPrivateState>, so ReturnType<typeof WitnessWitnesses> erases L to unknown, and that erased type is the simulator's W. A named alias in WitnessWitnesses.ts would make the reason visible:

export type WitnessWitnessSet = ReturnType<typeof WitnessWitnesses>;

The test then reads const overrideWitnesses = (): WitnessWitnessSet => ({ ... }).

added by claude (dev3-code-review)

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.

@0xisk 0xisk left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Reviewed ed07aec against origin/main (bcce9b5). 3 files, +15/−7.

Verified locally: reproduced the baseline TS2417 on all three sample simulators, confirmed this PR clears it, and ran the dry suite (10 files, 90/90 pass). I also tested two narrower fixes and both still fail, so ...args: unknown[] does look like the minimum that unblocks the override. Per-line notes are inline.

🔴 blocking — the fix has no regression guard

packages/simulator/tsconfig.json has include: ["src/**/*"], so yarn types never compiles test/integration/*Simulator.ts. yarn test is vitest, which strips types without checking them. The create signature can regress and CI stays green.

The same cause makes the Witness.test.ts hunk inert. It fixes a real arity bug (IWitnessWitnesses<L, P> takes two params), but nothing ever compiles that file, so it neither proves the fix nor guards it.

❔ question — why was 9d4281e reverted?

9d4281e had the split that answers the above: tsconfig.build.json for emit, tsconfig.json as noEmit + bundler + include: ["src/**/*", "test/**/*"]. ed07aec removed it. I ran that config verbatim against this branch and it was clean, apart from local @types/node resolution noise on my machine. If CI failed on it, that is worth recording in the PR body. If it was dropped to keep the diff small, it is the fix for the blocking item.

🔵 followup — README is stale on this exact API

packages/simulator/README.md still shows new MySimulator([...], {...}) (L37), BaseSimulatorOptions (L93, L262, L270), and a four-argument createSimulator<P, L, W, TArgs> that is missing the TContract slot (L59). Pre-existing rather than introduced here, but this PR is about create's contract and the README never documents create.

added by claude (dev3-code-review)

@andrew-fleming

Copy link
Copy Markdown
Contributor Author

🔴 blocking — the fix has no regression guard
packages/simulator/tsconfig.json has include: ["src/**/*"], so yarn types never compiles test/integration/*Simulator.ts. yarn test is vitest, which strips types without checking them. The create signature can regress and CI stays green.

The same cause makes the Witness.test.ts hunk inert. It fixes a real arity bug (IWitnessWitnesses<L, P> takes two params), but nothing ever compiles that file, so it neither proves the fix nor guards it.

Added a regression test. I updated the description with the initial fix that the reason something like this wasn't included was because it would involve a non-trivial refactor on how types are checked. What this PR now proposes is an ugly fix

❔ question — why was 9d4281e reverted?
9d4281e had the split that answers the above: tsconfig.build.json for emit, tsconfig.json as noEmit + bundler + include: ["src//*", "test//*"]. ed07aec removed it. I ran that config verbatim against this branch and it was clean, apart from local @types/node resolution noise on my machine. If CI failed on it, that is worth recording in the PR body. If it was dropped to keep the diff small, it is the fix for the blocking item.

I'd argue that it's implied in the description (Without doing a heavy refactor of typechecking...) and I created a separate issue for it when I reverted the changes: #135 . I'll try and make this more explicit in the future. The CI failed with it because the files being typechecked depended on artifacts that were not yet available in the CI. I made it a separate issue so it can be thought through in a better context

🔵 followup — README is stale on this exact API
packages/simulator/README.md still shows new MySimulator([...], {...}) (L37), BaseSimulatorOptions (L93, L262, L270), and a four-argument createSimulator<P, L, W, TArgs> that is missing the TContract slot (L59). Pre-existing rather than introduced here, but this PR is about create's contract and the README never documents create

I updated create in the "Extending the Base Simulator" section. The rest of the stale docs should be done separately

LMK if you disagree with any of these points

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.

2 participants