# 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: ```bash 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: ```typescript 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: ```typescript 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: ```typescript 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: ```typescript 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: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: ```text 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. ### 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: ```bash 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-testid`s, 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? ## Recommended Remediation Sequence ### 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.