Files
Imhotep/CODEBASE_REVIEW.md
T
John Dvorak 19559b658b feat: implement variable-bound FOL domain resolution for descendants/children
- Extend DomainResolver.resolve() signature to accept optional BindingEnv
  so that parentVar domains can be resolved with runtime variable bindings
- Pass BindingEnv through evaluateForAll/evaluateExists to resolver calls
- Add buildAncestorIndex() to precompute DOM ancestor sets from CDP data
- SelectorDomainResolver now filters descendant domains by the bound parent
  when domain.parentVar is present and ancestor index is available
- Return undefined for parentVar domains when no ancestor index or env
  (prevents silent fallback to global domain resolution)
- Update all test DomainResolver mocks for new resolve interface
- Add 10 unit tests covering ancestor index construction, backward compat,
  descendant filtering, exclusion of non-descendants, empty descendants,
  missing parentVar/env, and no-ancestor-index safety
2026-05-21 17:05:35 -07:00

30 KiB
Raw Blame History

Imhotep Codebase Review

Date: 2026-05-21

Perspective: a developer onboarding to contribute to Imhotep, and a developer evaluating whether to adopt the tooling in their own repositories, team workflows, or CI responsibilities.

Executive Summary

Imhotep has a compelling and unusually ambitious core: declarative UI geometry contracts, dense and fluent authoring styles, finite first-order logic evaluation, structured diagnostics, Playwright integration, property runners, and a package split that mostly reflects the conceptual model. The design promise is high, and several implementation choices already support that promise: broad tests, typed diagnostic shapes, canonical lowering, deterministic evaluation, and real browser fixture coverage.

The current adoption risk is trust, not ambition. A skeptical developer will find working internals next to misleading quick-start instructions, stale examples, package metadata drift, a contaminated lockfile, and several places where syntax support appears ahead of runtime semantics. That mismatch matters because this project sells correctness. If a tool promises First Order Logic Geometric Contract and Property Testing for the Web, every documented example, diagnostic, and CI signal needs to be boringly reproducible.

The most important next step is a hardening pass that makes the public story match the implementation. Fix the CLI and quick-start path, modernize examples, remove stale “not wired yet” expectations, clean the lockfile, add portable E2E paths, align package versions/peers, and explicitly label or hide internal surfaces. After that, focus on semantic correctness gaps: variable-bound domains, topology predicate semantics, diagnostic metric propagation, and world-schema consolidation.

High-Level Assessment

Area Current State Risk Priority
Product concept Strong, differentiated, useful Low Preserve
Core FOL model Clear finite-domain evaluator with quantifier semantics Medium Harden
Geometry extraction Useful fast and CDP paths, but multiple world shapes Medium Consolidate
Public docs Improved root README, but examples and quick-start have broken expectations High Immediate
CLI/scaffolding Exists, but consumer invocation and generated files are inconsistent High Immediate
CI/tooling Gitea workflow exists, but reproducibility signals need cleanup Medium High
Tests Broad, but some tests encode stale assumptions Medium High
Diagnostics Strong schema, but data loss/drift remains Medium High
API governance Public/internal annotations exist, but internals are still exported Medium Medium
Contributor ergonomics Package map is understandable, but production path is hard to trace Medium Medium

Strengths Worth Preserving

  • The repository has a real product thesis: layout assertions as relational contracts, not screenshot diffs or imperative Playwright coordinate checks. README.md:3 explains spatial, semantic, dimensional, and property-based layout assertions through fluent API or dense DSL.
  • The package split mostly matches the mental model: imhotep-core, imhotep-dsl, imhotep-solver, imhotep-playwright, imhotep-cdp, imhotep-reporter, imhotep-geometry, imhotep-topology, and related packages each have recognizable responsibilities. See the package table in README.md:15-32.
  • The public fluent path is compact from a user perspective: const ui = await imhotep(page), register assertions, then await ui.checkAll(). This is the right product shape for adoption. See README.md:5-13.
  • The test surface is broad: unit tests, property-style tests, integration tests, Playwright E2E fixtures, docs-example tests, external smoke, and benchmarks are all represented under packages/*/src and root scripts in package.json:9-18.
  • Diagnostics are treated as a product surface rather than incidental thrown errors. Public tests assert diagnostic schema fields, source references, fix hints, LLM output, and traceability.
  • CI now exists under .gitea/workflows/ci.yml and targets master, with separate lint, build/typecheck, unit, E2E, external smoke, and packaging stages.
  • The primary package READMEs now exist for packages/imhotep, packages/imhotep-playwright, packages/imhotep-dsl, and packages/imhotep-core.
  • Recent barrel export classification in packages/imhotep-playwright/src/index.ts is a good governance step. The @public and @internal markers make intent visible.

Critical And High-Priority Findings

1. The Consumer Quick Start Advertises A CLI Command That Likely Will Not Work

README.md:36-40 tells users to run:

npm install imhotep
npx imhotep init --preset react

But the published imhotep meta-package has no bin field in packages/imhotep/package.json:14-28, while the executable is defined by a separate package, imhotep-cli, in packages/imhotep-cli/package.json:16-18.

That means npx imhotep init is not a reliable consumer command unless npm resolves to imhotep-cli through unpublished/local workspace behavior. A team trying this from a clean project may hit “could not determine executable to run” or install the library package without a binary.

Recommendation: make one of these true before publishing docs:

  1. Add a bin entry to the imhotep meta-package that delegates to the CLI.
  2. Rename/publish the CLI package so npx imhotep init resolves to it intentionally.
  3. Change docs to npx imhotep-cli init --preset react and explain the relationship.

2. The README Quick Start Order Conflicts With The Scaffolder

The README says to install imhotep before running init. The scaffolder skips files that already exist, including package.json, via safeWrite() in packages/imhotep-cli/src/init.ts:16-23 and the package write in packages/imhotep-cli/src/init.ts:53-57.

If a user starts in an existing project or creates a package before init, the preset package scripts and dependencies may not be written. The result is an apparently successful scaffold with missing setup.

Recommendation: either scaffold into an empty directory first, or make init merge known-safe fields into existing package.json. At minimum, print a clear warning when package metadata is skipped and document --force or --merge behavior if added.

3. Scaffolded Projects Are ESM But Generated Configs Are CommonJS .js

Preset package JSON sets "type": "module" in packages/imhotep-cli/src/presets/react-playwright.ts:95-108 and packages/imhotep-cli/src/presets/vue-vitest.ts:115-127, but generated imhotep.config.js uses module.exports in react-playwright.ts:6-24 and vue-vitest.ts:6-26.

That is a runtime footgun. In an ESM project, module.exports in .js is not valid CommonJS unless the file is .cjs or the package type differs.

Recommendation: generate imhotep.config.cjs, or generate ESM config with export default. The root imhotep.config.js:1-19 has the same scaffold-residue smell and should be removed or converted if it is intentional.

4. Generated Preset READMEs Show Invalid Imhotep Usage

The React preset README template shows:

const ui = imhotep(page);
await ui.expect('.header').to.be.above('.content', { minGap: 16 });

Evidence: packages/imhotep-cli/src/presets/react-playwright.ts:132-135.

The actual API used by tests is const ui = await imhotep(page) and then synchronous assertion registration followed by await ui.checkAll(), as seen in react-playwright.ts:29-37.

The Vue preset has the same issue at packages/imhotep-cli/src/presets/vue-vitest.ts:151-154.

Recommendation: update every generated README snippet to use the exact tested API shape:

const ui = await imhotep(page);
ui.expect('.header').to.be.above('.content', { minGap: 16 });
const result = await ui.checkAll();
expect(result.passed).toBe(true);

5. Root Examples Are Stale And Teach The Old Extraction-Only Pattern

examples/page-test.js:4-5 says the primary V1.0 pattern is ui.extract() plus Playwright assertions. examples/page-test.js:7-8 and examples/responsive-test.js:7-8 use CommonJS require, while the repo root is ESM in package.json:5.

This conflicts with the current README, which presents ui.expect(...).to.be... plus ui.checkAll() as the main experience in README.md:5-13.

Worse, the docs-example test suite preserves the stale expectation that examples should not use the now-working public API: packages/imhotep-fixtures/src/docs-examples.test.ts:51-52, docs-examples.test.ts:65-66, docs-examples.test.ts:76-78, docs-examples.test.ts:88-89, and docs-examples.test.ts:134-137 assert that examples avoid ui.expect, checkAll, or higher-level APIs.

Recommendation: rewrite examples to ESM and current API usage. Then invert the docs tests: they should assert that canonical examples use await imhotep(page), ui.expect, ui.spec, checkAll, and failure diagnostics correctly.

6. README DSL And Property Examples Do Not Match The Parser/API

The dense DSL examples in README.md:83-88 omit selector quotes:

ui.spec('.button leftOf .icon gap 8px');
ui.spec('all .card centeredWithin .container');
ui.spec('forall $c in .card: $c width >= 200');

But parse guidance in diagnostics says selectors must be single-quoted strings, e.g. packages/imhotep-playwright/src/extraction.ts:1522-1527. E2E tests also use quoted selectors, such as packages/imhotep-fixtures/src/e2e-semantic-dsl.test.ts:65-77.

The property example in README.md:90-96 calls imhotepFixture(...).forAllProps(...), but fixture handles expose forAllInputs and exhaustivelyForAllInputs in packages/imhotep-playwright/src/public.ts:882-907. forAllProps exists for component/story targets at public.ts:776-819, not fixture targets.

Recommendation: make README examples executable and cover them with docs tests. If the desired DSL syntax is unquoted selectors, implement it explicitly and test ambiguity. If not, document quoted selectors consistently.

7. One E2E Test Contains An Absolute Workstation Path

packages/imhotep-fixtures/src/e2e-semantic-dsl.test.ts:5-10 hardcodes:

const FIXTURE_BASE = 'file:///home/johndvorak/Business/workspace/Imhotep/packages/imhotep-fixtures/src/pages'

That will not run on CI, another developers machine, or another checkout path. Most neighboring tests use shared fixture helpers or import.meta.url, so this is likely accidental.

Recommendation: use fileURLToPath(import.meta.url) plus pathToFileURL, or route through the existing fixture harness.

package-lock.json references ../Operator/node_modules at lines 25, 38, 1055, and 2942.

Even if npm install works locally, a public/release lockfile should not encode paths into a sibling checkout. This is exactly the kind of artifact that causes skeptical adopters to question release hygiene.

Recommendation: regenerate the lockfile from a clean checkout with no sibling-linked packages. Then switch CI to npm ci once the lockfile is clean.

9. Internal Dependency Ranges Lag Behind The Current Version

Packages are versioned 1.1.0, but internal dependency ranges are still ^1.0.0. Examples:

  • packages/imhotep/package.json:3 is 1.1.0, but dependencies in packages/imhotep/package.json:33-43 are ^1.0.0.
  • packages/imhotep-playwright/package.json:3 is 1.1.0, but dependencies in packages/imhotep-playwright/package.json:34-39 are ^1.0.0.
  • packages/imhotep-cli/package.json:32-35 depends on ^1.0.0 packages.

This may resolve to 1.1.0 if all packages are published and semver picks latest versions, but it undermines the synchronized monorepo release story.

Recommendation: choose the release policy. For lockstep releases, use ^1.1.0 or exact 1.1.0 internal ranges. For independently versioned packages, document compatibility and add cross-version smoke tests.

10. Playwright Peer Ranges Disagree Across The Main Entry Points

The meta-package allows Playwright ^1.40.0 in packages/imhotep/package.json:45-48, while imhotep-playwright requires ^1.59.1 in packages/imhotep-playwright/package.json:30-33.

The meta-package re-exports Playwright integration, so adopters should see one clear supported Playwright range.

Recommendation: align peer dependency ranges. If 1.40 is genuinely supported, prove it in CI. If 1.59.1 is required for extraction/runtime behavior, make the meta-package match.

Architecture And Semantics Findings

11. Variable-Bound Dense FOL Domains Parse And Compile But Are Not Resolved Correctly At Runtime

The DSL supports syntax such as descendants($card, '.title') and preserves parentVar; see packages/imhotep-dsl/src/compiler.ts:940-959 and tests in packages/imhotep-dsl/src/fol-dense-combinations.test.ts:391-403.

However, the runtime resolver interface is resolve(domain: DomainRef) with no binding environment in packages/imhotep-solver/src/logic-engine.ts:99-101, and SelectorDomainResolver.resolve() simply returns this.domains.get(domain.selector ?? domain.domain) in packages/imhotep-playwright/src/extraction.ts:958-960.

That means scoped runtime semantics like “titles under this card” likely degrade into global .title resolution or cannot resolve variable-only domains. For a FOL contract system, this is a high-value correctness gap because the syntax implies relational scoping.

Recommendation: extend domain resolution to receive the current binding environment, or precompute dependent domains keyed by parent subject ID. Add E2E tests for descendants($var, selector), children($var), and failing cases where a global match would pass but scoped semantics should fail.

12. inStackingContext Has An Arity/Semantics Mismatch

The solver descriptor declares inStackingContext with arity 1 in packages/imhotep-solver/src/predicates.ts:83, while the Playwright FOL compiler emits inStackingContext(subjectVar, refVar) for relation options in packages/imhotep-playwright/src/fol-compiler.ts:326-331.

The grammar also supports both standalone topology assertions and relation options around packages/imhotep-dsl/src/grammar.ts:1305-1360. Topology package tests model a two-argument context comparison, e.g. packages/imhotep-topology/src/topology.test.ts:390-405.

Recommendation: decide the product semantics. If inStackingContext is unary, the relation option should not emit a reference argument. If it is binary, update predicate metadata/evaluation and tests. Do not leave “same context” behavior as a known accepted failure in E2E.

13. Public Runtime Orchestration Bypasses Several Cleaner-Looking Abstractions

The conceptual architecture includes imhotep-core/src/pipeline.ts, imhotep-extractor/src/planner.ts, canonical adapters, CDP extraction, fast extraction, and Playwright-specific orchestration. The actual public checkAll() path runs primarily through packages/imhotep-playwright/src/check-all.ts, fol-compiler.ts, and extraction.ts.

This is understandable in a growing framework, but it increases onboarding cost. A contributor can easily modify a generic-looking pipeline or planner and not affect the production public API.

Recommendation: document the actual golden path for contributors:

public.ts -> check-all.ts -> fol-compiler.ts -> extraction.ts -> logic-engine.ts -> predicates.ts

Then either retire unused abstractions, mark them experimental/internal, or add tests proving they are equivalent to the public path.

14. World Schema Duplication Makes Predicate Changes Riskier Than They Need To Be

There are multiple world shapes and adapters:

  • Core geometry world schema in packages/imhotep-core/src/world.ts.
  • Canonical world schema in packages/imhotep-core/src/canonical.ts.
  • CDP snapshot/world extraction in packages/imhotep-cdp/src/extractor.ts.
  • Playwright fast-path world builder in packages/imhotep-playwright/src/world-builder.ts.
  • Solver predicates that sometimes reach through optional/adapted fields using as any, e.g. packages/imhotep-solver/src/predicates.ts:163-169 and predicates.ts:242-262.

The result is that a predicate author must know whether a fact is present in fast extraction, CDP extraction, canonical adaptation, test mocks, and solver-world adaptation.

Recommendation: define one canonical solver input contract and make every extraction path adapt into it before predicate evaluation. Add a “fact availability matrix” for each predicate so contributors can see which extraction facts are required and which paths provide them.

15. imhotep-core Contains Test/Mock Helpers That Import Downstream Packages

packages/imhotep-core/src/integration-mocks.ts:8-14 imports types from imhotep-solver and imhotep-state, but packages/imhotep-core/package.json:1-61 lists no dependencies.

This file is not exported from the core barrel, but it is still source under a publishable package and can end up in dist output. It violates the expected direction of dependency flow: core should not know about solver/state.

Recommendation: move integration mocks into a test-only package or packages/imhotep-core/test-support that is not included in production package files. Alternatively add explicit dependencies and accept the architectural coupling, but that seems worse.

16. Public/Internal API Classification Is Visible But Not Enforced

packages/imhotep-playwright/src/index.ts:64-98 labels pools, page wrapper, environment helpers, and target resolution as @internal, but still exports them from the public package root.

This is better than accidental export, but it is still a semver hazard. Users can import internals directly from imhotep-playwright, and package managers will treat them as public unless documentation and export maps say otherwise.

Recommendation: move internals to explicit subpaths such as imhotep-playwright/internal or remove them from the package root before a stable release. If they must remain for tests/tooling, document that @internal exports are not semver-stable.

packages/imhotep-playwright/src/index.ts:34-52 exports renderer registry and renderer descriptors (react, vue, storybook, custom), but packages/imhotep/src/index.ts:1-26 does not re-export those. A user following import { imhotep } from 'imhotep' may hit missing APIs for component/story/custom renderer workflows.

Recommendation: decide whether imhotep is the single recommended import path. If yes, re-export the stable Playwright public API from the meta-package. If no, the README should clearly say when to use imhotep versus imhotep-playwright.

18. Large Central Files Make Contributor Review Hard

Key files are large enough to slow down safe changes:

  • packages/imhotep-playwright/src/extraction.ts: 1951 lines.
  • packages/imhotep-dsl/src/grammar.ts: 1537 lines.
  • packages/imhotep-solver/src/predicates.ts: 1040 lines.
  • packages/imhotep-playwright/src/public.ts: 940 lines.
  • packages/imhotep-playwright/src/check-all.ts: 681 lines.

Large files are not automatically wrong, but these contain multiple responsibilities: parsing, AST shaping, extraction planning, world adaptation, public handles, diagnostics mapping, and evaluation orchestration.

Recommendation: split by responsibility after the current hardening pass. Good seams include extraction planning versus browser execution, diagnostic mapping versus evaluation, public types versus public factory construction, and predicate families by geometry/topology/size/alignment.

Diagnostics Findings

19. FOL Diagnostic Mapping Drops Metrics And Source References

mapFolDiagnostic() returns empty metrics and sourceRef in packages/imhotep-playwright/src/extraction.ts:1536-1547.

The projects value proposition depends heavily on actionable diagnostics. A schema-valid diagnostic with empty measured context is less useful than a plain Playwright assertion in many failure investigations.

Recommendation: preserve predicate metrics, witness IDs, selector/domain provenance, source labels, and clause source references through FOL diagnostic mapping. Add regression tests that assert compound and quantified failures include non-empty measured context.

20. Boolean Connectives Drop Operand Metrics

evaluatePredicate() preserves predicateResult.metrics in packages/imhotep-solver/src/logic-engine.ts:837-843, but evaluateAnd, evaluateOr, evaluateNot, and evaluateImplies construct new FormulaResult objects without carrying operand metrics in logic-engine.ts:526-667.

For compound contracts, this can make failures less actionable precisely when users need more explanation.

Recommendation: define a metric aggregation policy for connectives. For failing and, preserve the failing sides metrics. For failing or, preserve both failed sides if possible. For not, preserve operand metrics when it fails because the operand passed.

21. Diagnostic Code Metadata Has Multiple Sources Of Truth

The core diagnostic union and default categorization live in packages/imhotep-core/src/diagnostics.ts, while reporter code metadata is duplicated in packages/imhotep-reporter/src/codes.ts.

This can drift. One concrete example identified during review: reporter metadata classifies topology unsupported behavior differently from core fallback categorization.

Recommendation: generate reporter metadata from the core diagnostic registry, or move the canonical registry into core and import it everywhere.

Testing And Verification Findings

22. Root npm test Depends On Prebuilt dist For Most Packages

The root test script is npm run test --workspaces in package.json:12. Many workspace tests run node --test dist/**/*.test.js without compiling first, for example packages/imhotep-dsl/package.json:19-22, packages/imhotep-solver/package.json:19-22, and packages/imhotep/package.json:29-32.

Some packages compile tests first, such as imhotep-core, imhotep-playwright, and imhotep-fixtures. The split behavior is surprising.

Recommendation: either make npm test run npm run build first at the root, or make every package test script compile what it runs. A fresh clone should not be able to pass or fail tests based on stale dist output.

23. CI Uses npm install Despite Having A Lockfile

.gitea/workflows/ci.yml:19, ci.yml:30, ci.yml:52, and ci.yml:68 use npm install. A root lockfile exists.

Given the lockfile contamination noted above, using npm install may currently be pragmatic. But for a release-quality repository, CI should prove lockfile reproducibility.

Recommendation: clean the lockfile, then switch CI to npm ci. Add a local command in BUILD.md that matches CI exactly.

24. E2E Command Documentation Mixes Built Output And Source Paths

BUILD.md:50-55 correctly says Playwright config targets compiled E2E tests under packages/imhotep-fixtures/dist/, but BUILD.md:72-74 suggests running a source TypeScript file directly:

npx playwright test packages/imhotep-fixtures/src/e2e-public.test.ts

That may not work with the configured testDir/testMatch, and it gives contributors two incompatible mental models.

Recommendation: document one reliable targeted path, likely by building first and using --config packages/imhotep-fixtures/playwright.config.ts --grep ... or pointing at the compiled test if that is the intended mode.

25. External Smoke Uses Local Package Directories, Not Packed Tarballs

scripts/external-smoke.mjs:16-37 installs dependencies via file: paths to package directories. This is valuable, but it does not fully prove package tarball contents. CI packs packages later in .gitea/workflows/ci.yml:81-88, but does not appear to install and run from those tarballs.

Recommendation: add a publish-smoke mode that runs npm pack, installs the generated .tgz files into a clean temp project, and executes the same smoke. This catches missing files, bad exports, missing README/package metadata, and package dependency drift.

26. Fixture Registry Validation Does Not Cover All Fixture Pages

The fixture harness validates named categories in packages/imhotep-fixtures/src/harness.ts, while several pages used by E2E tests live outside that registry. This means duplicate data-testids, missing required fixtures, or structure drift can slip through for some pages.

Recommendation: either register every fixture page or add a generic validation pass over packages/imhotep-fixtures/src/pages/*.html.

Adoption Readiness Findings

27. Package READMEs Exist Only For The Main Four Packages

Package READMEs exist for imhotep, imhotep-playwright, imhotep-dsl, and imhotep-core. Other publishable packages have package metadata but no package-level usage/status docs.

That is acceptable if those packages are internal implementation details, but then they should be marked private or clearly described as internal. If they are public, npm browsers need enough context to evaluate them.

Recommendation: classify all packages as public, internal-but-published, or private. Add short READMEs for public packages and mark truly internal packages private if they should not be adopted directly.

28. Export Conditions Are Inconsistent Across Packages

The meta-package uses explicit types and import conditions in packages/imhotep/package.json:19-28, while other packages use types and default, e.g. packages/imhotep-playwright/package.json:24-29 and packages/imhotep-core/package.json:23-43.

This may work in Node ESM, but inconsistent export condition shapes make behavior harder to reason about across bundlers and TypeScript module resolution modes.

Recommendation: standardize on explicit ESM export conditions, ideally types plus import, across publishable packages.

29. Type Escape Hatches Are Concentrated In Boundary Code

There are many any and as any usages in parser/runtime boundary code, especially packages/imhotep-playwright/src/public.ts, packages/imhotep-playwright/src/extraction.ts, packages/imhotep-dsl/src/grammar.ts, and packages/imhotep-solver/src/predicates.ts.

Some of this is expected around JS proxies, browser evaluation, parser AST construction, and adapted world schemas. The risk is not that any exists; the risk is that a contributor cannot tell which casts are intentional boundary escapes versus accidental type debt.

Recommendation: do not chase zero any blindly. Instead, add local typed boundary interfaces for proxy metadata, browser-injected globals, adapted world extensions, and parser node unions. Add comments only where a cast is truly necessary.

Broken Expectations And Open Questions

  • Is npx imhotep init intended to be the canonical CLI command, or should the CLI package name be user-facing?
  • Is imhotep intended to be the only recommended import path, or should advanced users import imhotep-playwright directly?
  • Are all packages intended to be published independently, or should packages like imhotep-cdp, imhotep-extractor, imhotep-state, imhotep-topology, and imhotep-bench be private/internal?
  • Should dense DSL support unquoted CSS selectors, or should all examples use quoted selectors?
  • What is the intended runtime semantics for descendants($var, selector) and children($var)?
  • Is inStackingContext unary (“has any stacking context”) or binary (“shares/is in a specific context”)?
  • Which world schema is canonical for predicate authors?
  • Should CI test against the minimum supported Playwright version or only the latest supported version?
  • Should package tests be runnable from a fresh checkout without an explicit build?
  • Are @internal root exports semver-stable or explicitly unsupported?
  • Should external smoke validate local package directories, packed tarballs, or both?

Phase 1: Make The Public Story True

  1. Fix the CLI command and README quick start.
  2. Fix generated config format for ESM projects.
  3. Fix generated preset README snippets.
  4. Rewrite root examples to current ESM ui.expect/ui.spec/checkAll patterns.
  5. Update docs-example tests so they protect current public API examples rather than old “not wired yet” assumptions.
  6. Fix README dense DSL and property examples.

Phase 2: Make The Repo Reproducible Everywhere

  1. Replace the hardcoded E2E fixture path.
  2. Regenerate package-lock.json from a clean checkout.
  3. Switch CI from npm install to npm ci after lock cleanup.
  4. Make npm test source-reproducible or document/build-enforce its dependency on dist.
  5. Align internal package dependency ranges and Playwright peer ranges.
  6. Add tarball-based external smoke.

Phase 3: Close Semantic Correctness Gaps

  1. Implement environment-aware domain resolution for variable-bound domains.
  2. Decide and fix inStackingContext arity/semantics.
  3. Preserve metrics/source references through FOL diagnostics and boolean connectives.
  4. Add E2E tests for scoped domains where global resolution would produce a false positive.
  5. Add negative fluent compound tests where one side fails and diagnostics must remain actionable.

Phase 4: Govern API And Architecture

  1. Decide which packages are public and add READMEs or mark internal packages private.
  2. Move internal exports to explicit internal subpaths or remove them from root barrels.
  3. Standardize package export conditions.
  4. Document the actual contributor golden path.
  5. Consolidate world schemas or document a fact availability matrix.
  6. Move test mocks out of imhotep-core production source.
  7. Split large central files along stable seams.

Bottom Line

Imhotep is promising enough to justify high standards. The core concept and much of the implementation already look like a serious framework rather than a toy wrapper around Playwright. The current weaknesses are fixable, but they are highly visible: a broken/misleading CLI path, stale examples, lockfile contamination, hardcoded local paths, and semantics that parse before they truly resolve.

For a developer evaluating adoption, I would recommend a short hardening sprint before team rollout. For a developer onboarding to contribute, I would start with documentation truthfulness and reproducibility first, then move into variable-bound domain semantics and diagnostic fidelity. Those improvements would make the project live up much more closely to its stated goal: First Order Logic Geometric Contract and Property Testing for the Web.