chore: polish for FOL contract delivery (CHANGELOG, CLI guardrails, metadata)
- Update CHANGELOG.md for 1.1.0 (date, refactoring, repository fixes) - Add overwrite guardrails to imhotep-cli init — skips existing files - Add bugs/homepage/keywords metadata to 5 public packages - Mark imhotep-bench and imhotep-fixtures as private packages - Add selector field to SourceReference interface (core types) - Remove 7 casts from check-all.ts (folAst.position, cardinality results) - Generate package-lock.json for reproducible installs
This commit is contained in:
+14
-3
@@ -1,6 +1,6 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
## [1.1.0] - 2025-08-15
|
## [1.1.0] - 2026-05-21
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
@@ -15,17 +15,28 @@
|
|||||||
- Default concurrency capped at 4 (machine-specific tuning with `DEFAULT_CONCURRENCY_CAP`).
|
- Default concurrency capped at 4 (machine-specific tuning with `DEFAULT_CONCURRENCY_CAP`).
|
||||||
- CSS-based `ch` unit conversion uses true font metrics instead of approximation.
|
- CSS-based `ch` unit conversion uses true font metrics instead of approximation.
|
||||||
- Extraction path telemetry available via `IMHOTEP_EXTRACT_STATS=1` (opt-in).
|
- Extraction path telemetry available via `IMHOTEP_EXTRACT_STATS=1` (opt-in).
|
||||||
|
- **Architecture**: Split `public.ts` (3568 lines) into 6 focused modules: `public-types.ts`, `semantic-subjects.ts`, `llm-output.ts`, `extraction.ts`, `check-all.ts`, and `public.ts` (916 lines, −74.3%).
|
||||||
|
- **Repository**: Root package renamed to `imhotep-monorepo`; CI targets `master` branch; all repository URLs updated to Gitea.
|
||||||
|
- **Build**: 13 tsconfigs now use `noEmitOnError: true`; package descriptions added to all 14 packages; 4 package READMEs created.
|
||||||
|
- **Documentation**: Root README replaced with real project documentation; SECURITY.md updated for Gitea; RELEASE.md checklist created; BUILD.md updated with correct commands.
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- TypeScript import extensions and missing `generateSeed` export resolution.
|
- TypeScript import extensions and missing `generateSeed` export resolution.
|
||||||
- Flaky performance test threshold relaxed 500ms → 800ms.
|
- Flaky performance test threshold relaxed 500ms → 800ms.
|
||||||
- `imhotep-core` test compilation via `tsconfig.test.json`.
|
- `imhotep-core` test compilation via `tsconfig.test.json`.
|
||||||
|
- Duplicate `ComponentOptions`/`StoryOptions` declarations collapsed into single shared interface.
|
||||||
|
- Broken import path in `imhotep` meta-package test runner.
|
||||||
|
- Root `package.json` scripts: removed `test:unit` reference; aliased build to workspace target.
|
||||||
|
- Cleaned 694 generated `.js`/`.d.ts`/`.map` artifacts from `src/` directories.
|
||||||
|
- Missing `imhotep-cli` exports: `init`, `CliOptions`, and preset types added to CLI barrel.
|
||||||
|
- `.gitignore` updated for V8 heap snapshot logs.
|
||||||
|
|
||||||
### Test/Verification
|
### Test/Verification
|
||||||
|
|
||||||
- **1125 unit tests** passing across all packages.
|
- **1125 unit tests** passing across all packages (0 failures).
|
||||||
- **215 E2E tests** passing, 0 failures.
|
- **215 E2E tests** passing (0 failures).
|
||||||
|
- **23 benchmark tests** passing (0 failures).
|
||||||
- External smoke test passes in clean temp directory.
|
- External smoke test passes in clean temp directory.
|
||||||
- Build succeeds for all 14 packages.
|
- Build succeeds for all 14 packages.
|
||||||
- All packages pack cleanly with no `workspace:*` protocol leakage.
|
- All packages pack cleanly with no `workspace:*` protocol leakage.
|
||||||
|
|||||||
Generated
+3676
File diff suppressed because it is too large
Load Diff
@@ -29,5 +29,6 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"imhotep-core": "^1.0.0",
|
"imhotep-core": "^1.0.0",
|
||||||
"imhotep-solver": "^1.0.0"
|
"imhotep-solver": "^1.0.0"
|
||||||
}
|
},
|
||||||
|
"private": true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,5 +37,21 @@
|
|||||||
"@types/node": "^20.19.39",
|
"@types/node": "^20.19.39",
|
||||||
"playwright": "^1.59.1",
|
"playwright": "^1.59.1",
|
||||||
"typescript": "^5.9.3"
|
"typescript": "^5.9.3"
|
||||||
}
|
},
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://gitea.com/anomalyco/imhotep/issues"
|
||||||
|
},
|
||||||
|
"homepage": "https://docs.imhotep.dev",
|
||||||
|
"keywords": [
|
||||||
|
"testing",
|
||||||
|
"visual-testing",
|
||||||
|
"layout-testing",
|
||||||
|
"relational-testing",
|
||||||
|
"playwright",
|
||||||
|
"geometry",
|
||||||
|
"fol",
|
||||||
|
"first-order-logic",
|
||||||
|
"e2e",
|
||||||
|
"property-testing"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,8 +50,15 @@ function main(): void {
|
|||||||
: process.cwd();
|
: process.cwd();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
initProject({ preset, targetDir });
|
const result = initProject({ preset, targetDir });
|
||||||
console.log(`✓ Scaffolded ${preset} project in ${targetDir}`);
|
console.log(`✓ Scaffolded ${preset} project in ${targetDir}`);
|
||||||
|
|
||||||
|
if (result.created.length > 0) {
|
||||||
|
console.log(` Created: ${result.created.map(f => f.replace(targetDir, '.')).join(', ')}`);
|
||||||
|
}
|
||||||
|
if (result.skipped.length > 0) {
|
||||||
|
console.warn(` Skipped (already exists): ${result.skipped.map(f => f.replace(targetDir, '.')).join(', ')}`);
|
||||||
|
}
|
||||||
console.log(` Run: cd ${targetDir} && npm install && npm test`);
|
console.log(` Run: cd ${targetDir} && npm install && npm test`);
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error(`Error: ${error.message}`);
|
console.error(`Error: ${error.message}`);
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
// Public API exports for imhotep-cli
|
// Public API exports for imhotep-cli
|
||||||
export { initProject, type InitOptions } from './init.js';
|
export { initProject, type InitOptions, type InitResult } from './init.js';
|
||||||
export { presets, getPresetNames, getPreset, type PresetName } from './presets/index.js';
|
export { presets, getPresetNames, getPreset, type PresetName } from './presets/index.js';
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
// Core init logic for scaffolding Imhotep projects from presets
|
// Core init logic for scaffolding Imhotep projects from presets
|
||||||
import { mkdirSync, writeFileSync } from 'node:fs';
|
import { mkdirSync, writeFileSync, existsSync } from 'node:fs';
|
||||||
import { join } from 'node:path';
|
import { join } from 'node:path';
|
||||||
import { getPreset } from './presets/index.js';
|
import { getPreset } from './presets/index.js';
|
||||||
|
|
||||||
@@ -8,49 +8,59 @@ export interface InitOptions {
|
|||||||
targetDir: string;
|
targetDir: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function initProject(options: InitOptions): void {
|
export interface InitResult {
|
||||||
const { preset, targetDir } = options;
|
created: string[];
|
||||||
|
skipped: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
function safeWrite(filePath: string, content: string, results: { created: string[]; skipped: string[] }): void {
|
||||||
|
if (existsSync(filePath)) {
|
||||||
|
results.skipped.push(filePath)
|
||||||
|
} else {
|
||||||
|
writeFileSync(filePath, content, 'utf-8')
|
||||||
|
results.created.push(filePath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function initProject(options: InitOptions): InitResult {
|
||||||
|
const { preset, targetDir } = options;
|
||||||
|
const results: InitResult = { created: [], skipped: [] };
|
||||||
|
|
||||||
// Resolve preset template
|
|
||||||
const template = getPreset(preset);
|
const template = getPreset(preset);
|
||||||
|
|
||||||
// Create directory structure
|
|
||||||
mkdirSync(targetDir, { recursive: true });
|
mkdirSync(targetDir, { recursive: true });
|
||||||
mkdirSync(join(targetDir, 'tests'), { recursive: true });
|
mkdirSync(join(targetDir, 'tests'), { recursive: true });
|
||||||
mkdirSync(join(targetDir, 'fixtures'), { recursive: true });
|
mkdirSync(join(targetDir, 'fixtures'), { recursive: true });
|
||||||
|
|
||||||
// Write config file
|
safeWrite(
|
||||||
writeFileSync(
|
|
||||||
join(targetDir, 'imhotep.config.js'),
|
join(targetDir, 'imhotep.config.js'),
|
||||||
template.configFile,
|
template.configFile,
|
||||||
'utf-8'
|
results
|
||||||
);
|
);
|
||||||
|
|
||||||
// Write test file
|
safeWrite(
|
||||||
writeFileSync(
|
|
||||||
join(targetDir, 'tests', 'example.test.ts'),
|
join(targetDir, 'tests', 'example.test.ts'),
|
||||||
template.testFile,
|
template.testFile,
|
||||||
'utf-8'
|
results
|
||||||
);
|
);
|
||||||
|
|
||||||
// Write fixture file
|
safeWrite(
|
||||||
writeFileSync(
|
|
||||||
join(targetDir, 'fixtures', 'example.html'),
|
join(targetDir, 'fixtures', 'example.html'),
|
||||||
template.fixtureFile,
|
template.fixtureFile,
|
||||||
'utf-8'
|
results
|
||||||
);
|
);
|
||||||
|
|
||||||
// Write package.json
|
safeWrite(
|
||||||
writeFileSync(
|
|
||||||
join(targetDir, 'package.json'),
|
join(targetDir, 'package.json'),
|
||||||
JSON.stringify(template.packageJson, null, 2),
|
JSON.stringify(template.packageJson, null, 2),
|
||||||
'utf-8'
|
results
|
||||||
);
|
);
|
||||||
|
|
||||||
// Write README
|
safeWrite(
|
||||||
writeFileSync(
|
|
||||||
join(targetDir, 'README.md'),
|
join(targetDir, 'README.md'),
|
||||||
template.readme,
|
template.readme,
|
||||||
'utf-8'
|
results
|
||||||
);
|
);
|
||||||
|
|
||||||
|
return results
|
||||||
}
|
}
|
||||||
@@ -41,5 +41,21 @@
|
|||||||
"types": "./dist/world.d.ts",
|
"types": "./dist/world.d.ts",
|
||||||
"default": "./dist/world.js"
|
"default": "./dist/world.js"
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://gitea.com/anomalyco/imhotep/issues"
|
||||||
|
},
|
||||||
|
"homepage": "https://docs.imhotep.dev",
|
||||||
|
"keywords": [
|
||||||
|
"testing",
|
||||||
|
"visual-testing",
|
||||||
|
"layout-testing",
|
||||||
|
"relational-testing",
|
||||||
|
"playwright",
|
||||||
|
"geometry",
|
||||||
|
"fol",
|
||||||
|
"first-order-logic",
|
||||||
|
"e2e",
|
||||||
|
"property-testing"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -251,6 +251,8 @@ export interface SourceReference {
|
|||||||
specLine?: number
|
specLine?: number
|
||||||
/** 1-based column number in the dense spec string */
|
/** 1-based column number in the dense spec string */
|
||||||
specColumn?: number
|
specColumn?: number
|
||||||
|
/** CSS or semantic selector for cardinality assertions */
|
||||||
|
selector?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ClauseResult {
|
export interface ClauseResult {
|
||||||
|
|||||||
@@ -28,5 +28,21 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"imhotep-core": "^1.0.0"
|
"imhotep-core": "^1.0.0"
|
||||||
}
|
},
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://gitea.com/anomalyco/imhotep/issues"
|
||||||
|
},
|
||||||
|
"homepage": "https://docs.imhotep.dev",
|
||||||
|
"keywords": [
|
||||||
|
"testing",
|
||||||
|
"visual-testing",
|
||||||
|
"layout-testing",
|
||||||
|
"relational-testing",
|
||||||
|
"playwright",
|
||||||
|
"geometry",
|
||||||
|
"fol",
|
||||||
|
"first-order-logic",
|
||||||
|
"e2e",
|
||||||
|
"property-testing"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,5 +35,6 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"imhotep-cdp": "^1.0.0",
|
"imhotep-cdp": "^1.0.0",
|
||||||
"imhotep-playwright": "^1.0.0"
|
"imhotep-playwright": "^1.0.0"
|
||||||
}
|
},
|
||||||
|
"private": true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,5 +37,21 @@
|
|||||||
"imhotep-dsl": "^1.0.0",
|
"imhotep-dsl": "^1.0.0",
|
||||||
"imhotep-solver": "^1.0.0",
|
"imhotep-solver": "^1.0.0",
|
||||||
"imhotep-reporter": "^1.0.0"
|
"imhotep-reporter": "^1.0.0"
|
||||||
}
|
},
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://gitea.com/anomalyco/imhotep/issues"
|
||||||
|
},
|
||||||
|
"homepage": "https://docs.imhotep.dev",
|
||||||
|
"keywords": [
|
||||||
|
"testing",
|
||||||
|
"visual-testing",
|
||||||
|
"layout-testing",
|
||||||
|
"relational-testing",
|
||||||
|
"playwright",
|
||||||
|
"geometry",
|
||||||
|
"fol",
|
||||||
|
"first-order-logic",
|
||||||
|
"e2e",
|
||||||
|
"property-testing"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -121,6 +121,7 @@ export function makeCheckAll(deps: CheckAllDeps): ImhotepUi['checkAll'] {
|
|||||||
const validationClauseResults: ClauseResult[] = []
|
const validationClauseResults: ClauseResult[] = []
|
||||||
|
|
||||||
for (let i = 0; i < assertions.length; i++) {
|
for (let i = 0; i < assertions.length; i++) {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
const assertion = assertions[i] as any
|
const assertion = assertions[i] as any
|
||||||
// Cardinality assertions are evaluated outside the FOL engine.
|
// Cardinality assertions are evaluated outside the FOL engine.
|
||||||
if (assertion && ['exactlyOne', 'atLeastN', 'atMostN'].includes(assertion.kind)) {
|
if (assertion && ['exactlyOne', 'atLeastN', 'atMostN'].includes(assertion.kind)) {
|
||||||
@@ -240,8 +241,8 @@ export function makeCheckAll(deps: CheckAllDeps): ImhotepUi['checkAll'] {
|
|||||||
for (const folAst of folFormulas) {
|
for (const folAst of folFormulas) {
|
||||||
const solverFormula = compileDenseFOLToFormula(folAst)
|
const solverFormula = compileDenseFOLToFormula(folAst)
|
||||||
const selectors = getSelectorsFromFormula(solverFormula)
|
const selectors = getSelectorsFromFormula(solverFormula)
|
||||||
const sourceRef: SourceReference = (folAst as any).position?.start
|
const sourceRef: SourceReference = folAst.position?.start
|
||||||
? { specLine: (folAst as any).position.start.line, specColumn: (folAst as any).position.start.column }
|
? { specLine: folAst.position.start.line, specColumn: folAst.position.start.column }
|
||||||
: {}
|
: {}
|
||||||
checks.push({
|
checks.push({
|
||||||
clauseId: `clause_${specBase++}`,
|
clauseId: `clause_${specBase++}`,
|
||||||
@@ -420,11 +421,10 @@ export function makeCheckAll(deps: CheckAllDeps): ImhotepUi['checkAll'] {
|
|||||||
metrics: {
|
metrics: {
|
||||||
observedCount: 0,
|
observedCount: 0,
|
||||||
expectedCount: cardResult.expectedCount,
|
expectedCount: cardResult.expectedCount,
|
||||||
selector: cardResult.selector,
|
},
|
||||||
} as any,
|
|
||||||
witness: { subjectId: 0, frameId: 0 },
|
witness: { subjectId: 0, frameId: 0 },
|
||||||
diagnostics: cardResult.diagnostics.map((d) => d.code),
|
diagnostics: cardResult.diagnostics.map((d) => d.code),
|
||||||
sourceRef: { selector: cardResult.selector } as any,
|
sourceRef: { selector: cardResult.selector },
|
||||||
clauseLabel: cardResult.label,
|
clauseLabel: cardResult.label,
|
||||||
})
|
})
|
||||||
cardDiagnostics.push(...cardResult.diagnostics)
|
cardDiagnostics.push(...cardResult.diagnostics)
|
||||||
@@ -481,11 +481,10 @@ export function makeCheckAll(deps: CheckAllDeps): ImhotepUi['checkAll'] {
|
|||||||
metrics: {
|
metrics: {
|
||||||
observedCount: selectorToIds.get(cardResult.selector)?.length ?? 0,
|
observedCount: selectorToIds.get(cardResult.selector)?.length ?? 0,
|
||||||
expectedCount: cardResult.expectedCount,
|
expectedCount: cardResult.expectedCount,
|
||||||
selector: cardResult.selector,
|
},
|
||||||
} as any,
|
|
||||||
witness: { subjectId: 0, frameId: 0 },
|
witness: { subjectId: 0, frameId: 0 },
|
||||||
diagnostics: cardResult.diagnostics.map((d) => d.code),
|
diagnostics: cardResult.diagnostics.map((d) => d.code),
|
||||||
sourceRef: { selector: cardResult.selector } as any,
|
sourceRef: { selector: cardResult.selector },
|
||||||
clauseLabel: cardResult.label,
|
clauseLabel: cardResult.label,
|
||||||
})
|
})
|
||||||
allDiagnostics.push(...cardResult.diagnostics)
|
allDiagnostics.push(...cardResult.diagnostics)
|
||||||
|
|||||||
@@ -984,7 +984,7 @@ describe('Selector Cardinality Contracts (P2.1)', () => {
|
|||||||
if (cardClause) {
|
if (cardClause) {
|
||||||
assert.strictEqual(cardClause.metrics.observedCount, 0)
|
assert.strictEqual(cardClause.metrics.observedCount, 0)
|
||||||
assert.strictEqual(cardClause.metrics.expectedCount, 1)
|
assert.strictEqual(cardClause.metrics.expectedCount, 1)
|
||||||
assert.strictEqual(cardClause.metrics.selector, '.does-not-exist')
|
assert.strictEqual(cardClause.sourceRef?.selector, '.does-not-exist')
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -45,5 +45,21 @@
|
|||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@playwright/test": "^1.40.0",
|
"@playwright/test": "^1.40.0",
|
||||||
"playwright": "^1.40.0"
|
"playwright": "^1.40.0"
|
||||||
}
|
},
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://gitea.com/anomalyco/imhotep/issues"
|
||||||
|
},
|
||||||
|
"homepage": "https://docs.imhotep.dev",
|
||||||
|
"keywords": [
|
||||||
|
"testing",
|
||||||
|
"visual-testing",
|
||||||
|
"layout-testing",
|
||||||
|
"relational-testing",
|
||||||
|
"playwright",
|
||||||
|
"geometry",
|
||||||
|
"fol",
|
||||||
|
"first-order-logic",
|
||||||
|
"e2e",
|
||||||
|
"property-testing"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user