- 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
30 KiB
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:3explains 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 inREADME.md:15-32. - The public fluent path is compact from a user perspective:
const ui = await imhotep(page), register assertions, thenawait ui.checkAll(). This is the right product shape for adoption. SeeREADME.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/*/srcand root scripts inpackage.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.ymland targetsmaster, 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, andpackages/imhotep-core. - Recent barrel export classification in
packages/imhotep-playwright/src/index.tsis a good governance step. The@publicand@internalmarkers 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:
- Add a
binentry to theimhotepmeta-package that delegates to the CLI. - Rename/publish the CLI package so
npx imhotep initresolves to it intentionally. - Change docs to
npx imhotep-cli init --preset reactand 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 developer’s 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.
8. The Lockfile Contains Sibling-Project Links
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:3is1.1.0, but dependencies inpackages/imhotep/package.json:33-43are^1.0.0.packages/imhotep-playwright/package.json:3is1.1.0, but dependencies inpackages/imhotep-playwright/package.json:34-39are^1.0.0.packages/imhotep-cli/package.json:32-35depends on^1.0.0packages.
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-169andpredicates.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.
17. The Meta-Package Does Not Re-Export The Full Recommended Public Surface
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 project’s 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 side’s 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 initintended to be the canonical CLI command, or should the CLI package name be user-facing? - Is
imhotepintended to be the only recommended import path, or should advanced users importimhotep-playwrightdirectly? - Are all packages intended to be published independently, or should packages like
imhotep-cdp,imhotep-extractor,imhotep-state,imhotep-topology, andimhotep-benchbe 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)andchildren($var)? - Is
inStackingContextunary (“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
@internalroot exports semver-stable or explicitly unsupported? - Should external smoke validate local package directories, packed tarballs, or both?
Recommended Remediation Sequence
Phase 1: Make The Public Story True
- Fix the CLI command and README quick start.
- Fix generated config format for ESM projects.
- Fix generated preset README snippets.
- Rewrite root examples to current ESM
ui.expect/ui.spec/checkAllpatterns. - Update docs-example tests so they protect current public API examples rather than old “not wired yet” assumptions.
- Fix README dense DSL and property examples.
Phase 2: Make The Repo Reproducible Everywhere
- Replace the hardcoded E2E fixture path.
- Regenerate
package-lock.jsonfrom a clean checkout. - Switch CI from
npm installtonpm ciafter lock cleanup. - Make
npm testsource-reproducible or document/build-enforce its dependency ondist. - Align internal package dependency ranges and Playwright peer ranges.
- Add tarball-based external smoke.
Phase 3: Close Semantic Correctness Gaps
- Implement environment-aware domain resolution for variable-bound domains.
- Decide and fix
inStackingContextarity/semantics. - Preserve metrics/source references through FOL diagnostics and boolean connectives.
- Add E2E tests for scoped domains where global resolution would produce a false positive.
- Add negative fluent compound tests where one side fails and diagnostics must remain actionable.
Phase 4: Govern API And Architecture
- Decide which packages are public and add READMEs or mark internal packages private.
- Move internal exports to explicit internal subpaths or remove them from root barrels.
- Standardize package export conditions.
- Document the actual contributor golden path.
- Consolidate world schemas or document a fact availability matrix.
- Move test mocks out of
imhotep-coreproduction source. - 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.